Craft · 12 min read

Fifty faces, one skeleton

What happens to a design system when you refuse to let any component name a colour, and hand the entire palette to a single attribute on the root element.

ŞH

Şahan Hasret

Published 28 August · Updated today

The same page, photographed under fifty different lights. Nothing in the markup moved.

Most design systems die of the same disease. Someone, under deadline, writes color: #2563eb inside a button. It works. Nobody notices. Six months later there are four hundred of them, and the word "rebrand" means a quarter of engineering time.

The rule here is narrower than it sounds, and it is the only rule: no component stylesheet may name a colour, a radius, a shadow or a typeface. It may only read a token. Everything else follows from that.

Three tiers, and only two of them move

Primitives are raw values. Semantics give them jobs — --bk-primary, --bk-surface, --bk-border. Component knobs are the last mile. A skin rewrites the middle two and never touches the first or the last.

A skin is not a theme file. It is a claim about what the words "surface" and "border" mean in one particular world, and a component that reads those words gets dressed for free.

That is why fifty skins cost roughly nine kilobytes each instead of a fork of the entire stylesheet, and why adding a component means adding it once.

Where it gets hard

Contrast. A single "text on primary" token breaks the moment a skin paints primary as fluorescent yellow. One accent needs three foregrounds — on the fill, on the page, and on a soft chip — and each has to be audited separately.

Audited, not eyeballed

4,386 foreground/background pairs are checked on every commit, across fifty-one palettes in both colour modes, without opening a browser.

  1. Resolve the token graph the way a browser would — var() chains, fallbacks, alpha compositing.
  2. Walk every pair the components actually paint.
  3. Move lightness in one per cent steps until the pair clears AA. Never touch hue or saturation.
  4. Write the fix back into the skin as a regenerable block.

The last step matters more than the first three. A fix that lives in someone's memory is not a fix.

What you give up

Arbitrary one-off colours, mostly. If a page needs a shade the token contract does not have, the honest move is to add it to the contract — for all fifty skins — or to accept that the page will look wrong in forty-nine of them.

That trade is the whole product.

design systems css tokens accessibility

ŞH

Written by Şahan Hasret

Full-stack developer in Ankara. Writes CSS that other people have to maintain, and tries to make that a kindness rather than a threat.

Keep reading

All essays

One essay a month. Nothing else.

No tracking pixels, no drip campaign, no "quick question".