Building a Design System from Scratch: Tokens, Components, and the Process That Actually Works
Short Answer: A practitioner
Every design system guide begins at the wrong place. They start with "decide your color palette" or "build a button component." The result is a system that looks coherent in Storybook and falls apart when engineers try to implement it in a real product, or a Figma library that designers use inconsistently because the governance model was never established.
This guide starts at the actual beginning: with the fundamental decisions that determine whether a design system succeeds or becomes an expensive artifact that nobody touches after the launch blog post.
The Strategic Decisions That Happen Before Design
Decision 1: What problem are you actually solving?
Design systems fail for one of two reasons:
Failure mode A: The system was built for designers, not engineers. A Figma library with beautiful, well-organized components is not a design system — it's a design asset library. A design system is a set of shared language and reusable patterns that works for both the design process and the engineering implementation. If it doesn't have code, it's not a system.
Failure mode B: The system was built for engineers, not designers. A Storybook full of coded components that designers can't easily use in their design tool produces a gap between design and implementation. Designers stop treating the library as a source of truth and start doing custom work. The system fragments.
The problem a successful design system solves is: reducing the cognitive and production cost of making design decisions repeatedly, for both designers and engineers. Every component in the system should eliminate a category of decisions that were previously made ad-hoc.
Decision 2: What scope are you committing to?
The scope that looks fastest (just build the most-used components) is often the slowest over time because it leaves the token layer undefined. Define scope using three tiers:
Tier 1 — Tokens (mandatory, build first): Color, typography, spacing, radius, shadow, motion. These are primitive values that the entire system is built from. Without tokens, components can't be themed, updated globally, or understood systematically.
Tier 2 — Core components (committed): The 15–25 components that appear in 80–90% of product surfaces. Button, Input, Select, Checkbox, Radio, Modal, Toast, Tooltip, Accordion, Table, Card, Badge, Avatar, Loading states, Empty states.
Tier 3 — Specialist components (aspirational): Complex domain-specific components — data visualizations, rich text editors, calendar pickers. These often have non-trivial implementation complexity and are frequently better handled as separate packages.
Phase 1: Design Tokens — The Foundation
Design tokens are the single source of truth for the values that define your visual language. They are not "variables in Figma" or "CSS variables in your stylesheet" — they exist at a level above any specific tool and translate into whatever tool or format is needed.
The Token Architecture
Modern token architecture uses three layers, following the W3C Design Token Community Group standard:
Global Tokens (primitives)
↓
Alias Tokens (semantic)
↓
Component Tokens (specific)
Global tokens are raw values with no implied purpose:
{
"color": {
"blue-50": { "value": "#eff6ff" },
"blue-100": { "value": "#dbeafe" },
"blue-200": { "value": "#bfdbfe" },
"blue-300": { "value": "#93c5fd" },
"blue-400": { "value": "#60a5fa" },
"blue-500": { "value": "#3b82f6" },
"blue-600": { "value": "#2563eb" },
"blue-700": { "value": "#1d4ed8" },
"blue-800": { "value": "#1e40af" },
"blue-900": { "value": "#1e3a8a" }
}
}
Alias tokens assign semantic meaning to global tokens:
{
"color": {
"action": {
"primary": { "value": "{color.blue-600}" },
"primary-hover": { "value": "{color.blue-700}" },
"primary-active": { "value": "{color.blue-800}" },
"primary-subtle": { "value": "{color.blue-50}" }
},
"surface": {
"default": { "value": "{color.neutral-50}" },
"raised": { "value": "#ffffff" },
"sunken": { "value": "{color.neutral-100}" },
"overlay": { "value": "#ffffff" }
},
"border": {
"default": { "value": "{color.neutral-200}" },
"strong": { "value": "{color.neutral-400}" },
"focus": { "value": "{color.blue-500}" }
},
"text": {
"primary": { "value": "{color.neutral-900}" },
"secondary": { "value": "{color.neutral-600}" },
"tertiary": { "value": "{color.neutral-400}" },
"on-action": { "value": "#ffffff" },
"link": { "value": "{color.blue-600}" }
},
"feedback": {
"success": { "value": "{color.green-600}" },
"warning": { "value": "{color.amber-600}" },
"error": { "value": "{color.red-600}" },
"info": { "value": "{color.blue-600}" }
}
}
}
Component tokens allow per-component overrides without breaking global semantics:
{
"button": {
"primary": {
"background": { "value": "{color.action.primary}" },
"background-hover": { "value": "{color.action.primary-hover}" },
"text": { "value": "{color.text.on-action}" },
"border-radius": { "value": "{radius.md}" },
"padding-x": { "value": "{space.4}" },
"padding-y": { "value": "{space.2}" }
}
}
}
Token File Structure and Tooling
The token format above follows the emerging W3C standard, which is also the format used by Style Dictionary — the most mature build tool for design tokens.
tokens/
├── global/
│ ├── color.json
│ ├── type.json
│ ├── space.json
│ ├── radius.json
│ ├── shadow.json
│ └── motion.json
├── alias/
│ ├── color.json
│ ├── type.json
│ └── space.json
├── component/
│ ├── button.json
│ ├── input.json
│ └── card.json
└── config.json ← Style Dictionary config
Style Dictionary compiles the token JSON into outputs for every platform:
// style-dictionary.config.js
export default {
source: ["tokens/**/*.json"],
platforms: {
css: {
transformGroup: "css",
prefix: "ds",
buildPath: "dist/css/",
files: [{ destination: "tokens.css", format: "css/variables" }]
},
figma: {
// Uses style-dictionary-to-figma-tokens transformer
buildPath: "dist/figma/",
files: [{ destination: "tokens.json", format: "json/nested" }]
},
ios: {
transformGroup: "ios-swift",
buildPath: "dist/ios/",
files: [{ destination: "Tokens.swift", format: "ios-swift/class.swift" }]
}
}
};
This produces CSS custom properties, Figma-importable JSON, iOS Swift constants, and Android XML from the same source.
Phase 2: Component Architecture
Components should be built after tokens are stable, not at the same time. The most expensive design system mistake is building components before the token layer is complete, then having to rewrite components when tokens change.
The Compound Component Pattern
Components should be structured as compound components: a parent container with explicitly named child slots. This provides maximum flexibility without requiring component variants for every edge case.
// Instead of this — requires new variant for every combination:
<Card
title="Card title"
subtitle="Subtitle"
image={src}
imagePosition="top"
actions={<Button>Action</Button>}
/>
// Use this — consumer controls the composition:
<Card>
<Card.Image src={src} alt="..." />
<Card.Body>
<Card.Title>Card title</Card.Title>
<Card.Subtitle>Subtitle</Card.Subtitle>
</Card.Body>
<Card.Footer>
<Button variant="primary">Action</Button>
</Card.Footer>
</Card>
The compound pattern means the component doesn't need a footerActions prop, an imagePosition prop, or a showSubtitle prop — it just maps to how the consumer wants to compose it. The surface area (and documentation burden) stays small.
The Component API Design Checklist
Before building any component, define:
-
The rendered element: What HTML tag does the root element become? (Button →
<button>, Link →<a>, etc.) This affects accessibility. -
The visual variants: Semantically named (primary, secondary, destructive), not stylistically named (blue, outlined, red).
-
The size scale: How many sizes does this component need? Usually 3 is enough (sm, md, lg). Design the rationale — sm and lg should follow your spacing scale proportions.
-
The state model: What states does the component have? Default, hover, focus, active, disabled, loading, error. Every state needs a visual treatment.
-
The accessibility contract: aria-label requirements, role, keyboard interaction, focus management. Define this before writing any CSS.
Phase 3: Governance — How the System Stays Alive
The most common design system failure is not technical — it's organizational. Systems that launch with fanfare are abandoned when:
- There's no clear process for proposing and reviewing new components
- Engineers fork components instead of contributing back
- The system version falls behind the product and becomes "legacy"
The Contribution Model
Define two types of system updates:
Tokens/global changes: Require system team review. Color, spacing, and type changes cascade across all components. These changes should be infrequent and deliberate.
New components: Follow a three-stage process: (1) Proposal — document the use case, show three instances in the existing product, get approval; (2) Design — create the Figma specs, document the API, write accessibility requirements; (3) Engineering — implement, document, write tests, add to Storybook.
Component updates: Minor changes (new size variant, new state treatment) can be submitted as pull requests by any team. Major changes (API changes, behavioral changes) require system team review.
Versioning and the Breaking Change Policy
Use semantic versioning for the design system package:
- Patch (1.0.x): Bug fixes, visual corrections that don't change component APIs
- Minor (1.x.0): New components, new optional props, new token additions
- Major (x.0.0): Breaking API changes, token renames, removed components
A clear deprecation policy is required for major versions. Components should be marked @deprecated in code and in documentation, with a migration guide, before they're removed. Minimum deprecation window: 2 release cycles (typically 2 months).
The Design System Launch Checklist
- Token layer complete: color, type, spacing, radius, shadow, motion all defined
- Token build pipeline outputs CSS, Figma JSON, and target platform formats
- All alias tokens follow semantic naming (not visual/physical naming)
- Core 15–20 components built with compound component pattern
- Every component has: all 5 states, accessibility spec, Storybook story, usage docs
- Figma library published and linked (not linked to individual files — linked to the library file)
- Contribution process documented: proposal → design review → engineering → merge
- Version number established, changelog format defined
- Governance owner(s) assigned (not just "the design team")
- Dark mode token set exists (even if not shipped — it signals the system is theme-capable)
About the Editorial Team This analysis was conducted by our independent research desk. We utilize verified market data and specialized methodology to provide objective, expert insights. Our strict editorial policy ensures no undue influence from sponsors or external parties.