API
Style utilities
Values in style, one custom property per decision.
A style utility is a custom property in an element's style attribute: style="--p: 4; --w--md: fit-content". The rule is Tailwind's names, CSS values: a utility is named after the Tailwind class it stands in for (--justify, --items, --rounded), and it takes the value its CSS property takes (--justify: space-between, --w: 100%). If you know the property and not the class, look it up in the property-to-utility table.
The kit matches the attribute text, so spelling is exact: --<utility>: <value>, one space after the colon, declarations separated by ; .
Composed from atoms
No component class here — just flex, gap, padding, radius, border, shadow, and color utilities reading the same semantic tokens.
<article
style="
--display: flex-col;
--p: 11;
--gap: 6;
--w: 100%;
--max-w: 96;
--text: var(--color-card-foreground);
--bg: var(--color-card);
--border-color: var(--color-border);
--border-width: 1px;
--shadow: md;
--rounded: var(--radius-lg);
"
>
<div
style="
--display: flex;
--items: center;
--justify: space-between;
--gap: 4;
"
>
<span data-ui="badge" data-badge-variant="primary">Utilities</span>
<span data-ui="text-xs" style="--text: var(--color-muted-foreground)">12 groups</span>
</div>
<hgroup
style="
--display: flex-col;
--gap: 2;
"
>
<h3 data-ui="text-lg" style="--font-family: var(--font-family-heading)">Composed from atoms</h3>
<p
data-ui="text-sm"
style="
--text-wrap: pretty;
--text: var(--color-muted-foreground);
"
>
No component class here — just flex, gap, padding, radius, border, shadow, and color utilities
reading the same semantic tokens.
</p>
</hgroup>
<div
style="
--display: flex;
--items: center;
--pt: 4;
--gap: 4;
--border-t: 1;
"
>
<button type="button" data-ui="button" data-button-variant="primary">Get started</button>
<button type="button" data-ui="button" data-button-variant="ghost">Browse groups</button>
</div>
</article>What a value can be
- A scale number on spacing, margin, and sizing utilities:
--px: 6is6 × --spacing. The space tokens sit on this scale (2xs 1, xs 2, sm 4, md 6, lg 11, xl 24, 2xl 40), so write the number and skip thevar(). - A length or any CSS value:
--w: 100%,--rounded: var(--radius-lg),--shadow: 0 1px 2px black. - A token by name, on a few utilities, at every tier:
--shadow: md,--font-weight: strong,--display: flex-col. Thevar()form (--shadow: var(--shadow-md)) always works too. - A keyword where the property takes one:
--position: sticky,--w: fit-content,--grid-cols: subgrid.
One space after the colon
A tier gate reads the property itself, so --p:4 and --w--md:6 work with any spacing. A keyword (auto, subgrid), a token by name (md, strong), a --display shorthand, --bg: none, and --position: sticky are matched as text, and the text the kit looks for has one space after the colon. Written without it, --w:auto is 0 wide and --shadow:md draws nothing. The audit (<ui-debug>, the editor, and the ESLint plugin) reports both, and the formatter writes the space back.
The place this bites is a framework's object style: React and Vue serialize { "--w": "auto" } as --w:auto, and JSX is outside the linter. There, write those values as a string style, or in the var() form ("--shadow": "var(--shadow-md)").
Rules that bite
- A tier needs a base unless the utility works tier-only (the tables say which): write
--text: currentColor; --text--hover: red, not--text--hover: redalone. Padding and--bgwork tier-only on a plain element (--px--md: 8,--bg--hover: var(--color-muted)), since the reset leaves it unpadded and unpainted; where a primitive pads or paints the element itself, its value wins unless it reads the utility.--text, margin, and--font-sizedo not, because the base sets a link's color, the margins between text blocks, and a heading's size, and a tier-only value would reset them at rest. - Breakpoints reach layout and color; states reach color and effects.
--px--hoverdoes not exist. The anchor utilities (--anchor-name,--position-anchor,--position-area) take both. - A utility on a primitive flattens its states:
--bg: redon a button is red on hover too. Add the state tier, or set the hook (--ui-button-bg) instead. - Breakpoints read the nearest inline-size container, not the window. See breakpoints.
Every utility that writes a property is registered with @property as non-inheriting, so a value never leaks into a child; the --before-* / --after-* forms are the exception, read on the pseudo-element and never registered. Utilities live in @layer ui.utilities at zero specificity: they beat component rules by layer order, and any rule you write in overrides beats them.