- Design Systems
- Frontend
Creating Design Systems That Scale
Most design systems don't fail because of bad components. They fail because nobody uses them. Here's how to build one people actually want to adopt.
• 4 min read •

Here’s an uncomfortable truth: a lot of design systems end up as beautifully documented libraries that teams quietly work around.
The components are fine. The Storybook is lovely. But product teams keep copy-pasting their own buttons because “the system one doesn’t quite do what we need,” and six months later you’ve got four slightly different modals in production.
Scaling a design system isn’t really about adding more components. It’s about making the system the easiest option for the people using it. Let’s talk about how.
Start with an audit, not a blank canvas
Before you design a single component, go screenshot every button in your product. Every input. Every modal. Put them on one big Figma page.
It’s usually a little horrifying. You’ll find 14 shades of grey and buttons with 5 different border radii. But this audit gives you two things:
- Evidence. It’s much easier to get buy-in when you can show the mess.
- Priorities. The components with the most variants are where the system will save the most time. Start there.
Get your tokens right early
Design tokens are the named values everything else is built on: colors, spacing, type sizes, radii, shadows. They’re the boring foundation, and they’re the hardest thing to change later. So it’s worth slowing down here.
The pattern that’s worked best for me is two layers:
/* Layer 1: primitives. The raw palette. */
:root {
--teal-500: #00a0a8;
--teal-600: #008a91;
--grey-900: #111827;
--grey-500: #6b7280;
--red-600: #dc2626;
}
/* Layer 2: semantic tokens. What the value is FOR. */
:root {
--color-action-primary: var(--teal-500);
--color-action-primary-hover: var(--teal-600);
--color-text-default: var(--grey-900);
--color-text-muted: var(--grey-500);
--color-feedback-danger: var(--red-600);
}
Components only ever use the semantic tokens. That indirection is what lets you do a rebrand, add dark mode, or support a white-label theme by changing one layer instead of hunting through every component.
If a component reaches straight for --teal-500, that’s a smell.
Design component APIs like products
The difference between a component people love and one they work around is usually its API, meaning the props.
A few principles I lean on:
Use variants, not one-off booleans.
// Gets messy fast
<Button primary large outline danger />
// Clear and constrained
<Button variant="danger" size="lg" />
Booleans multiply. Four booleans means 16 combinations, many of them nonsense. A variant prop with a fixed set of options keeps things sane.
Make the common thing easy and the rare thing possible. 90% of the time, someone just wants <Button>Save</Button>. Make that work with zero config. For the 10% case, allow escape hatches like passing a className or rendering as a link.
Lean on composition. Instead of a Card with 25 props (title, subtitle, image, footerButtons…), give people pieces they can arrange:
<Card>
<Card.Header>
<Card.Title>Monthly report</Card.Title>
</Card.Header>
<Card.Body>...</Card.Body>
<Card.Footer>
<Button>Download</Button>
</Card.Footer>
</Card>
It’s more flexible, and you don’t have to predict every layout in advance.
Bake accessibility in so teams don’t have to think about it
This is one of the best arguments for a design system. If your Modal handles focus trapping, closes on Escape and returns focus to the trigger, then every modal in the product is accessible by default. One team does the hard work once; everyone benefits.
Each component should ship with keyboard support, visible focus states, proper labelling and colors that pass contrast. Don’t leave this as an exercise for the consumer.
Docs are part of the product
If people can’t figure out how to use a component in 30 seconds, they’ll build their own. Good docs for each component include:
- A live example they can copy
- When to use it, and when not to (this is the part people skip, and it’s the most useful)
- The props, with sensible defaults listed
- Do’s and don’ts with visuals
Storybook is great for this. So is a simple docs site. The tool matters less than keeping it up to date.
Version it like you mean it
Once multiple teams depend on your system, every change is potentially a breaking change for someone. A few habits that save a lot of pain:
- Use semantic versioning. Patch for fixes, minor for new features, major for breaking changes.
- Write changelogs humans can read. “Button:
variant="ghost"added” beats “updated button component.” - Deprecate before you delete. Mark the old prop as deprecated, log a console warning in development, give teams a release or two to migrate. Then remove it.
Tools like Changesets make this a lot less painful in a monorepo.
Make it a shared thing, not a gatekeeper
The fastest way to kill a design system is for the system team to become a bottleneck. “Submit a ticket and we’ll get to it next quarter” sends people right back to building their own.
What works better:
- A clear contribution path. Product teams can propose and even build components, with the system team reviewing.
- Regular office hours. Half an hour a week where anyone can drop in with questions.
- Celebrate adoption. When a team migrates to the system, shout about it.
Measure whether it’s working
You can’t improve what you don’t track. Some simple signals:
- How many product screens use system components vs. custom ones?
- How often do teams override system styles?
- How long does it take a new developer to build a standard page?
Even rough numbers tell you where the friction is.
Wrapping up
A design system that scales is less like a library and more like a product with internal customers. Get the foundations (tokens) right, design APIs people enjoy using, bake in accessibility, document generously, and make it easy for everyone to contribute.
Do that, and teams won’t use the system because they’re told to. They’ll use it because it’s the fastest way to ship.