Design System Architecture: Tokens, Components, and Versioning

“Walk me through how you would design a design system from scratch” is a standard frontend system design question for staff+ roles. The interviewer wants to see structural thinking, awareness of the tradeoffs (build vs buy, pace of change vs stability, theme flexibility vs design consistency), and an understanding of the rollout politics that kill most internal design systems.

Layers of a design system

  1. Tokens: raw design values (color, spacing, typography)
  2. Primitives: low-level building blocks (Box, Stack, Text)
  3. Components: opinionated UI (Button, Input, Modal)
  4. Patterns: compositions of components (form layouts, dashboards)
  5. Templates: full page layouts

Each layer has different change velocity. Tokens are stable; templates change frequently.

Tokens

Tokens are key-value pairs of design decisions. Examples:

  • color.primary.500: #2563eb
  • spacing.4: 16px
  • fontSize.base: 16px
  • radius.md: 8px

Tokens have aliases for semantic use:

  • color.text.primary → color.gray.900
  • color.background.surface → color.white

Aliases enable theming. Switch dark mode by remapping aliases to different raw values.

Token format: Style Dictionary or W3C tokens

Industry standard: store tokens in JSON; transform to platform-specific outputs (CSS variables, Sass variables, JS objects, iOS Swift, Android Kotlin) via a build pipeline. Style Dictionary (Amazon) is the most-used tool. The W3C is standardizing a token JSON format.

Component layer

Each component should:

  • Have a clear, narrow API
  • Be composable (children, render props, slots)
  • Handle accessibility by default (aria-* attributes, focus management)
  • Use tokens, not hard-coded values
  • Have a Storybook story
  • Have an automated visual regression test

Build vs buy

Tradeoffs:

  • Build from scratch: total control, but huge upfront cost. Only justified at FAANG scale or for very distinctive brands.
  • Build on top of headless library (Radix, React Aria, Headless UI): get accessibility for free, style with your own tokens. Most pragmatic choice.
  • Buy/use a complete UI library (MUI, Chakra, Mantine): fastest, but constraints on design and risk of vendor lock-in.

Theming

CSS variables are the simplest theming mechanism. Define theme as a set of CSS custom properties at the root; change them at the body or theme-provider level for dark mode, brand variants, etc.

Avoid: theme objects passed via JS context that components consume at runtime — this scales poorly and increases bundle size.

Versioning

Use semver discipline:

  • Major: breaking API changes
  • Minor: new components, additions to existing APIs
  • Patch: bug fixes, accessibility improvements

Migration guides for every breaking change. Codemods for large breaking changes.

Rollout strategy

Most design systems fail not from poor code but from poor rollout. Strategies:

  • Build for the loudest, most influential team first — let them champion it
  • Don’t mandate adoption; make adoption better than not adopting
  • Provide migration paths from existing component libraries
  • Track adoption metrics: % of new code using the design system, % of legacy code migrated

Documentation

  • Storybook for component playground
  • Written docs for principles and patterns
  • Code examples for every common case
  • Migration guides

Common antipatterns

  • Building 100 components before anyone uses 10
  • Hard-coding colors and spacing in components
  • No semver discipline — random breaking changes
  • No automated tests — visual regressions slip through
  • Refusing customization — design system becomes a constraint, not an enabler

Frequently Asked Questions

Should I make my design system open source?

Only if you have the team to maintain it. Open source is good marketing but a significant maintenance commitment. Start internal, open-source later if it makes sense.

How do I handle product team’s edge cases that the design system does not support?

Treat them as feature requests. Triage. If a need is recurring, add it to the system. If it is one-off, the team can extend locally.

How do I migrate a large legacy codebase?

Codemods for mechanical translations. Targeted refactors for design changes. Track progress with a migration dashboard. Set deadlines for sunset of legacy components.

Scroll to Top