Skip to content
← Writing

Tokens before components

  • design-systems
  • tailwind

A site built from twelve individually excellent components looks like twelve individually excellent components. The thing that makes it read as designed is that every one of them draws from the same small set of decisions.

The decisions worth centralising

Five, and they fit on one screen:

  1. Type — two families, one scale
  2. Space — one rhythm, applied everywhere
  3. Colour — a substrate, an ink, and accents you can count on one hand
  4. Radius — one value, maybe two
  5. Motion — one easing curve, one duration band

That is the whole system. Everything else is composition.

Tailwind v4 makes this the default

There is no tailwind.config.js any more. Tokens live in CSS, and declaring one generates its utilities:

@theme {
  --color-canvas: #FBFBFA;
  --color-ink:    #111111;
  --font-serif:   var(--font-newsreader), Georgia, serif;
  --radius-card:  0.75rem;
}

--color-canvas becomes bg-canvas, text-canvas, border-canvas. One declaration, one source of truth, no sync step between config and stylesheet.

Why this matters more when an agent writes the code

Given a blank slate, a language model will produce competent, generic output — #6366f1 on white with shadow-lg and Inter. Not because it lacks taste, but because averages are what it optimises for absent constraints.

Give it a token block and the failure mode inverts. It now has a specific vocabulary to compose within, and the output stops looking like every other generated site. The tokens are the brief.

Re-skinning as the test

The honest measure of whether a token layer is real: can you change the entire visual identity by editing only @theme?

If yes, the system holds. If you find yourself hunting hex values through component files, it was never a system — just a stylesheet with good intentions.