Token files drive everything — the CSS, the Figma variables, two independent brands. Three weeks lost to a naming mistake. One pipeline that made it worth it.
Approach
The problem was structural. Design files, token JSON and compiled CSS each held their own version of the truth, and every handoff between them was manual — a value tweaked in Figma that never reached the CSS, a hex edited in code that Figma never heard about. A token name spelled differently in two places is a hidden handoff step in disguise.
Most token-first systems start in Figma: set the variable, export it, hope the handoff holds. I inverted that. The JSON file is authoritative — Style Dictionary compiles it to CSS, and a separate step pushes the same names into Figma. Figma shows you whether the pipeline worked. If Figma and CSS disagree, CSS is right.
The constraints were the honest kind: one person, no inherited conventions, and one self-imposed rule — every visual decision traces back to a token, no exceptions. A rule like that is cheap to declare and expensive to keep. The cost landed later: three weeks spent not on components, but on names.
Primitive values
tokens/global.json
color.neutral-900#1a1a1bspace.524pxfont-weight.bold700Component decisions
tokens/brands/portfolio/tokens.json
color-text-primary{color.neutral-900}color-accent-default{color.teal-500}space-component-gap{space.5}Brand overrides
tokens/brands/[brand]/tokens.json
color-accent-default{brand-a.indigo-400}font-family-heading'DM Serif Display'color-surface-primary{brand-a.background-0}Primitive values never appear in component CSS — only semantic tokens do.
Design decisions
The token layer has three tiers. Global primitives hold raw values — a hex, a size, a duration. Semantic tokens hold decisions: color-text-secondary points at a primitive and says what it's for. Component tokens inherit those decisions. Component CSS never touches a primitive or a raw value — it reads the semantic layer, directly or through its own component tokens.
That one rule is what makes theming cheap. Dark mode and the bold expression override the middle tier — the semantic decisions — and everything downstream re-resolves on its own. Four theme combinations, and the components never knew anything changed.
Technical decisions
BEM with CSS custom properties: class names describe structure, tokens carry all visual values. Swap the brand class and every component re-resolves through a different token set — no logic, no JavaScript, no build change. Radix handles the one category I chose not to build: interactive components that need accessible behaviour by specification. Everything visible is mine.
Next.js App Router
Framework — pages, routing, server and client components
BEM + CSS Custom Properties
Component structure and visual values — driven entirely by tokens
Style Dictionary
Compiles token JSON to CSS — single build output: styles/brands/portfolio.css
React + Radix UI
Stateless presentational components — Radix only for accessible primitives
Storybook + Chromatic
Component review across both brand contexts — visual regression per commit
One stack. One source of truth. One codebase.
Where design met code
The Figma variable panel holds the same names as the Style Dictionary source. They were never synced — they were compiled from the same file. The pipeline never interprets. It copies.
The compiled CSS custom property carries the same name. Three sources, one namespace. When all three agree — as the block below shows — there is nothing left to translate. That is what the absence of a handoff step looks like.
button-primary-background = #292524button-primary-background: #292524--button-primary-background: #292524Figma
Component designed with variants, tokens, and correct naming convention
Console MCP
Exposes Figma structure and token data directly to Claude Code — no screenshots
Claude Code
Reads CLAUDE.md and token files, generates BEM component and Storybook story
Storybook
Review props, variants, and states across both brand contexts via brand switcher
Chromatic
Visual regression sign-off per commit — snapshots compared per brand automatically
GitHub
Commit reviewed component — production-ready, token-compliant, system-aligned
Design to code. No manual translation.
Outcome
630 tokens across three tiers — 100 global primitives, 109 semantic decisions, 421 component-level. No hardcoded values anywhere in component code. Every visual property resolves through the pipeline.
One identity, two axes. Light and dark mode cross with two brand expressions — four combinations compiled from three token files. Switching is two data attributes on the html element; no component logic, no duplicated components. Adding an expression is a token file.
Figma variables map 1:1 to Style Dictionary token names. The pipeline compiles — it doesn't interpret, negotiate, or drift. When Figma and CSS agree, the agreement is structural; there is nothing to maintain. A weekly check compares the two and files an issue the moment they disagree.
Where it went
This is the version that runs now.
Reflection
I added tokens as I needed them. That worked for the first few weeks. Then the system had to support three tiers simultaneously — global primitives, semantic decisions, brand overrides — and the names I'd used for the first tier made no sense for the second. color-gray-100 makes an appearance claim. neutral-100 describes a position. Nothing visual changed. The language did, completely. In a token-first system, naming is architecture — get it wrong and you don't fix the name, you refactor the system.
That stopped being theoretical. An agent now reviews every branch against the system's rules. A contrast check runs every token pair across all four theme combinations in CI. A weekly job compares Figma against the token source and files what drifted. Every one of them depends on stable, unambiguous language. The naming work wasn't just architecture. It was the condition for safe AI participation.
Before agents can coordinate a design system, the design system has to know what its own words mean.
Most portfolios build something, then document the system behind it. This one is the system. Every visual decision runs through the same token pipeline — the dark and light mode toggle in the navigation is that same system running live, documented step by step in the DS Playbook. The agents that help maintain it work inside the same guardrails: they propose, deterministic checks verify, I decide.
Open the inspector. It's all tokens.
The foundations, running
Explore the full design system →Typography
Color
Spacing