TechLead
Lesson 5 of 9
5 min read
Tailwind CSS

Hover, Focus & State Variants

Style interactive states like hover, focus, active, and disabled using Tailwind's state variant modifiers.

State Variants in Tailwind

Tailwind provides modifiers for styling elements based on their state. These modifiers can be combined with any utility class.

Hover State

<!-- Basic hover -->
<button class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded">
  Hover me
</button>

<!-- Hover with multiple changes -->
<div class="bg-white hover:bg-gray-50 hover:shadow-lg transition-all p-4 rounded">
  Hover for effects
</div>

<!-- Hover text color -->
<a href="#" class="text-gray-600 hover:text-blue-600">
  Hover link
</a>

Focus State

<!-- Focus ring -->
<input
  type="text"
  class="border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 rounded px-4 py-2 outline-none"
  placeholder="Focus me"
>

<!-- Focus visible (keyboard only) -->
<button class="px-4 py-2 bg-blue-500 text-white rounded focus-visible:ring-2 focus-visible:ring-blue-300 focus-visible:ring-offset-2">
  Keyboard focus only
</button>

<!-- Focus within (child focused) -->
<div class="p-4 border focus-within:border-blue-500 focus-within:ring-2 focus-within:ring-blue-200 rounded">
  <input type="text" class="outline-none" placeholder="Focus changes parent">
</div>

Active State

<button class="bg-blue-500 hover:bg-blue-600 active:bg-blue-700 active:scale-95 transition-all text-white px-4 py-2 rounded">
  Click and hold
</button>

Disabled State

<button
  disabled
  class="bg-blue-500 text-white px-4 py-2 rounded disabled:bg-gray-300 disabled:cursor-not-allowed disabled:opacity-50"
>
  Disabled Button
</button>

<input
  disabled
  class="border rounded px-4 py-2 disabled:bg-gray-100 disabled:text-gray-400"
  value="Disabled input"
>

Group Hover

Style child elements based on parent hover state:

<div class="group p-4 bg-white hover:bg-blue-500 rounded transition-colors">
  <h3 class="font-bold text-gray-900 group-hover:text-white">
    Card Title
  </h3>
  <p class="text-gray-600 group-hover:text-blue-100">
    Card description changes on parent hover
  </p>
  <span class="text-blue-500 group-hover:text-white group-hover:translate-x-2 inline-block transition-all">
    Learn more →
  </span>
</div>

Peer Modifier

Style an element based on sibling state:

<div>
  <input type="checkbox" id="toggle" class="peer sr-only">
  <label for="toggle" class="cursor-pointer">Toggle</label>
  <div class="hidden peer-checked:block mt-2 p-4 bg-green-100 rounded">
    This appears when checkbox is checked
  </div>
</div>

<!-- Form validation styling -->
<div>
  <input
    type="email"
    class="peer border rounded px-4 py-2 invalid:border-red-500"
    placeholder="Enter email"
    required
  >
  <p class="hidden peer-invalid:block text-red-500 text-sm mt-1">
    Please enter a valid email
  </p>
</div>

First, Last, Odd, Even

<ul class="divide-y">
  <li class="py-2 first:pt-0 last:pb-0">Item 1</li>
  <li class="py-2 first:pt-0 last:pb-0">Item 2</li>
  <li class="py-2 first:pt-0 last:pb-0">Item 3</li>
</ul>

<!-- Zebra striping -->
<table>
  <tbody>
    <tr class="odd:bg-gray-50 even:bg-white"><td>Row 1</td></tr>
    <tr class="odd:bg-gray-50 even:bg-white"><td>Row 2</td></tr>
    <tr class="odd:bg-gray-50 even:bg-white"><td>Row 3</td></tr>
  </tbody>
</table>

Form States

<!-- Required field -->
<input
  type="text"
  required
  class="border rounded px-4 py-2 required:border-red-500"
>

<!-- Valid/Invalid -->
<input
  type="email"
  class="border rounded px-4 py-2 valid:border-green-500 invalid:border-red-500"
>

<!-- Placeholder shown -->
<input
  type="text"
  placeholder="Enter name"
  class="border rounded px-4 py-2 placeholder-shown:border-gray-300"
>

<!-- Read only -->
<input
  readonly
  value="Read only"
  class="border rounded px-4 py-2 read-only:bg-gray-100"
>

Combining States

<!-- Responsive + hover -->
<button class="bg-blue-500 md:hover:bg-blue-600 lg:hover:bg-blue-700">
  Responsive hover
</button>

<!-- Dark mode + hover -->
<button class="bg-white hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700">
  Dark mode aware
</button>

<!-- Group + focus -->
<div class="group">
  <input class="peer" type="text">
  <span class="group-hover:text-blue-500 peer-focus:text-green-500">
    Label
  </span>
</div>

Interactive Button Example

<button class="
  px-6 py-3
  bg-gradient-to-r from-blue-500 to-purple-500
  hover:from-blue-600 hover:to-purple-600
  active:from-blue-700 active:to-purple-700
  focus:ring-4 focus:ring-blue-300
  disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed
  text-white font-semibold rounded-lg
  transform hover:scale-105 active:scale-95
  transition-all duration-200
  shadow-lg hover:shadow-xl
">
  Interactive Button
</button>

Continue Learning