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:
- Type — two families, one scale
- Space — one rhythm, applied everywhere
- Colour — a substrate, an ink, and accents you can count on one hand
- Radius — one value, maybe two
- 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.