Building a Design System from Scratch
Lessons learned from creating a component library that scales — from tokens to documentation.
Every growing product eventually faces the same question: how do we keep our UI consistent? For our team, the answer was building a design system from the ground up.
Why Not Use an Existing Library?
We evaluated several options — Radix, shadcn/ui, Chakra — and they're all excellent. But our product had specific interaction patterns that didn't map cleanly to any existing library. We needed full control over the component API and styling approach.
Starting with Tokens
The foundation of any design system is its design tokens: colors, spacing, typography, radii, shadows. We defined these as CSS custom properties:
:root {
--color-primary: #6366f1;
--color-surface: #ffffff;
--radius-md: 8px;
--space-4: 16px;
}This made theming straightforward — dark mode was just a matter of overriding the token values.
Component Architecture
Each component follows a consistent pattern:
interface ButtonProps {
variant: "primary" | "secondary" | "ghost";
size: "sm" | "md" | "lg";
children: React.ReactNode;
}
export function Button({ variant, size, children }: ButtonProps) {
return (
<button className={cn(baseStyles, variants[variant], sizes[size])}>
{children}
</button>
);
}We kept the API surface small and explicit. No sx prop, no style overrides — just well-defined variants.
Documentation
We used Storybook for interactive documentation, but the real win was writing usage guidelines alongside each component. When should you use a ghost button vs. secondary? That context matters more than the API reference.
Key Takeaways
- Start with tokens. They're the contract between design and engineering.
- Keep APIs small. Every prop is a maintenance commitment.
- Write guidelines, not just docs. Help people make decisions, not just use components.
- Ship incrementally. We released one component at a time, gathering feedback before expanding.
The system isn't "done" — and it never will be. But it's saved us hundreds of hours of inconsistency debates and duplicate code.