My First Design System: What I Would Do Differently
The first design system I built properly — tokens, components, documentation — had every layer wrong. Not the components themselves. The foundations under them.
What I built
A fintech client needed consistency across their marketing site, their dashboard, and their mobile-web account management views. Three surfaces, one team, no shared design language. I proposed a design system and spent six weeks building one.
I started with components. Button, Input, Select, Modal, Table — the obvious things. I made them good. Typed with TypeScript, tested, responsive, accessible attributes in the right places.
Then I realized the components had hardcoded values: #2563EB for primary blue, 16px for body text, 8px for base spacing. I went back and extracted those into CSS variables.
The structure I ended up with: components referenced CSS variables, CSS variables were defined in a global stylesheet. Done.
What was wrong
The token layer was too thin. I had one level: raw values. --color-primary: #2563EB. When the client wanted to update their brand to a slightly different blue — a common request, six months after launch — I updated one variable and everything that referenced it changed. Great.
Except some things shouldn't have changed. The primary button should use the brand blue. The link in a destructive error message should not. Both were referencing --color-primary because I hadn't defined the semantic layer.
The right structure is two levels:
- Primitive tokens: raw values.
--blue-600: #2563EB. - Semantic tokens: meaning.
--color-interactive: var(--blue-600).--color-danger: var(--red-600).
Components reference semantic tokens only. Never primitives directly. When the brand color changes, you update one primitive. When an error message needs a different color, you change the semantic token for that context. The two concerns don't collide.
The spacing system wasn't systematic. I had spacing variables but they were arbitrary: --space-sm: 8px, --space-md: 16px, --space-lg: 24px. No mathematical relationship. When a designer asked for something "between sm and md," there was no answer in the system — they'd pick 12px and hardcode it.
A proper spacing scale has a mathematical base — 4px or 8px — and generates steps multiplicatively. --space-1: 4px, --space-2: 8px, --space-3: 12px, --space-4: 16px. Any spacing decision in the system is a multiple of the base. Designers have a vocabulary. Developers have a scale. Custom values become auditable because they break the pattern.
Documentation was an afterthought. I documented components after I built them. The documentation was accurate but thin — it showed the component, listed the props, included a usage example.
What it didn't include: when to use this component versus an alternative. What the component intentionally does not do. Known edge cases and how they're handled.
That information lives in the builder's head at the time of construction. Six months later it doesn't. Write the constraints into the documentation while you still know them.
What I'd do first
If I were starting again, I'd spend the first week on tokens only. Define the primitive scale — colors, spacing, type sizes, shadows, radii. Define the semantic layer on top of it. Write down the decisions and why.
No components until the foundation is right. Components built on a weak foundation inherit the weakness. Components built on a strong foundation are just assembling what the tokens already decided.