A design system for a small team
Tokens, a dozen components, well-defined states and a few habits that keep design and code in sync, without a hundred pages of documentation.
Small teams tend to flinch at the words “design system”. It sounds like a documentation site, a dedicated maintainer and six months of work. What a small team actually needs is much smaller: a set of decisions made once, so nobody has to make them again on every screen.
The classic sign you don’t have one: seven shades of gray in the code, four corner radii and three different Cancel buttons. Nobody decided that. It happened because each screen was built on its own, often with an AI that doesn’t remember what it did yesterday.
Start with tokens
Tokens are named values: colors, spacing, radii, font sizes, shadows. Instead of #6B7280 the code says --text-muted; instead of 12px, --space-3.
This is the boring part, and it pays off the most. When a color is named for its role rather than its value, you can change it in one place. When there are five spacing values instead of forty, the interface starts looking tidier on its own.
What to define first:
- colors by role: background, surface, text, muted text, border, accent, error, success;
- a spacing scale of five to seven steps;
- two or three radii;
- a type scale of five or six sizes.
:root {
--bg: #ffffff;
--surface: #f6f6f7;
--text: #111113;
--text-muted: #6b6b73;
--border: rgb(0 0 0 / 0.08);
--accent: #111113;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--radius-sm: 4px;
--radius-md: 8px;
}
Once this exists, a dark theme or a new accent color costs almost nothing.
A dozen components, not a hundred
Don’t start with a catalog. Start with what repeats most: button, text input, select, checkbox, card, modal, toast, list or table, empty state. That covers most screens in a typical product.
Add the rest when something shows up for the third time. The first time, build it in place. The second time, notice it. The third time, extract it.
States matter more than variants
A component with five color variants and no loading state is a bad component. For every interactive element, decide what default, hover, keyboard focus, pressed, disabled, loading and error look like. For lists and tables: empty, loading, failed to load, and far too much data.
States are exactly what generated code tends to skip. In a demo, everything loads instantly and nothing ever fails.
Keeping design and code in sync
Drift between mockups and code starts with small things, and a couple of months later you have two different products. A few habits help:
- tokens live in code and code is the source of truth; mockups use the same names;
- components have a single page showing every state, even if it’s just a hidden route in the app;
- new screens are assembled from existing components, and any deviation gets discussed rather than quietly shipped;
- once a month, someone searches the codebase for hardcoded colors and spacing and removes them.
If you write code with AI, put the token file and the component list in its context and ask it to use only those. You’ll get noticeably fewer new grays.
What not to do
Don’t write documentation before anyone uses the components. Don’t build one component for every situation with twenty props; two simple ones are better. Don’t import someone else’s system wholesale when it was built for a company of a thousand people.
A small team’s design system should fit in your head. If someone has to open the docs to make a button, it’s already too big.
SUBXCAT builds systems like this out of what’s already in your product: extracting tokens, merging duplicate components and leaving one page where everything is visible.