frontend / component libraries / design_tokens_and_theming.md

Design Tokens and Theming at Scale

4 min read source

Design Tokens and Theming at Scale

TL;DR

Design tokens are named design decisions stored as data — color.brand.primary = #2563eb, space.4 = 16px — the single source of truth shared by design and every platform. They’re tiered: primitive (raw values) → semantic/alias (color.text.danger) → component (button.bg). The modern pipeline is Figma → tokens (JSON, increasingly the W3C DTCG format) → Style Dictionary → CSS custom properties / iOS / Android outputs. At runtime, CSS variables make theming (dark mode, multi-tenant) a matter of swapping values, not rebuilding.

Interview Q&A

Q: What are design tokens and why use them?

A: Platform-agnostic, named values for the atomic design decisions (color, spacing, typography, radius, shadow, motion). Instead of hard-coding #2563eb in 50 places, you reference color.brand.primary. Benefits: one source of truth, consistent design, themeable, and design↔code stay in sync (designers change the token, code picks it up). They scale a design system across products and platforms.

Q: What are the token tiers?

A:

Tier Example Purpose
Primitive / global blue.500 = #2563eb, space.4 = 16px the raw palette/scale; no meaning
Semantic / alias color.text.danger → red.600 intent; what it’s for
Component button.primary.bg → color.action per-component, optional

Components reference semantic tokens, semantic reference primitives. That indirection is what lets you re-theme: dark mode remaps semantic→primitive without touching components.

Q: How do CSS custom properties power theming?

A: Define tokens as CSS variables, theme by overriding them on a scope:

:root { --color-bg: #fff; --color-text: #111; }
[data-theme="dark"] { --color-bg: #0b0b0b; --color-text: #f5f5f5; }

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

Flip data-theme on <html> and the whole tree re-themes — no re-render, no prop drilling, cascades to portals. For OS sync, default from @media (prefers-color-scheme: dark) and let an explicit toggle override. See ../02_css/.

Q: What is Style Dictionary’s role?

A: It’s a build tool that transforms one token source (JSON/YAML) into many platform outputs: CSS variables, SCSS, JS/TS constants, iOS (Swift), Android (XML). You author tokens once; it generates per-platform artifacts with transforms (e.g., px→pt, hex→rgba). This is how a token change propagates everywhere from a single edit, and how design and native apps stay consistent.

Q: How do you do multi-tenant / white-label theming?

A: Keep components referencing semantic tokens; per tenant, provide a different set of token values (a theme bundle) injected as CSS variables at runtime (from tenant config) or built per tenant. Because components never hard-code values, a new tenant is “a new token file,” not a code change. Runtime injection (set CSS vars from an API response on load) supports many tenants from one build.

Q: Runtime vs build-time theming — trade-offs?

A:

  • Build-time (compile a theme into CSS) — zero runtime cost, but a new theme needs a build/deploy; bad for many tenants.
  • Runtime (CSS variables swapped live) — instant theme switching and unlimited tenants from one bundle; tiny runtime cost, and you must avoid FOUC by setting the theme before first paint (inline script in <head>). CSS variables make runtime theming cheap, so it’s the common choice.

Gotchas / edge cases

  • Skipping the semantic tier — components referencing primitives (blue.500) directly can’t be re-themed; you lose the whole point. Always go through semantic tokens.
  • FOUC / theme flash — reading theme from localStorage in React after hydration flashes the wrong theme; set data-theme via a blocking inline script in <head> before paint (../19_rendering_modes/).
  • Tokens in JS vs CSS — JS token objects can’t be overridden by the cascade and bloat the bundle; prefer CSS variables for themeable values, JS tokens only where you must compute.
  • Naming churn — renaming tokens breaks consumers; treat token names as an API (deprecate, don’t silently rename).
  • Contrast across themes — a token palette that passes contrast in light mode may fail in dark mode; verify both (../16_accessibility/01_wcag_and_pour.md).
  • DTCG format is still stabilizing — the W3C Design Tokens spec is in progress; tooling support varies.

What a senior is expected to say

  • “Tokens are named design decisions in three tiers — primitive, semantic, component. Components reference semantic tokens so re-theming remaps values without touching components.”
  • “CSS custom properties make theming a value swap on a scope — dark mode and multi-tenant for free, cascading to portals, no re-render.”
  • “Style Dictionary turns one token source into CSS/JS/iOS/Android outputs; a token edit propagates everywhere.”
  • “Runtime theming via CSS vars supports many tenants from one build — just guard against the theme-flash with a pre-paint script.”

Cross-references

Further reading