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

Try it yourself
Loading editor…
Press Run to see the result.

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.

Example Β· html
<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.

Example Β· html
<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.

Example Β· html
<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

  • Be the first to comment on this lesson.
data-* and aria-* Variants β€” Tailwind CSS | SoundsCode