Skip to main content
Contact
Design Systems

Design systems are tokens, not components

A component library is the visible half of a design system. The half that decides whether a rebrand takes an afternoon or a quarter is the token architecture underneath it.

  • Jonas Wilder
3 min read

Most teams start a design system by building a button. It is the obvious first move: everyone agrees a button is a component, everyone has forty of them scattered across the codebase, and consolidating them feels like progress. It is progress. It is also the part that ages worst.

The button you build in month one encodes a hundred small decisions — a radius, a focus ring, a disabled opacity, the exact grey of a secondary variant. If those decisions live inside the component, then every one of them is a merge conflict waiting for the day the brand changes.

What a token actually buys you

A token is a named decision. --primary is not “orange”; it is “the colour this product uses to mean act now”. Naming it that way does three things at once:

  • A rebrand becomes one edit. Change the value, not the forty components that reference it.
  • Dark mode falls out for free. The name stays constant while the value flips per theme.
  • Review gets easier. A pull request that adds bg-orange-700 is visibly wrong in a way that bg-primary never is, because the first one is a decision being made in the wrong place.

The third is the one people underestimate. A token architecture is mostly a review tool.

Three layers, each with one job

The arrangement we land on for nearly every project has three tiers, and the discipline is that no tier reaches past the one below it.

  1. Palette aliases name the brand on top of a raw scale. This is the only layer that ever sees a literal colour.
  2. Semantic variables map meaning onto that palette, once per theme. --background, --foreground, --border.
  3. The bridge exposes those variables to utility classes so markup can say bg-background without knowing which theme is active.
@theme {
  --color-primary-700: var(--color-orange-700);
}

:root {
  --primary: var(--color-primary-700);
}

@theme inline {
  --color-primary: var(--primary);
}

Markup only ever touches the third layer. That is the whole rule, and it is enforceable by grep.

The test for whether something deserves a token is ownership, not inconvenience. If you could restyle it, it is a token. If it belongs to somebody else — another company’s logo, a partner’s brand colour — it is a fact, and it ships as a literal.

Where it breaks

Two ramps that are not parallel will catch you out. A card surface that sits above the page in dark mode may sit below it in light, and a component that hard-codes the dark relationship flattens to nothing when the theme flips. The fix is unglamorous: pair the two values on the element so they cannot drift apart, and check every surface against both ramps before assuming the relationship holds.

The other failure is subtler. Once a system has tokens, there is a pull toward adding one every time a value is slightly off. Resist it. A new stop in the palette should answer a different question, not a four-unit difference that only an eyedropper can see.

Jonas Wilder

Design director. Interested in the boundary where a design system stops being a component library and starts being a set of decisions.

More from Jonas Wilder
All posts

Ready to get off the ground?

Tell us what you're building. We'll tell you how we'd approach it — no pitch deck required.

Book a call