Svetlozar Apostolov
Design systems · ongoing

One token system, two products, and a reader that is not human

A shared semantic token layer sitting on top of a third-party component library, feeding two products with separate libraries — and an ongoing attempt to state it explicitly enough that a model can build with it instead of inventing the parts it cannot see.

Role
System owner and maintainer
Products served
2
Base library
Ant Design
Status
Live and ongoing

Three tiers, and the rule for each

The value of a token layer is not that values are named. It is that there is a single answer to “which token do I use here”, and that the answer survives a rebrand.

PrimitiveSemanticComponentrose/500rose/700rose/50surface / accentborder / accenttext / on-accentProduct Abutton / primaryProduct Bbutton / primary
One value followed from primitive through semantic to component, landing in two product libraries. Products can look different without either team quietly redefining what a semantic token means.
  • Primitive. Raw values. A palette, a spacing ramp, a type scale. Nothing in a component may reference these directly.
  • Semantic. Values with a job. Surface, border, text, and their states. This is the tier a designer reasons in, and the only tier that changes when a product’s identity changes.
  • Component. Scoped to one component and one state, referencing semantic tokens. This tier exists so a component can deviate deliberately and visibly, rather than by someone hard-coding a hex value at two in the afternoon.

Two products share the primitive and semantic tiers and maintain their own component tier. That split is the whole design of the system.

When to extend a library component, and when to break it

The front end builds on Ant Design and would prefer to stay on native components. Business logic keeps demanding shapes Ant does not have. That tension is permanent, so it needs a rule rather than a fresh argument every sprint.

  • Extend when the requirement is additive and the behavioural contract is unchanged — an extra slot, a new size, a state the library already models. Extending keeps the accessibility and keyboard behaviour we did not write and do not want to maintain.
  • Break and own when the requirement changes what the component is. A control whose options depend on a value elsewhere in the form, or a container that holds three different things behind one affordance, is not a themed version of a library component. It is a different component wearing its clothes, and pretending otherwise is how a design system starts lying about what it contains.

The cost of getting this wrong runs one way. An over-extended library component looks fine in Figma and fails at the third edge case in code, usually after the estimate has been given.

Teaching it is the test

A token system only its author can use is a personal preference with extra steps. The honest test of the structure was handing it to someone else.

I ran a thirty minute session on the token tiers and the naming rules, then had the designer build one real component in each of the two product libraries. Not a demo — a component the product needed, in both libraries, so the shared-versus-scoped boundary had to be understood rather than described.

Both were built the same day. The parts that needed explaining twice are the parts of the naming that are wrong, and that is the most useful documentation feedback I have had.

Making the system readable by something that is not a designer

Handing a design file to a model produces confident nonsense. Not because the model is bad at design, but because a design system encodes most of its rules in places a file does not contain: canvas position, the meaning of a boolean property, which combinations of variants are valid, and the reasons a component exists at all.

The work is stating those explicitly:

  • Every component property described by what it means, not by what it toggles.
  • Valid and invalid combinations written down, because a variant matrix will happily produce states that must never ship.
  • Selection rules: when to use this component, and when to use the one next to it.
  • The token a component reads for each state, so appearance is derivable rather than guessed.

The goal is a specification going in and a compliant flow coming out. It is not solved. What has become clear is that everything a model needs is also everything a new designer needs, written more plainly than we usually bother to write it. The agent work is a forcing function for documentation the system should have had anyway.

Where it currently falls short

  • Components sit loose on a shared canvas with no per-component documentation pages. I have proposed the alternative twice: a page per component covering usage, properties, tokens, and when not to use it. It has not been adopted, and it remains what I would do with ownership of the format.
  • Figma runs ahead of what is built. An earlier platform redesign reached roughly half implementation before priorities shifted, so parts of the system describe a product that does not exist yet. Design debt in that direction is quieter than the other kind and just as expensive.
  • Naming is inconsistent across the older parts of the tree, which is exactly where a new person and a model both trip.

What I would measure

  • Hard-coded values appearing in new work. The clearest health metric a design system has, and countable directly from the file.
  • Time from a new designer’s first day to their first merged component. The onboarding session gave me one data point. A number needs several.
  • Component reuse against one-off builds, per release. If one-offs are rising, the system is missing something rather than being ignored.
  • Questions asked per component. Whatever gets asked about most is the next thing to document. This is free to collect and nobody collects it.
PreviousA roulette table you can bet on the edges ofNextBack to all work