Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Hover, focus and other states

A variant is a prefix with a colon that applies a utility only in a certain state. hover:bg-indigo-500 changes the background while the cursor is over the element. In plain CSS you would write a separate rule with the :hover pseudo-class.

Hover

The "Add to cart" button gets lighter on hover:

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
 
<button class="rounded-lg bg-indigo-600 px-4 py-2 text-white hover:bg-indigo-500">
  Add to cart
</button>

Hover over the button — the background turns lighter. In Tailwind 4 hover: only applies on devices with a mouse: on a phone the button does not "stick" in the hover state after a tap.

Focus

When a user moves through the page with the Tab key, the focused element must stand out. focus: applies on any focus, focus-visible: only on keyboard focus:

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
 
<input class="rounded-lg border border-gray-300 px-3 py-2 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200 focus:outline-none" placeholder="Email">
<button class="ml-2 rounded-lg bg-indigo-600 px-4 py-2 text-white focus-visible:ring-4 focus-visible:ring-indigo-300 focus-visible:outline-none">
  Subscribe
</button>

Click into the field — the border turns blue and a soft ring appears. Press Tab to move to the button: a ring appears around it, but not when you click it with the mouse.

The main variants

VariantWhen it applies
hover:The cursor is over the element
focus:The element has focus
focus-visible:Keyboard focus
active:The element is being pressed
disabled:A field or button is disabled
first:, last:First or last among its siblings
odd:, even:Odd or even
placeholder:The hint text in a field

List rows

odd: and last: save you separate classes for striping and for the last row:

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
 
<ul class="w-64">
  <li class="border-b border-gray-200 p-2 odd:bg-gray-50 last:border-0">Headphones</li>
  <li class="border-b border-gray-200 p-2 odd:bg-gray-50 last:border-0">Speaker</li>
  <li class="border-b border-gray-200 p-2 odd:bg-gray-50 last:border-0">Charger</li>
</ul>

Odd rows are gray, and the last one has no bottom line.

Combining variants

Variants stack from left to right: md:hover:bg-indigo-500 works only on a wide screen on hover, and disabled:opacity-50 dims a disabled button:

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
 
<button disabled class="rounded-lg bg-indigo-600 px-4 py-2 text-white hover:bg-indigo-500 disabled:cursor-not-allowed disabled:opacity-50">
  Out of stock
</button>

Common mistake

Removing the focus outline with outline-none and giving nothing in return:

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
 
<button class="rounded-lg bg-indigo-600 px-4 py-2 text-white focus:outline-none">Buy</button>

Now nobody can see where the focus is when using Tab, and keyboard users struggle with the site. If you remove the default outline, always add your own: focus-visible:ring-2.

Thanks for reading!

Donate to the site — we work hard for you.