Tailwind CSS Hover, Focus, and Active State Variants
In this tutorial, you will learn about Tailwind CSS Hover, Focus, and Active State Variants. We cover key concepts, practical examples, and best practices to help you master this topic.
Tailwind CSS state variants let you style elements on interaction: hover (hover:), focus (focus:), active (active:), focus-visible (focus-visible:), visited (visited:), and disabled (disabled:).
What You'll Learn
You will learn how to apply state variants to any utility, combine multiple states, create smooth transitions, and style form elements and interactive components with interaction feedback.
Why It Matters
Interactive feedback improves usability. DodaTech's buttons use hover:bg-* and focus:ring-* for keyboard Accessibility, and active:scale-95 for tactile press feedback.
Real-World Use
Durga Antivirus Pro uses hover:bg-gray-50 hover:shadow-sm for clickable table rows, focus:ring-2 focus:ring-blue-500 for input fields, and active:bg-blue-700 for button press states.
flowchart LR
A[Responsive] --> B[State Variants]
B --> C[Hover]
B --> D[Focus]
B --> E[Active]
B --> F[Disabled]
B --> G[Group]
style B fill:#38bdf8,stroke:#0284c7,color:#fff
style C fill:#22c55e,stroke:#16a34a,color:#fff
Hover States
<div class="flex flex-wrap gap-4 p-4">
<button class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-3 rounded-lg font-medium transition-colors">
Hover me (darkens)
</button>
<button class="bg-white border-2 border-gray-300 hover:border-blue-500 hover:text-blue-500 px-6 py-3 rounded-lg font-medium transition-colors">
Hover (border change)
</button>
<a href="#" class="text-blue-600 hover:text-blue-800 hover:underline font-medium">
Link with hover underline
</a>
<div class="bg-gray-100 hover:bg-gray-200 hover:shadow-md p-4 rounded-lg cursor-pointer transition-all">
Card with hover shadow
</div>
</div>
Expected output: Elements change background, border, text color, underline, and shadow on hover, with smooth transitions.
Focus States for Accessibility
<div class="space-y-4 p-4 max-w-md mx-auto">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Email Input</label>
<input type="email" placeholder="your@email.com"
class="w-full px-4 py-2 border border-gray-300 rounded-lg
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500
placeholder:text-gray-400">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Password</label>
<input type="password" placeholder="********"
class="w-full px-4 py-2 border border-gray-300 rounded-lg
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<button class="w-full bg-blue-600 text-white px-4 py-2 rounded-lg font-medium
hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
Sign In
</button>
</div>
Expected output: Inputs show a blue ring on focus. The button shows a ring with offset on focus for keyboard navigation.
Active and Press States
<div class="flex flex-wrap gap-4 p-4">
<button class="bg-blue-600 hover:bg-blue-700 active:bg-blue-800 text-white px-6 py-3 rounded-lg font-medium transition-colors">
Press me (darkens more)
</button>
<button class="bg-purple-600 hover:bg-purple-700 active:scale-95 text-white px-6 py-3 rounded-lg font-medium transition-transform active:transition-transform">
Press me (scales down)
</button>
<button class="bg-green-600 hover:bg-green-700 active:ring-2 active:ring-green-300 text-white px-6 py-3 rounded-lg font-medium transition-all">
Press me (ring effect)
</button>
</div>
Expected output: Buttons respond to active (mouse down) with darker color, scale transform, or ring effect, providing tactile feedback.
Disabled States
<div class="flex flex-wrap gap-4 p-4 items-center">
<button class="bg-blue-600 text-white px-6 py-3 rounded-lg font-medium
hover:bg-blue-700 focus:ring-2 focus:ring-blue-500
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-blue-600"
disabled>
Disabled Button
</button>
<button class="bg-blue-600 text-white px-6 py-3 rounded-lg font-medium
hover:bg-blue-700 focus:ring-2 focus:ring-blue-500">
Enabled Button
</button>
<input type="text" placeholder="Disabled input" disabled
class="px-4 py-2 border border-gray-300 rounded-lg bg-gray-100
disabled:opacity-50 disabled:cursor-not-allowed">
</div>
Expected output: Disabled elements have reduced opacity and a not-allowed cursor. The disabled button does not respond to hover.
Group Hover
<div class="grid grid-cols-2 gap-4 p-4 max-w-lg mx-auto">
<div class="group bg-white border rounded-lg p-4 hover:shadow-lg transition-shadow cursor-pointer">
<h3 class="font-bold text-gray-900 group-hover:text-blue-600 transition-colors">Card Title</h3>
<p class="text-gray-600 text-sm mt-1">Card description text.</p>
<span class="text-blue-500 opacity-0 group-hover:opacity-100 transition-opacity text-sm font-medium">
Read more →
</span>
</div>
<div class="group bg-white border rounded-lg p-4 hover:shadow-lg transition-shadow cursor-pointer">
<h3 class="font-bold text-gray-900 group-hover:text-blue-600 transition-colors">Another Card</h3>
<p class="text-gray-600 text-sm mt-1">Hover to reveal the link below.</p>
<span class="text-blue-500 opacity-0 group-hover:opacity-100 transition-opacity text-sm font-medium">
Read more →
</span>
</div>
</div>
Expected output: When hovering over the card (group), the title color changes and the link fades in. The group-hover variant styles children when the parent is hovered.
Common Mistakes
1. Not Adding focus:outline-none
The default browser focus outline conflicts with custom focus rings. Always add focus:outline-none when using focus:ring-*.
2. Forgetting transition-*
State changes without transition appear instantaneous. Add transition-colors, transition-opacity, or transition-all for smooth animations.
3. Using hover for Mobile
Hover states do not work on touch devices. Ensure interactive elements work without hover (use focus for mobile feedback).
4. Not Styling focus-visible
focus-visible applies only when focusing via keyboard (Tab key), not mouse click. Use it for focus rings that do not appear on click.
5. Applying State Variants Without Base Styles
hover:bg-blue-700 does nothing without a base bg-blue-600. State variants only override the base value.
Practice Questions
What variant applies styles on mouse-down?
active:. It applies while the element is being pressed.How do you style a parent when a child is hovered? Add
groupto the parent and usegroup-hover:on child elements.What is the difference between focus and focus-visible? focus applies on any focus. focus-visible applies only when focused via keyboard navigation (Tab).
How do you style a disabled button? Use
disabled:opacity-50 disabled:cursor-not-allowedand any other disabled-specific overrides.Can state variants be combined with responsive prefixes? Yes:
md:hover:bg-blue-600applies hover background only on medium screens and above.
Challenge
Build an interactive card: group hover with image scale, title color change, and button reveal. Add focus ring for keyboard navigation. Disable the card if a disabled class is present.
FAQ
Mini Project
Build an interactive form: 3 inputs with focus rings, submit button with hover/active states, checkbox with checked: variant, disabled submit when form is incomplete. Use group-hover for tooltip labels.
What's Next
Now master Dark Mode with the dark: variant. Combine dark mode with state variants for complete theme-aware interactive components.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro