Token-first, from the ground up.

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.

Design system token architecture

I started with the token file, not the components.

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.

Tier 01 · Global

Primitive values

tokens/global.json

Never referenced directly in components
color.neutral-900#1a1a1b
space.524px
font-weight.bold700
Tier 02 · Semantic

Component decisions

tokens/brands/portfolio/tokens.json

The only tier components are allowed to reference
color-text-primary{color.neutral-900}
color-accent-default{color.teal-500}
space-component-gap{space.5}
Tier 03 · Brand

Brand overrides

tokens/brands/[brand]/tokens.json

Only the tokens that diverge from the default theme
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.

Three tiers, and a grammar to hold them.

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.

Structure from BEM. Behaviour from Radix. Everything visible: mine.

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.

Design SystemsToken ArchitectureThemingReactStyle DictionaryStorybookClaude Code
Layer 01 · Delivery

Next.js App Router

Framework — pages, routing, server and client components

Layer 02 · Styling

BEM + CSS Custom Properties

Component structure and visual values — driven entirely by tokens

Layer 03 · Tokens

Style Dictionary

Compiles token JSON to CSS — single build output: styles/brands/portfolio.css

Layer 04 · Components

React + Radix UI

Stateless presentational components — Radix only for accessible primitives

Layer 05 · Review

Storybook + Chromatic

Component review across both brand contexts — visual regression per commit

One stack. One source of truth. One codebase.

Where design met code

One name, three places.

Design

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.

Code

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.

Figma variablebutton-primary-background = #292524
Style Dictionarybutton-primary-background: #292524
Compiled CSS--button-primary-background: #292524
Design

Figma

Component designed with variants, tokens, and correct naming convention

Bridge

Console MCP

Exposes Figma structure and token data directly to Claude Code — no screenshots

Generate

Claude Code

Reads CLAUDE.md and token files, generates BEM component and Storybook story

Review

Storybook

Review props, variants, and states across both brand contexts via brand switcher

Approve

Chromatic

Visual regression sign-off per commit — snapshots compared per brand automatically

Ship

GitHub

Commit reviewed component — production-ready, token-compliant, system-aligned

Design to code. No manual translation.

One source. Four themes. Zero drift.

Token coverage

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.

Theme switching

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.

Design-to-code pipeline

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.

A stable language first. Then agents building on top of it.

This is the version that runs now.

Final angle: orchestration on top of a stable language

Three weeks spent not on components. On names.

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

H1Display
H3Sub-heading
LeadLead paragraph
BodyBody text
Labellabel

Color

Surface
primary
secondary
tertiary
inverse
Text
primary
secondary
inverse
Accent
default
hover
Border
default
strong

Spacing

tight-gap
inline-gap
label-gap
element-gap
component-gap