Keys
All 149 keys ss() accepts — base, breakpoints, max-width ranges, pseudo-classes, pseudo-elements, media queries, group-* and peer-* states.
The full list
ss accepts base plus Tailwind's own keys — 149 in total, and nothing else. That closed set is what makes autocomplete exhaustive and a mistyped variant a compile error rather than a class with no rule behind it.
| Group | Count | Keys |
|---|---|---|
| base | 1 | unprefixed classes |
| Breakpoints | 5 | sm md lg xl 2xl |
| Max-width ranges | 5 | max-sm max-md max-lg max-xl max-2xl |
| Pseudo-classes | 34 | hover focus focus-within focus-visible active visited target first last only odd even first-of-type last-of-type only-of-type empty disabled enabled checked indeterminate default optional required valid invalid user-valid user-invalid in-range out-of-range placeholder-shown autofill read-only open inert |
| Pseudo-elements | 10 | before after first-letter first-line marker selection file backdrop placeholder details-content |
| Media & feature queries | 17 | dark motion-safe motion-reduce contrast-more contrast-less forced-colors inverted-colors portrait landscape print noscript pointer-fine pointer-coarse pointer-none any-pointer-fine any-pointer-coarse any-pointer-none |
| Direction & transition | 3 | rtl ltr starting |
| Descendants | 2 | * (direct children) ** (all descendants) |
| group-* | 36 | parent state — group-hover group-focus group-checked group-open … |
| peer-* | 36 | sibling state — peer-hover peer-checked peer-invalid … |
Nested groups
A bucket value can be another map, which stacks the prefixes. Every nested group takes the same 149 keys under the same rules, and that is how a compound variant is spelled — the ss page has the nesting rules in full.
ss({ base: "text-black p-4", md: { base: "p-6", // → md:p-6 hover: "p-8", // → md:hover:p-8 "max-lg": "grid", // → md:max-lg:grid },});Keys containing a hyphen — max-lg, group-hover, motion-reduce — are quoted, as any JavaScript object key with a hyphen has to be.
Deliberately absent
Anything with a value of its own is absent by design: data-*, aria-*, supports-*, has-*, not-* and arbitrary queries such as min-[…]. A key that carries an open-ended value cannot be one entry in a finite list, so it cannot be completed or checked — those variants get their own helpers instead.
dataandaria— attribute variants, for headless UI libraries.withPrefix— the escape hatch for every other Tailwind variant.
withPrefix("supports-[display:grid]", "grid"); // → "supports-[display:grid]:grid"withPrefix("has-[:checked]", "bg-blue-50"); // → "has-[:checked]:bg-blue-50"withPrefix("group-[.open]", "rotate-90"); // → "group-[.open]:rotate-90"Exported at runtime
The lists themselves are exported, so JavaScript can read the same values the classes use. screens maps a breakpoint to its width; stateKeys, screenKeys and maxScreenKeys are the key names.
import { screens, screenKeys, maxScreenKeys, stateKeys } from "tailess"; window.matchMedia(`(min-width: ${screens.md})`).matches; // "48rem"Generated, then checked
stateKeys is regenerated and re-verified against the Tailwind compiler in CI, and the test suite asserts real generated rules for every one of the 149 keys on Tailwind 4.3.3.