data · aria
Attribute variants for headless UI libraries like Radix, Ark and React Aria — data-[state=open]: and aria-expanded: prefixes, built safely.
What they build
Both helpers put an attribute variant in front of a set of classes, so a component can style itself from the state its headless library — Radix, Ark, React Aria — already writes to the DOM.
data("state", "open", "opacity-100"); // → "data-[state=open]:opacity-100"data("disabled", null, "pointer-events-none"); // → "data-[disabled]:pointer-events-none"aria("expanded", "rotate-180"); // → "aria-expanded:rotate-180"data takes the attribute, a value and the classes — pass nullas the value to match the attribute's presence rather than a particular value. aria takes the attribute and the classes.
Why these are not keys
ss accepts base plus Tailwind's own keys — 149 in total, and every one of them is a fixed name. That closed set is what makes autocomplete exhaustive and a typo a compile error.
Anything carrying a value of its own — data-*, aria-*, supports-*, has-*, not-*, an arbitrary min-[…] — is deliberately absent from that list, because the value cannot be enumerated ahead of time. These two helpers cover the first two; everything else goes through withPrefix.
Values with spaces
A value containing a space cannot appear in a class name, so write it Tailwind's way — with _, which Tailwind reads back as a space.
data("state", "half_open", "opacity-50"); // matches data-state="half open"A literal space is caught, not ignored
The arguments must be literals
Both helpers assemble the variant prefix at runtime, so Tailwind never sees the finished class in your source — they need the build plugin, exactly like ss. One line of setup covers all of them.
The scanner reads the literal strings at the call site and over-approximates on purpose, so a value that flips between two states costs you nothing: both branches are enumerated.
data("state", open ? "open" : "closed", "p-2") // both values are readA value held in a variable is not in the source to read, and neither is an interpolated template. Keep the attribute, the value and the classes literal at the call site — a ternary is fine, a variable is not.