Primary action
Cards pull their background from color-surface-elevated.
This is my playbook, shown working: how I structure tokens, components, and Figma files from day zero (Setup), automate the path to production (Automate), and run the system as a product long-term (Maintain). Everything below is interactive — a token you edit in Act 01 ripples through every act, exactly like it would in the real pipeline.
DESIGN SYSTEM
ARCHITECTURE
Figma Variables and Modes are the single origin for every design decision — color, space, radius, type.
Tokens Studio exports to W3C DTCG JSON. Style Dictionary transforms and distributes to every platform.
One pipeline. Four outputs: CSS custom properties, Tailwind config, iOS Swift, Android Compose.
Web, marketing, mobile, and internal tools consume one versioned package. Zero hard-coded values anywhere.
I audited 6 repositories and 47 components before touching anything. Without a shared system, every team had made local decisions — the same "primary blue" existed in 14 different hex values.
/* Button.css — Team A */ background: #2563EB; border-radius: 6px; /* Card.css — Team B */ border-color: #3B82F6; /* different! */ border-radius: 8px; /* different! */ /* Badge.tsx — Marketing */ background: "#1D4ED8" /* different! */
/* tokens.css — generated */ --color-action-primary: #3B82F6; --radius-control: 8px; /* Button.css */ background: var(--color-action-primary); border-radius: var(--radius-control); /* Card.css — same token */ border-color: var(--color-action-primary);
The first thing I set up: Primitives → Semantics → Components. This cascade is what makes everything else possible. Edit a primitive; the graph ripples.
brand-500#3B82F6accent-500#F59E0Bneutral-900#0B1020neutral-100#F4F6FAspace-416pxradius-md8pxcolor-surface-primary→ neutral100color-surface-elevated→ neutral100color-text-primary→ neutral900color-action-primary→ brand500color-action-accent→ accent500spacing-inline-sm→ space4radius-control→ radiusMdbutton-background→ color-action-primarybutton-text→ color-surface-primarycard-background→ color-surface-elevatedcard-border→ color-action-primaryinput-border→ color-text-primarybadge-background→ color-action-accentbrand-500#3B82F6color-action-primarybutton-backgroundTry it Change the color swatch on brand-500 — downstream semantics and components re-render instantly across all four acts.
I organize the component library on Brad Frost’s five levels, extended with a subatomic token layer. Each level maps 1:1 to a Figma library location and a code package — so designers and engineers on my teams navigate the same hierarchy.
Not in Brad Frost’s original model — modern systems treat tokens as the particle layer every atom is built from.
brand-500color-action-primaryradius-controlpackages/tokensSmallest usable UI. Every visual property is bound to a semantic token — never a raw value.
ButtonInputBadgeIconLabelpackages/react/src/atomsAtoms composed into one job. Molecules introduce layout tokens (spacing, stack gaps) but no new colors.
Form rowSearch barCard headerNav itempackages/react/src/moleculesStandalone sections of an interface. Organisms own responsive behaviour and content slots.
HeaderCard gridCheckout formData tablepackages/react/src/organismsOrganisms arranged into a page-level structure with placeholder content. This is where modes are QA’d.
Dashboard shellArticle layoutEmpty stateapps/*/src/layoutsTemplates filled with production content. Pages live in product repos and consume the system — they never define it.
praveenmanchi.art/homeCheckout /paySettingsapps/web · apps/mobilecomponents/ + patterns/ layout while still enforcing these composition rules.Atomic design predates design tokens — Frost’s 2013 model starts at atoms. Modern component-driven teams add the token layer underneath: change a particle (brand-500) and every atom, molecule, and organism repaints without a single component edit. That’s the property the three-tier cascade in Act 01 guarantees, and it is exactly how the mode switch in the Modes act repaints this entire page.
Every component I build renders from the token graph — no exceptions. Click any component to trace its variable chain.
Cards pull their background from color-surface-elevated.
I structure collections so a brand is just a mode. Same component tree · different soul — switching rebinds primitives only, logic is untouched.
The component logic never changed. Only the primitive → semantic bindings did.
Surface & text derived from neutral-900.
NEWSurface & text derived from neutral-900.
NEWSurface & text derived from neutral-900.
NEWSurface & text derived from neutral-900.
NEWbrand-a · brand-b · dark · hc. Each mode rebinds aliases to different primitives.| Figma plan | Modes / collection |
|---|---|
| Starter | 1 |
| Professional | 10 |
| Organization | 20 |
| Enterprise | 40 |
For brand portfolios beyond mode counts, Figma’s extended collections(Enterprise) let a child brand collection inherit a published parent and override only brand-specific values — updates flow down automatically.
I automated the path from a Figma variable edit to a versioned npm release — no manual handoff anywhere. Click trigger sync to replay — logs stream live.
How a single Figma variable change propagates to every platform in under 5 minutes.
.tokens.json extensions), while Terrazzo (formerly Cobalt UI) provides a streamlined DTCG-native alternative.[category]-[concept]-[property]-[state] color-action-primary-hover color-text-on-brand spacing-inline-sm radius-control
Purpose-driven paths (color-bg-surface-hover) have replaced the older Category/Type/Item taxonomy — the token name should say what it’s for, never what it looks like.
I use Storybook as the airlock between design and production: every component I ship passes through story → test → visual review before any app can consume it.
Variants + properties published to the Library. Component bound to semantic variables.
One story per state. Controls mirror Figma variant properties 1:1. Figma frame embedded via the Designs addon.
axe-core a11y checks, play-function interaction tests, and Chromatic snapshots across all brand modes.
Chromatic diffs reviewed by design + eng. Visual change must be approved in every mode before merge.
Semver npm release of the component package. Storybook deployed as living documentation.
Product teams upgrade the package. The story is the contract — what shipped is what was reviewed.
Edit brand-500 or the radius back in Act 01 and this “new snapshot” drifts from its frozen baseline — exactly the diff a reviewer would be asked to approve or deny before merge.
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '@org/react';
const meta: Meta<typeof Button> = {
component: Button,
parameters: {
design: { // Figma embed
type: 'figma',
url: 'figma.com/file/…?node-id=42',
},
},
};
export default meta;
export const Primary: StoryObj<typeof Button> = {
args: { variant: 'primary', children: 'Continue' },
play: async ({ canvas, userEvent }) => {
await userEvent.click(canvas.getByRole('button'));
},
};Controls mirror the Figma variant properties, the design parameter embeds the source frame, and the play function turns the story into an interaction test that runs in CI.
Since Storybook 9, interaction, a11y, and visual checks run as one Vitest-powered test suite; Chromatic’s TurboSnap only re-snapshots stories a change actually affects, and its UI Review step is the designer sign-off gate before merge.
The four layers I designed to move value from design intent to shipped pixels. Each layer has exactly one responsibility.
Figma Variables and Modes are the single origin for every design decision — color, space, radius, type.
Tokens Studio exports to W3C DTCG JSON. Style Dictionary transforms and distributes to every platform.
One pipeline. Four outputs: CSS custom properties, Tailwind config, iOS Swift, Android Compose.
Web, marketing, mobile, and internal tools consume one versioned package. Zero hard-coded values anywhere.
How I organize files and run every design change day-to-day: a predictable layout + Figma Branches = auditable changes with the same rigour as code.
📁 DS · Foundations (Published Library) ├─ 🎨 Variables │ ├─ Primitives · brand-500, neutral-100, space-4… │ ├─ Semantics · color-surface-primary, spacing-inline-sm │ └─ Component · button-background, card-border ├─ 🔤 Type styles └─ 🌓 Modes · Brand A · Brand B · Dark · HC 📁 DS · Components (Published Library) ├─ 🧩 Base · Button, Input, Badge, Chip ├─ 🧩 Patterns · Card, Alert, Form row, Nav item └─ 🧩 Templates · Page shells, empty states 📁 DS · Icons (Published Library) └─ 🔣 Icon set (24px base) 📁 DS · Docs └─ 📝 Usage, do / don't, accessibility notes
| Component property | Bound variable |
|---|---|
| Button · Fill | color-action-primary |
| Button · Label color | color-on-action-primary |
| Button · Corner radius | radius-control |
| Card · Background | color-surface-elevated |
| Card · Padding | spacing-stack-lg |
| Input · Border | color-border-input |
Rule of thumb: components bind to semantics only. Semantics bind to primitives. No component should reference a raw primitive — that's how you keep modes repaintable.
📁 design-system/ (monorepo) ├─ tokens/ · source of truth in code │ ├─ primitives/ · color.json · space.json · type.json │ ├─ semantic/ · brand-a.json · brand-b.json · dark.json · hc.json │ └─ component/ · button.json · card.json · input.json ├─ packages/ │ ├─ tokens/ · built output — CSS vars, TS, Swift, Compose │ ├─ react/ │ │ └─ src/ │ │ ├─ atoms/ · Button, Input, Badge, Icon │ │ ├─ molecules/ · FormRow, SearchBar, NavItem │ │ └─ organisms/ · Header, CardGrid, DataTable │ └─ icons/ · generated from Figma export ├─ apps/ │ └─ storybook/ · stories, a11y + interaction tests ├─ style-dictionary.config.js · transform pipeline config └─ .github/workflows/tokens.yml · CI — build, test, publish
The repo mirrors the Figma structure act-for-act: tokens/ matches the three variable collections, packages/react matches the atomic component ladder, and Storybook sits beside them as the review surface. One mental model, two tools.
I run the design system as a product — with owners, a contribution path, and a public changelog. This is the part that keeps it alive after launch.
| Role | Owns |
|---|---|
| DS Core | Token architecture, library publishing, releases |
| Federated contributors | Patterns from product teams · PRs into DS |
| Design reviewers | Approve visual changes · a11y · cross-brand |
| Engineering reviewers | Approve tokens diff · breaking change flag |
#3B82F6 to #2D6AE0.brand/primary-refresh in Figma.brand-500.@org/design-tokens.I treat WCAG 2.2 AA as a token constraint, not an afterthought — contrast is enforced at the primitive level and re-checked in CI, never patched in components.
| Mode | Text/Surface | Ratio | WCAG AA |
|---|---|---|---|
| Brand A | neutral-900 / neutral-100 | 15.3:1 | PASS |
| Brand B | neutral-900 / neutral-100 | 14.8:1 | PASS |
| Dark | neutral-900 (light) / neutral-100 (dark) | 13.1:1 | PASS |
| High Contrast | #FFF / #000 | 21:1 | PASS |
color-focus-ring maps to a 2px solid brand-500 line with 2px offset, satisfying the WCAG 2.2 focus appearance criteria.:focus-visible via focus-ring token. Tab order strictly follows the semantic DOM tree.outline: none without providing a token-bound focus indicator.The system pays for itself on the first rebrand — but the real product is the operating rhythm that keeps it healthy after launch.
Naming workshops in week 1 saved months of renames. Teams argue about hex values; they align on purpose.
Teams didn’t migrate because of a mandate — they migrated because the pipeline made tokens easier than hard-coding.
Contribution rules and quarterly audits kept the system from becoming a component dumping ground.
Versioned releases, a changelog, and migration guides earned engineering trust faster than any design review.
Everything on this page is how I actually work: audit first, architect the tokens, automate the pipeline, then run the system as a product. If your team is starting a design system — or rescuing one — I’d love to talk.
Get in touch →