Skip to content
All posts
· 6 min read

Design tokens in Tailwind CSS v4: one @theme to rule your UI

Tailwind v4 moves your design system into CSS itself. Semantic tokens, a dark-first contrast budget, and why fewer names beat more utilities.

Tailwind CSSDesign SystemsCSS
Design tokens in Tailwind CSS v4: one @theme to rule your UI — cover

Tailwind v4 retires the JavaScript config file: your design system now lives in CSS, in a single @theme block that turns every token into both a CSS variable and a utility class. It sounds like a small change. It quietly fixes how teams name things.

Name the role, not the colour

Raw palette names (slate-800, violet-400) leak implementation into every component. Semantic tokens — bg, surface, ink, muted, line, accent — describe what a colour is for, so swapping the accent or retuning contrast is a one-line change instead of a codebase-wide find-and-replace.

css
@theme {
  --color-bg: #0a0a0c;
  --color-surface: #121216;
  --color-ink: #f4f4f5;   /* primary text */
  --color-muted: #a1a1aa; /* secondary text */
  --color-line: rgba(255, 255, 255, 0.07);
  --color-accent: #f43f5e;
}

Each token doubles as a class (bg-surface, text-muted, border-line) and a variable for the occasional handwritten rule. One source of truth, two ways to consume it.

A dark-first palette is a contrast budget

  • One accent, used sparingly — everything else stays neutral
  • Hairline borders at 7% white do the separating work shadows do in light UIs
  • Elevation is a slightly lighter surface, not a bigger shadow
A design system isn't the components. It's the constraints that make the components look inevitable.

Keep the token count under twenty. If a value shows up twice without a name, name it; if a token hasn't been used in a month, delete it. The system stays legible exactly as long as it stays small.