🛠️DevKitPack
Back to blog
Design Systems5 min read

Design Tokens, Explained: The Single Source of Truth for UI

Hardcoded hex values and magic numbers scattered across a codebase are a slow leak in any design system. Design tokens plug it by naming every decision once and reusing it everywhere.

Every design system eventually faces the same quiet crisis: the same blue appears as #3B82F6 in one file, #3b82f6 in another, and rgb(59, 130, 246) in a third, and nobody is quite sure which is canonical. Multiply that by every color, spacing value, font size, and radius in a product, and you have a system that looks consistent by luck rather than by design. Design tokens are the fix — a way to name each decision once and refer to it everywhere.

The problem: magic values everywhere

When a designer picks a color or a developer types a pixel value, that decision gets frozen into code as a literal. On its own, harmless. At scale, it becomes a thousand tiny copies of the same intention, each one free to drift. Change the brand blue and you are hunting through the codebase for every shade and spelling of it, certain you have missed a few.

The deeper issue is that the literal value carries no meaning. The number 16 might be a font size, a gap, or a border radius; the codebase cannot tell you which, or why. When everything is a raw value, nothing is a decision you can revisit — it is just a constant someone typed once and everyone is now afraid to touch.

What a design token is

A design token is a named, stored design decision. Instead of writing the value, you write the name, and the name resolves to the value in one central place. "Brand blue" lives once; everything that needs it points at the name. Change the definition and every reference updates at once, because they were never copies — they were all the same source.

Tokens are platform-agnostic by nature. The same token can compile to a CSS custom property for the web, a constant for iOS, and a resource for Android. That is the real promise: one set of decisions, expressed once, kept identical across every surface a product ships on.

The three tiers

Mature token systems are layered, and the layering is what makes them flexible without becoming chaos. Three tiers are common: primitive, semantic, and component.

  • Primitive (or core) tokens are the raw palette — blue-500, gray-900, space-4. They describe values, not usage. They rarely change.
  • Semantic tokens describe intent — color-text-primary, color-surface, color-border. They point at primitives and are what most of the UI actually consumes.
  • Component tokens are the most specific — button-background, card-padding. They point at semantic tokens and exist only where a component needs a local override.

The discipline that makes this work is simple: components reference semantic tokens, semantic tokens reference primitives, and you almost never reach across the layers. That indirection feels like extra ceremony until the day you need to reskin the entire product — then it is the difference between an afternoon and a month.

:root {
  /* primitive */
  --blue-600: #2563eb;
  --gray-900: #111827;

  /* semantic — points at primitives */
  --color-accent: var(--blue-600);
  --color-text:   var(--gray-900);
}

.button {
  /* component — consumes semantic, never primitive */
  background: var(--color-accent);
  color: white;
}

Naming tokens

Naming is where most token systems live or die, because a name you cannot predict is a name nobody will use. A good convention reads like a path from general to specific: category, then property, then variant — color-text-muted, space-inline-sm, font-size-heading-lg. The pattern should be regular enough that a developer can guess a token's name correctly without looking it up.

Resist two temptations. The first is naming by appearance — color-blue is a primitive's job, not a semantic token's, because the day the accent turns green, color-blue meaning green is a small lie that confuses everyone. The second is over-specificity: a token per element is just hardcoding with extra steps. Name by role, and let one good semantic token serve many places.

A token named for what it is will betray you the day the design changes. Name it for what it does.

Theming and dark mode

Tokens are what make theming tractable. Because components consume semantic tokens rather than raw values, a theme is nothing more than a different set of values behind the same names. Dark mode is not a sprawling override of every component; it is a single remapping of the semantic layer.

:root        { --color-surface: #ffffff; --color-text: #111827; }
:root.dark   { --color-surface: #0a0a0b; --color-text: #e5e7eb; }

.card { background: var(--color-surface); color: var(--color-text); }

The card does not know or care which theme is active. It asks for the surface color and the text color, and the active theme answers. Adding a third theme — high-contrast, a seasonal brand, a per-customer skin — means adding one more set of values, not touching a single component.

Keeping design and code in sync

Tokens earn their keep only if the design tool and the codebase agree on them. The common pattern is to treat tokens as data — usually a structured file the design team owns — and generate platform-specific output from it. Designers edit the source of truth; a build step turns it into CSS variables, native constants, and documentation, so the two worlds never quietly diverge.

You do not need elaborate tooling to start. A single file of CSS custom properties, agreed on and actually used, already captures most of the value: one place to change, one vocabulary to share, one source of truth. The tooling can grow later, once the habit of naming decisions is in place.

Where to begin

Start with the values that hurt most when they drift: colors and spacing. Pull them into a small set of named tokens, route your components through the semantic layer, and resist hardcoding even once. The first time you change the brand color in one line and watch the entire product follow, the abstraction stops feeling like overhead and starts feeling like the point.

Related posts