State Variants in Tailwind
Tailwind lets you apply styles conditionally based on element state using variant prefixes. The most common are hover:, focus:, active:, and group-hover:.
Hover
<button class="bg-blue-600 text-white py-2 px-4 rounded-lg hover:bg-blue-700 hover:shadow-lg">
Hover me
</button>
<a href="#" class="text-gray-600 hover:text-indigo-600 hover:underline">
Hover link
</a>
Focus
<input class="border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent" placeholder="Focus me">
<button class="bg-indigo-600 text-white py-2 px-4 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
Focus ring
</button>
Active
<button class="bg-indigo-600 text-white py-2 px-4 rounded-lg hover:bg-indigo-700 active:bg-indigo-800 active:scale-95 transform transition">
Press me
</button>
Group Hover
Apply hover styles to a child when the parent is hovered. Add group to the parent and group-hover: to children:
<div class="group bg-white rounded-xl shadow-md p-6 hover:shadow-xl transition-shadow cursor-pointer">
<h3 class="font-bold text-gray-900 group-hover:text-indigo-600 transition-colors">
Card Title
</h3>
<p class="text-gray-500 mt-2">Hover the card to see the title change color.</p>
<span class="mt-4 inline-block text-indigo-600 font-semibold opacity-0 group-hover:opacity-100 transition-opacity">
Read more →
</span>
</div>
First & Last Child
<ul class="divide-y divide-gray-200">
<li class="py-3 first:pt-0 last:pb-0">Item A</li>
<li class="py-3 first:pt-0 last:pb-0">Item B</li>
<li class="py-3 first:pt-0 last:pb-0">Item C</li>
</ul>
Odd & Even
<table class="w-full">
<tbody>
<tr class="odd:bg-gray-50 even:bg-white"><td class="p-3">Row 1</td></tr>
<tr class="odd:bg-gray-50 even:bg-white"><td class="p-3">Row 2</td></tr>
<tr class="odd:bg-gray-50 even:bg-white"><td class="p-3">Row 3</td></tr>
<tr class="odd:bg-gray-50 even:bg-white"><td class="p-3">Row 4</td></tr>
</tbody>
</table>
Combining States
You can stack variants:
<!-- Dark mode + hover -->
<button class="bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600">
Combined states
</button>
<!-- Responsive + hover -->
<div class="text-sm md:text-base md:hover:text-indigo-600">
Responsive hover
</div>
Practical Example — Interactive Card
<div class="group bg-white rounded-2xl shadow-md hover:shadow-2xl p-6 transition-all duration-300 cursor-pointer max-w-sm">
<div class="overflow-hidden rounded-xl mb-4">
<img src="https://picsum.photos/400/200" alt="" class="w-full h-44 object-cover group-hover:scale-110 transition-transform duration-500">
</div>
<span class="bg-indigo-100 text-indigo-800 text-xs font-semibold px-2.5 py-0.5 rounded-full">Featured</span>
<h3 class="mt-3 text-lg font-bold text-gray-900 group-hover:text-indigo-600 transition-colors">Interactive Design</h3>
<p class="mt-2 text-sm text-gray-500">Hover effects make your UI feel alive and responsive.</p>
</div>
All State Variants
| Prefix | Applies When |
|---|---|
hover: | Mouse cursor is over the element |
focus: | Element has keyboard focus |
focus-within: | Any child has focus |
active: | Element is being pressed |
visited: | Link has been visited |
disabled: | Element is disabled |
checked: | Checkbox/radio is checked |
first: | First child element |
last: | Last child element |
odd: | Odd-numbered child |
even: | Even-numbered child |
group-hover: | Parent with group class is hovered |
Recap
- State variants (
hover:,focus:,active:) apply styles conditionally. group+group-hover:lets parent hover affect children.- Combine states with responsive prefixes:
md:hover:text-indigo-600. first:,last:,odd:,even:target list and table patterns.
Next up: building a responsive navigation bar — part 1!