Most “token” projects start with a bulk export.

Hundreds of color styles leave the design file. Someone renames them in a JSON blob. CSS variables appear. Three months later marketing invents #4B41E1 inline, mobile uses a different spacing scale, and dark mode is a fork nobody merges.

Tokens are not a file format stunt. They are a contract for how visual decisions move from design intent into shipping code — with owners, layers, and a ban list.

The Design Tokens Community Group (a W3C Community Group) publishes a format so design tools and code generators can exchange those decisions without proprietary lock-in. That interoperability is useful. It does not replace product discipline about what is allowed to exist.

Letterpress punches and indigo ink on grid paper

Shared primitives. Controlled ink. Not freehand on every surface.

Why dumps fail

A dump answers “what colors exist in the file?” A contract answers:

  • Which values may components use?
  • Who can add a new one?
  • How do light and dark map?
  • What breaks when we rename?
  • How does CI reject freestyle hex?

Without those answers, tokens become a second junk drawer — only with better JSON formatting.

Three layers that stay honest

Token layers diagram

1. Primitive

Raw ramps: color.indigo.600, space.4, font.size.300. They document the material. UI code should rarely import them directly. Primitives change when the brand palette changes — rarely, on purpose.

2. Semantic

Role-based: color.text.muted, color.action.primary, color.surface.raised, space.section.y. Components and pages consume semantics. Themes and modes remap semantics to primitives.

3. Component (optional, sparse)

Only when semantics are not enough: button.padding.x. Every component token is a future regret if overused. Prefer extending semantics first.

When every button hardcodes a primitive, you have a palette dump, not a system.

Naming is product language

Bad names encode tools and accidents: Blue/500-copy-2, Gray 3 (final), spacing-from-figma-8.

Good names encode role and state:

  • color.border.strong
  • color.text.inverse
  • color.feedback.danger
  • space.section.y
  • font.display.weight

Alias across modes (light/dark, high-contrast) at the semantic layer. Do not duplicate entire component trees for each theme.

Ownership and change control

Decision Owner Gate
Add a primitive ramp Design systems lead Brand review
Add a semantic token Design + eng PR + docs
One-off hex in a feature Rejected unless dated exception
Breaking rename Systems lead Migration note + version bump
Deprecate token Systems lead Find usages; remove after soak

If anyone can invent a token in a feature PR without review, the set becomes archaeology in six months.

Codegen is the bridge, not the product

Pipelines (Style Dictionary and similar) should emit platform outputs from one source: CSS variables, iOS/Android, documentation tables. The source of truth is the token set plus its human docs — not whichever tool last exported.

Track in CI where you can:

  • Diff of semantic tokens on every PR
  • Unused token detection for dead weight
  • Contrast checks on text/surface pairs before merge

Dark mode and multi-brand

Map modes by reassigning semantics — for example color.text.primary points at different primitives in light vs dark. Multi-brand programs need a clear rule: shared primitives with brand semantic packs, or fully separate sets. “One file with everything” usually means nobody knows what ships where.

What tokens will not fix

  • Broken information architecture
  • Components with no keyboard model
  • Performance debt from forty third parties
  • Designers and engineers who never share a review channel

Tokens reduce accidental variance. They do not create taste, research, or process.

A practical adoption sequence

  1. Inventory colors, spacing, and type already in production (not only Figma).
  2. Define semantic roles for text, surface, border, action, and feedback.
  3. Map primitives; kill near-duplicates.
  4. Wire codegen to CSS variables used by the real component library.
  5. Ban free hex in lint for application UI paths.
  6. Document how to request a new token.

Closing

Adopt a standard exchange format when it helps your toolchain. Write the contract first: layers, names, owners, and “no free hex.” Then exports stop being archaeology and start being releases.


Building or cleaning a design system? Start a project inquiry with your current token file and three components that always drift. That is enough for a contract workshop.