Component Patterns
In this chapter, you learn patterns for reusable UI components you can use in real projects.
This chapter focuses on "styling"
Interactive behavior, such as opening and closing a modal or expanding an accordion, requires JavaScript (or a framework). Basic JavaScript implementation examples are included for the major components.
Buttons
Basic buttons
<!-- Primary -->
<button class="px-4 py-2 bg-blue-500 text-white font-medium rounded-lg
hover:bg-blue-600 focus:ring-4 focus:ring-blue-300
transition-colors">
Primary
</button>
<!-- Secondary -->
<button class="px-4 py-2 bg-gray-200 text-gray-800 font-medium rounded-lg
hover:bg-gray-300 focus:ring-4 focus:ring-gray-200
transition-colors">
Secondary
</button>
<!-- Outline -->
<button class="px-4 py-2 border-2 border-blue-500 text-blue-500 font-medium rounded-lg
hover:bg-blue-50 focus:ring-4 focus:ring-blue-200
transition-colors">
Outline
</button>
<!-- Ghost -->
<button class="px-4 py-2 text-blue-500 font-medium rounded-lg
hover:bg-blue-50 focus:ring-4 focus:ring-blue-200
transition-colors">
Ghost
</button>
Size variations
<!-- Small -->
<button class="px-3 py-1.5 text-sm bg-blue-500 text-white rounded-md">
Small
</button>
<!-- Medium -->
<button class="px-4 py-2 text-base bg-blue-500 text-white rounded-lg">
Medium
</button>
<!-- Large -->
<button class="px-6 py-3 text-lg bg-blue-500 text-white rounded-xl">
Large
</button>
Button group
<div class="inline-flex rounded-lg overflow-hidden">
<button class="px-4 py-2 bg-blue-500 text-white border-r border-blue-400">
Left
</button>
<button class="px-4 py-2 bg-blue-500 text-white border-r border-blue-400">
Center
</button>
<button class="px-4 py-2 bg-blue-500 text-white">
Right
</button>
</div>
Button with an icon
<button class="inline-flex items-center gap-2 px-4 py-2 bg-blue-500 text-white rounded-lg">
<svg class="w-5 h-5">...</svg>
<span>Download</span>
</button>
<!-- Icon only -->
<button class="p-2 bg-blue-500 text-white rounded-lg">
<svg class="w-5 h-5">...</svg>
</button>
Cards
Basic card
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<img src="..." class="w-full h-48 object-cover" />
<div class="p-6">
<span class="text-sm text-blue-600 font-semibold">Category</span>
<h3 class="mt-2 text-xl font-bold text-gray-900">Card title</h3>
<p class="mt-2 text-gray-600">
Description goes here...
</p>
<button class="mt-4 px-4 py-2 bg-blue-500 text-white rounded-lg w-full">
View details
</button>
</div>
</div>
Horizontal card
<div class="flex bg-white rounded-xl shadow-lg overflow-hidden">
<img src="..." class="w-1/3 object-cover" />
<div class="flex-1 p-6">
<h3 class="text-xl font-bold">Title</h3>
<p class="mt-2 text-gray-600">Description...</p>
</div>
</div>
Interactive card
<div class="group bg-white rounded-xl shadow-lg overflow-hidden
hover:shadow-xl transition-shadow cursor-pointer">
<div class="relative">
<img src="..." class="w-full h-48 object-cover
group-hover:scale-105 transition-transform duration-300" />
<div class="absolute inset-0 bg-linear-to-t from-black/50 to-transparent
opacity-0 group-hover:opacity-100 transition-opacity"></div>
</div>
<div class="p-6">
<h3 class="text-xl font-bold group-hover:text-blue-500 transition-colors">
Title
</h3>
<p class="mt-2 text-gray-600">Description...</p>
</div>
</div>
Form elements
Text input
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
Label
</label>
<input
type="text"
class="w-full px-4 py-2 border border-gray-300 rounded-lg
focus:ring-2 focus:ring-blue-500 focus:border-transparent
placeholder:text-gray-400"
placeholder="Placeholder"
/>
<p class="mt-1 text-sm text-gray-500">Help text</p>
</div>
Error state
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
Email address
</label>
<input
type="email"
class="w-full px-4 py-2 border border-red-500 rounded-lg
focus:ring-2 focus:ring-red-500 focus:border-transparent
text-red-900 placeholder:text-red-300 bg-red-50"
placeholder="example@email.com"
/>
<p class="mt-1 text-sm text-red-500">Please enter a valid email address</p>
</div>
Select
<select class="w-full px-4 py-2 border border-gray-300 rounded-lg
focus:ring-2 focus:ring-blue-500 focus:border-transparent
bg-white appearance-none
bg-[url('data:image/svg+xml,...')] bg-no-repeat bg-position-[right_1rem_center]">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</select>
Checkbox
<label class="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
class="w-5 h-5 accent-blue-500"
/>
<span class="text-gray-700">I agree to the terms of service</span>
</label>
Radio buttons
<div class="space-y-3">
<label class="flex items-center gap-3 cursor-pointer">
<input
type="radio"
name="plan"
class="w-5 h-5 accent-blue-500"
/>
<span class="text-gray-700">Basic plan</span>
</label>
<label class="flex items-center gap-3 cursor-pointer">
<input
type="radio"
name="plan"
class="w-5 h-5 accent-blue-500"
/>
<span class="text-gray-700">Pro plan</span>
</label>
</div>
Toggle switch
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" class="sr-only peer" />
<div class="w-11 h-6 bg-gray-200 rounded-full
peer-focus:ring-4 peer-focus:ring-blue-300
peer-checked:bg-blue-500
after:content-[''] after:absolute after:top-0.5 after:left-0.5
after:w-5 after:h-5 after:bg-white after:rounded-full
after:transition-all peer-checked:after:translate-x-5"></div>
<span class="ml-3 text-gray-700">Enable notifications</span>
</label>
Navigation
Header
<header class="bg-white shadow-sm sticky top-0 z-50">
<nav class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<!-- Logo -->
<div class="flex items-center">
<a href="/" class="font-bold text-xl text-gray-900">Logo</a>
</div>
<!-- Desktop menu -->
<div class="hidden md:flex items-center gap-8">
<a href="#" class="text-gray-600 hover:text-gray-900">Home</a>
<a href="#" class="text-gray-600 hover:text-gray-900">Products</a>
<a href="#" class="text-gray-600 hover:text-gray-900">About</a>
<a href="#" class="text-gray-600 hover:text-gray-900">Contact</a>
</div>
<!-- Action buttons -->
<div class="flex items-center gap-4">
<button class="px-4 py-2 text-gray-600 hover:text-gray-900">
Log in
</button>
<button class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
Sign up
</button>
</div>
</div>
</nav>
</header>
Tabs
<div class="border-b border-gray-200">
<nav class="flex gap-8">
<a href="#" class="py-4 px-1 border-b-2 border-blue-500 text-blue-600 font-medium">
Tab 1
</a>
<a href="#" class="py-4 px-1 border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300">
Tab 2
</a>
<a href="#" class="py-4 px-1 border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300">
Tab 3
</a>
</nav>
</div>
Breadcrumbs
<nav class="flex items-center gap-2 text-sm">
<a href="#" class="text-gray-500 hover:text-gray-700">Home</a>
<span class="text-gray-400">/</span>
<a href="#" class="text-gray-500 hover:text-gray-700">Category</a>
<span class="text-gray-400">/</span>
<span class="text-gray-900 font-medium">Current page</span>
</nav>
Pagination
<nav class="flex items-center justify-center gap-1">
<button class="px-3 py-2 text-gray-500 hover:bg-gray-100 rounded-lg disabled:opacity-50" disabled>
Previous
</button>
<button class="px-4 py-2 bg-blue-500 text-white rounded-lg">1</button>
<button class="px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg">2</button>
<button class="px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg">3</button>
<span class="px-2">...</span>
<button class="px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg">10</button>
<button class="px-3 py-2 text-gray-700 hover:bg-gray-100 rounded-lg">
Next
</button>
</nav>
Feedback
Alerts
<!-- Info -->
<div class="flex items-start gap-3 p-4 bg-blue-50 border-l-4 border-blue-500 rounded-r-lg">
<svg class="w-5 h-5 text-blue-500 shrink-0 mt-0.5">...</svg>
<div>
<h4 class="font-medium text-blue-800">Info</h4>
<p class="mt-1 text-sm text-blue-700">The notification content goes here.</p>
</div>
</div>
<!-- Success -->
<div class="flex items-start gap-3 p-4 bg-green-50 border-l-4 border-green-500 rounded-r-lg">
<svg class="w-5 h-5 text-green-500 shrink-0 mt-0.5">...</svg>
<div>
<h4 class="font-medium text-green-800">Success</h4>
<p class="mt-1 text-sm text-green-700">The operation completed successfully.</p>
</div>
</div>
<!-- Warning -->
<div class="flex items-start gap-3 p-4 bg-yellow-50 border-l-4 border-yellow-500 rounded-r-lg">
<svg class="w-5 h-5 text-yellow-500 shrink-0 mt-0.5">...</svg>
<div>
<h4 class="font-medium text-yellow-800">Warning</h4>
<p class="mt-1 text-sm text-yellow-700">Caution is required.</p>
</div>
</div>
<!-- Error -->
<div class="flex items-start gap-3 p-4 bg-red-50 border-l-4 border-red-500 rounded-r-lg">
<svg class="w-5 h-5 text-red-500 shrink-0 mt-0.5">...</svg>
<div>
<h4 class="font-medium text-red-800">Error</h4>
<p class="mt-1 text-sm text-red-700">A problem occurred.</p>
</div>
</div>
Badges
<span class="px-2.5 py-0.5 text-xs font-semibold bg-blue-100 text-blue-800 rounded-full">
New
</span>
<span class="px-2.5 py-0.5 text-xs font-semibold bg-green-100 text-green-800 rounded-full">
Done
</span>
<span class="px-2.5 py-0.5 text-xs font-semibold bg-yellow-100 text-yellow-800 rounded-full">
Pending
</span>
<span class="px-2.5 py-0.5 text-xs font-semibold bg-red-100 text-red-800 rounded-full">
Urgent
</span>
Tooltip
<div class="relative group">
<button class="px-4 py-2 bg-gray-800 text-white rounded-lg">
Hover me
</button>
<div class="absolute bottom-full left-1/2 -translate-x-1/2 mb-2
px-3 py-1 bg-gray-900 text-white text-sm rounded
opacity-0 group-hover:opacity-100 transition-opacity
pointer-events-none whitespace-nowrap">
Tooltip text
<div class="absolute top-full left-1/2 -translate-x-1/2
border-4 border-transparent border-t-gray-900"></div>
</div>
</div>
Modal
<!-- Overlay -->
<div class="fixed inset-0 z-50 flex items-center justify-center p-4
bg-black/50 backdrop-blur-sm">
<!-- Modal body -->
<div class="bg-white rounded-xl shadow-2xl max-w-lg w-full max-h-[90vh] overflow-y-auto">
<!-- Header -->
<div class="flex items-center justify-between p-6 border-b border-gray-200">
<h2 class="text-xl font-bold">Modal title</h2>
<button class="p-1 text-gray-400 hover:text-gray-600 rounded">
<svg class="w-6 h-6">...</svg>
</button>
</div>
<!-- Body -->
<div class="p-6">
<p class="text-gray-600">The modal content goes here.</p>
</div>
<!-- Footer -->
<div class="flex justify-end gap-3 p-6 border-t border-gray-200 bg-gray-50 rounded-b-xl">
<button class="px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg">
Cancel
</button>
<button class="px-4 py-2 bg-blue-500 text-white hover:bg-blue-600 rounded-lg">
Confirm
</button>
</div>
</div>
</div>
Opening and closing the modal
CSS alone cannot toggle the modal's visibility. Below is an implementation example in vanilla JavaScript.
<button id="open-modal" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
Open modal
</button>
<!-- Overlay (hidden initially) -->
<div id="modal-overlay" class="fixed inset-0 z-50 hidden flex items-center justify-center p-4
bg-black/50 backdrop-blur-sm">
<div class="bg-white rounded-xl shadow-2xl max-w-lg w-full">
<!-- ...modal body (same structure as above)... -->
</div>
</div>
<script>
const openBtn = document.getElementById('open-modal');
const overlay = document.getElementById('modal-overlay');
const closeBtn = overlay.querySelector('button');
openBtn.addEventListener('click', () => {
overlay.classList.remove('hidden');
});
// Close on overlay click
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
overlay.classList.add('hidden');
}
});
// Close button
closeBtn.addEventListener('click', () => {
overlay.classList.add('hidden');
});
</script>
Accessibility
When implementing components, add appropriate ARIA attributes.
<!-- Modal -->
<div role="dialog" aria-modal="true" aria-labelledby="modal-title">
<h2 id="modal-title">Modal title</h2>
...
</div>
<!-- Alert -->
<div role="alert" class="bg-red-50 border border-red-200 text-red-800 p-4 rounded-lg">
An error occurred
</div>
<!-- Icon button -->
<button aria-label="Close" class="p-1 text-gray-400 hover:text-gray-600 rounded">
<svg class="w-6 h-6">...</svg>
</button>
Summary
- Buttons: combine sizes, variants, and states
- Cards: a composition of image, content, and actions
- Forms: labels, inputs, and validation states
- Navigation: headers, tabs, and pagination
- Feedback: alerts, badges, and modals
- Consistent spacing and color usage matter
- Accessibility: add ARIA attributes appropriately
What to read next
- Practical project — build an LP that integrates the components you learned
- React Guide — Tailwind — designing reusable components in React
- Tailwind CSS Guide table of contents — back to the structure of all 18 chapters