data-* and aria-* Variants
Drive styles straight from state attributes and accessibility roles.
Modern components track their state in attributes: a menu is data-state="open", a tab is aria-selected="true", a nav link is aria-current="page". Tailwind can style directly off those attributes, so your CSS follows your semantics for free.
aria variants
Boolean ARIA attributes have first-class variants: aria-checked:, aria-disabled:, aria-expanded:, aria-selected:. For any value, use the bracket form:
<a aria-current="page"
class="aria-[current=page]:text-cyan-600 aria-[current=page]:font-semibold">
Dashboard
</a>data variants
Match a custom data attribute the same way — perfect for headless libraries (Radix, Headless UI) that expose data-state:
<div data-state="open"
class="data-[state=open]:rotate-180 transition">…</div>Why this is a big deal
- No wrapper divs or extra classes toggled by JavaScript — you flip one attribute, styling reacts.
- The attribute you set for a screen reader is the same one that drives the visual state. Accessibility and appearance stay in sync by construction.
Example
When to use it
- A developer highlights an active sidebar link based on a data-current attribute using data-[current=true]:text-indigo-600 without toggling multiple classes from JavaScript.
- An accordion button's icon rotates when aria-expanded is true using aria-expanded:rotate-180, driven purely by the accessibility attribute.
- A navigation item shows a badge ring only when aria-selected is true using aria-selected:ring-2 to provide a keyboard-visible selection indicator.
More examples
Style from data attribute
data-[active=true]: variants style the element based on its data attribute value, letting JavaScript only toggle the attribute instead of a class list.
<li
data-active="true"
class="px-4 py-2 rounded
data-[active=true]:bg-indigo-50
data-[active=true]:text-indigo-700
data-[active=true]:font-semibold"
>
Dashboard
</li>aria-expanded chevron rotation
aria-[expanded=true]:rotate-180 on the child SVG reads the parent's ARIA attribute to flip the chevron, tying visual state to semantic state.
<button
aria-expanded="false"
class="flex items-center justify-between w-full px-4 py-3"
>
FAQ Question
<svg
class="w-5 h-5 transition-transform aria-[expanded=true]:rotate-180"
fill="none" stroke="currentColor" viewBox="0 0 24 24"
>
<path d="M19 9l-7 7-7-7"/>
</svg>
</button>Disabled via aria-disabled
aria-disabled: variants style the button based on the accessibility attribute rather than the HTML disabled property, keeping it focusable for screen readers.
<button
aria-disabled="true"
class="bg-blue-600 text-white px-5 py-2 rounded
aria-disabled:opacity-50
aria-disabled:cursor-not-allowed"
>
Save
</button>
Discussion