Design systems practice

Tokens before components: how I start every design system

Tokens before components. Design systems practice cover for the veljanoski.com blog

Most design systems start with a button. Mine start with an argument about naming. That is not a quirk, it is the whole method: if the tokens are right, the components are mostly bookkeeping. If the tokens are wrong, every component is a small negotiation you will lose slowly over the next year.

The order of operations

Every system I have shipped, from client libraries to GliaKit, follows the same sequence, and the sequence is the point:

  1. Primitives. The raw palette, the type scale, the spacing ramp, the radii. Boring on purpose. No opinions yet.
  2. Semantic tokens. Names that describe a job, not a value: surface.raised, text.muted, border.strong, accent.default. This is the layer people actually use.
  3. Modes. Light and dark are not two palettes, they are two mappings of the same semantic names onto different primitives. If dark mode needs a new token name, the semantic layer has a hole.
  4. Components. Only now. Each one consumes semantic tokens exclusively, never primitives.

Teams that skip straight to step four end up with forty shades of grey and a button that looks slightly different on the settings page. Not because anyone was careless, but because there was nothing to be careful with.

Semantic names are a contract with the future

The test I use for a token name: can a new designer guess where to use it without opening the docs? gray-300 fails. border.subtle passes. accent.default passes and survives a rebrand, which emerald-600 never will.

I keep the semantic layer deliberately small. A working SaaS product needs roughly:

  • Four surfaces: base, raised, sunken, overlay
  • Four text roles: default, muted, subtle, inverse
  • Three borders: subtle, default, strong
  • One accent with default, hover, active, and a soft tint
  • Status colors for success, warning, danger, info, each with a soft tint

That is under thirty names. Everything else is a component-level decision, and component-level decisions are cheap to change.

Rule of thumb

If you need a new semantic token, first ask whether an existing one is being used wrong. Nine times out of ten the answer is yes, and the tenth time you have found a real gap worth naming.

Figma variables and code, in lockstep

The single most valuable thing a design system can do is make the Figma file and the codebase describe the same reality. Not similar. The same.

In practice that means the Figma variable collection and the Tailwind config (or CSS custom properties) share names one to one. In GliaKit, surface/raised in Figma is bg-surface-raised in code, and the light and dark modes in Figma are the same two mappings the CSS ships. When a developer opens a Figma frame, the variable panel reads like the class names they are about to type.

This is also why I build tokens before components in Figma specifically. Variables have to exist before components can bind to them, and rebinding forty components later is the kind of afternoon that turns people off design systems for life.

What I deliberately skip in version one

  • Density modes. Nobody needs compact and comfortable on day one. Ship one density, measure.
  • A full icon system. Pick a library, wrap it, move on. Custom icons are a version three problem.
  • Exhaustive component variants. A button needs primary, secondary, ghost, and destructive. It does not need twelve.
  • Documentation sites. The Figma file with good descriptions and the code with good comments is documentation. A separate site is maintenance debt until the team is bigger than five.

Governance is a two-line rule

Design systems die from erosion, not catastrophe. The governance I set up is intentionally tiny:

  1. Components use semantic tokens only. A hard-coded hex value in a component is a bug, and the linter (or the Figma review) treats it as one.
  2. New tokens require a written reason, and the reason has to explain why no existing token fits.

That is it. Everything else is taste, and taste is what the design lead is for.

Where to start on Monday

Open your product, screenshot six screens, and list every distinct color, size, and radius you find. That list is your primitives, whether you like them or not. Then name the jobs those values are doing. That is your semantic layer. Only after that, open the component file.

If you want the tokens-first structure without the six weeks, that is what my design systems service is, and GliaKit is the same method shipped as a Figma kit and a Next.js boilerplate you can start from.