Hover, Focus and Other States

beginner
11 min

Hover, Focus and Other States

Static pages feel dead; interfaces feel alive because they respond to the user. Tailwind CSS styles interactive states the same way it handles breakpoints: with prefixes. hover:, focus:, active:, disabled:, and friends can be attached to any utility. In this lesson you will learn the state variants you need for buttons, forms, lists, and cards.

Hover and Active

Prefix a utility with hover: and it applies while the pointer is over the element; active: applies while it is being pressed:

html
<button class="rounded-lg bg-blue-600 px-5 py-2 text-white transition hover:bg-blue-700 active:bg-blue-800 active:scale-95"> Click me </button>

Adding the plain transition utility makes state changes animate smoothly instead of snapping. The active:scale-95 gives a satisfying press-down effect.

Focus States and Accessibility

Keyboard users rely on visible focus indicators, so never remove them without a replacement. Tailwind gives you three related variants:

  • focus: — element has focus by any means
  • focus-visible: — focused via keyboard (the one you usually want)
  • focus-within: — an ancestor style that applies when any child is focused

A well-behaved input:

html
<input class="rounded-lg border border-gray-300 px-4 py-2 outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-200" placeholder="Email address">

The ring utilities draw an accessible halo around the element without shifting the layout the way borders can.

Disabled and Form States

Style unavailable controls with disabled:, and validate visually with invalid:, required:, and checked::

html
<button disabled class="bg-blue-600 text-white px-5 py-2 rounded-lg disabled:cursor-not-allowed disabled:opacity-50"> Submit </button> <input type="email" required class="border invalid:border-red-500 invalid:text-red-600">

These map straight onto native CSS pseudo-classes, so the browser does the state tracking for you with zero JavaScript.

Group: Parent-Triggered Styles

Often a child should react when its parent is hovered, like a card whose title changes color. Mark the parent with group, then use group-hover: on children:

html
<a href="#" class="group block rounded-xl border p-6 hover:shadow-lg"> <h3 class="font-semibold text-gray-900 group-hover:text-blue-600">Card title</h3> <p class="text-gray-500">Hover anywhere on the card.</p> <span class="text-blue-600 opacity-0 transition group-hover:opacity-100">Read more →</span> </a>

Peer: Sibling-Triggered Styles

peer does the same trick for siblings, powering floating labels and toggle switches with pure CSS. Mark the input with peer, then style a following sibling with peer-focus: or peer-checked::

html
<input type="checkbox" class="peer sr-only" id="toggle"> <label for="toggle" class="block h-6 w-11 rounded-full bg-gray-300 peer-checked:bg-green-500"></label>

First, Last, and Odd Children

Structural variants clean up lists and tables: first:, last:, odd:, and even:.

html
<ul> <li class="border-b p-3 last:border-b-0 odd:bg-gray-50">Row</li> </ul>

Zebra striping and removing the final divider used to need extra CSS; now it is two utilities.

Stacking Variants

Variants compose with each other and with breakpoints. md:hover:scale-105 scales on hover only at tablet sizes and up; dark:hover:bg-gray-700 handles hover in dark mode. Order the prefixes from general to specific and Tailwind does the rest.

Key Takeaways

  • State variants are prefixes: hover:bg-blue-700, focus:ring-2, disabled:opacity-50.
  • Always keep a visible focus style; prefer focus-visible: with ring utilities.
  • group-hover: styles children when the parent is hovered; peer-checked: styles siblings.
  • first:, last:, odd:, even: handle structural styling like zebra stripes.
  • Variants stack with breakpoints for fully responsive interactive design.

Next up: Dark Mode, where a single dark: prefix gives your site a second theme.

Hover, Focus and Other States - Tailwind CSS | CodeYourCraft | CodeYourCraft