Component Patterns
In this chapter, you learn patterns for reusable UI components you can use in real projects.
This chapter focuses on "styling"
This chapter focuses on styling with Tailwind CSS. 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
- Introduction to React — Tailwind — designing reusable components in React
- Tailwind CSS Guide table of contents — back to the structure of all 18 chapters