nuzairb@gmail.com— press E to copy
DESIGN ENGINEERING

My First Design System: What I Would Do Differently

BY3 MIN READ

The first design system I built properly — tokens, components, documentation — had every layer wrong. Not the components themselves. The foundations under them.

Key Takeaways

  • One token layer is not enough. Primitives hold raw values, semantics hold meaning, and components reference semantics only — otherwise a brand refresh drags your error states with it.
  • A spacing scale needs a mathematical base. Arbitrary sm/md/lg values give designers no answer when they want something in between, so they hardcode it.
  • Write the constraints down while you still know them. What a component deliberately does not do lives in the builder's head for about six months.

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:

  1. Primitive tokens: raw values. --blue-600: #2563EB.
  2. 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, the same way a type scale derives every size from one ratio. --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, and no library's components either. Components built on a weak foundation inherit the weakness. Components built on a strong foundation are just assembling what the tokens already decided.

WRITTEN BY

Design Engineer with 5+ years shipping production React and TypeScript interfaces across fintech, SaaS, and Web3. Writes about the seam between design and engineering.

Articles in the Same Series