Variants are how utility classes handle interaction states that inline styles never could.
A variant is a prefix that applies a utility only under a condition: hover:bg-indigo-700 sets the background only on hover. Variants exist for nearly every pseudo-class:
- interaction:
hover:,focus:,focus-visible:,active:,focus-within:, - forms:
disabled:,checked:,required:,invalid:,user-invalid:,placeholder:(the pseudo-element),file:, - structure:
first:,last:,odd:,even:,nth-3:,not-first:,empty:, open:for details and dialogs,before:/after:for pseudo-elements,aria-expanded:anddata-[state=open]:for attributes.
In v4, hover: is wrapped in @media (hover: hover), so tapping on a touch screen doesn't leave sticky hover styles.
Styling based on other elements:
- Mark a parent with
group, then usegroup-hover:orgroup-focus:on any descendant. - Mark a sibling with
peerand usepeer-checked:orpeer-invalid:on later siblings (it must come before them, as with the CSS~combinator). has-checked:andhas-[img]:use:has()to style an element by its contents.- Name groups to disambiguate nested ones:
group/itemwithgroup-hover/item:.
Variants stack: dark:md:hover:bg-gray-700. In v4 they apply left to right, like the nested CSS they produce.
Example
<button class="rounded-md bg-indigo-600 px-4 py-2 text-white hover:bg-indigo-700
focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600
active:scale-95 disabled:cursor-not-allowed disabled:opacity-50">
Save
</button>
<!-- group: hover anywhere on the card changes the arrow -->
<a href="/docs" class="group block rounded-lg p-4 hover:bg-gray-50">
Read the docs
<span class="inline-block transition group-hover:translate-x-1">→</span>
</a>
<!-- peer: the checkbox must come before the elements it styles -->
<label>
<input type="checkbox" class="peer sr-only">
<span class="rounded-full bg-gray-200 px-3 py-1 peer-checked:bg-emerald-500 peer-checked:text-white">
Notifications
</span>
</label>
<ul><li class="py-2 not-last:border-b">One</li><li class="py-2 not-last:border-b">Two</li></ul>Remember
- Prefix any utility with a state: hover:, focus-visible:, disabled:
- group/group-* styles children from a parent's state
- peer/peer-* styles later siblings; the peer must come first
- hover: only applies on devices that can hover
Watch out for
- Putting the peer element after the element that uses peer-*; the sibling selector only looks backwards.