Design Systems at Scale: Lessons from Building Midwest
Why design systems fail
Most design systems start with the best intentions. A handful of components, a shared colour palette, a Figma file that everyone promises to keep updated. Then product pressure hits. Components fork. The Figma file drifts. Engineers start copy-pasting instead of composing.
We went through exactly this cycle before building Midwest — and the experience taught us a handful of patterns that make component libraries actually stick.
Constraint is a feature
Every option you expose in a component API is a decision you're pushing to every consumer. A button with twenty-three props isn't flexible — it's undecided. The most durable systems have a small, opinionated surface area and a clear path for one-off extensions.
The best component is the one you don't have to think about.
Midwest keeps props minimal by leaning on semantic tokens (states, sizes, variants) rather than exposing raw CSS values. This means consumers stay in the design language rather than escaping it.
Co-locate styles with components
Separating component styles into a global stylesheet is a maintenance trap. When the stylesheet is far from the component, both drift. Midwest co-locates a .css file with every component, imported via a single Tailwind entry point. Deleting a component means deleting a folder — no orphan styles left behind.
Test the contract, not the implementation
Unit tests for UI components should verify what the component promises to render, not how it renders it internally. We test output HTML and ARIA attributes — things that consumers depend on — and leave CSS class composition to linting. This gives us confidence to refactor internals without breaking consumers.