The Hasan Dispatch logo

The Hasan Dispatch

Archives
Log in
Subscribe
March 10, 2021

Building a Design System from Scratch

A component library for a growing team — 30 components, full accessibility, Storybook documentation, and the lessons from maintaining it for two years.

#Design System #React #Accessibility

The Problem

The team had grown to ten developers working on the same frontend codebase, and the inconsistency was becoming a real problem. Every developer built buttons, forms, and modals slightly differently — different colors, different spacing, different behavior. The visual inconsistency was the visible symptom, but the deeper problem was the maintenance burden: when the design language changed (a new primary color, a revised spacing scale), updating every component across the codebase took days because every developer had implemented things differently. We needed a shared component library that encapsulated the design language and gave every developer the same building blocks.

The decision to build our own design system rather than adopt an existing one (Material UI, Ant Design) was driven by two factors. First, the design language was custom and didn't map cleanly onto any existing system — the spacing scale, the color palette, and the typography were all specific to the brand. Second, we wanted full control over the component behavior, particularly around accessibility and keyboard navigation, which existing systems often handle in ways that don't match the application's needs. Building our own was more upfront work, but it gave us a system that fit the application exactly rather than a system we had to work around.

What We Built

The design system grew to 30 components over six months: form inputs (text, select, checkbox, radio, date picker), layout primitives (stack, grid, container), feedback components (toast, modal, alert), and data display components (table, card, badge). Every component was built with accessibility as a first-class requirement — keyboard navigation, ARIA attributes, focus management — rather than as a retrofit. That emphasis on accessibility from the start was much cheaper than adding it later, because retrofitting accessibility onto components that weren't designed for it requires rethinking the component's structure, not just adding attributes. Building accessible from day one meant the components worked for all users from the first release.

Storybook served as the documentation and testing ground. Every component had a Story that showed its various states — default, hover, focus, disabled, error — and the Storybook served as both a reference for developers (how do I use this component?) and a visual regression test (did this change break any component's appearance?). The visual regression testing caught changes that would have otherwise slipped through — a CSS change that inadvertently shifted a button's padding, a refactoring that changed a modal's focus behavior — and the documentation reduced the number of questions the design system maintainers fielded from the rest of the team.

Lessons from Two Years of Maintenance

Two years later, the design system is still in use and has grown to 40 components. The biggest lesson from maintaining it is that the API surface matters more than the implementation. A component with a clean, predictable API — props that follow consistent naming, sensible defaults, and composition patterns that match how developers think — gets used correctly. A component with an awkward API — props that conflict, defaults that surprise, composition patterns that fight the consumer — gets misused, and the misuse becomes the de facto standard because it's what everyone copies from existing code. Spending time on API design before implementation is the highest-leverage investment in a component library, because the API is much harder to change than the implementation.

The other lesson is about versioning and communication. When the design system changes — a new prop, a changed default, a removed component — the consumers need to know, and they need to know before they upgrade. Semantic versioning, a changelog written for humans (not just for the git log), and migration guides for breaking changes are the minimum communication infrastructure for a design system that more than a handful of developers depend on. Skipping any of those creates a communication gap that manifests as surprise breakages, frustrated developers, and eventually a design system that nobody trusts enough to upgrade.

Don't miss what's next. Subscribe to The Hasan Dispatch:
Share this email:
Share on LinkedIn
GitHub
🙃
LinkedIn
Powered by Buttondown, the easiest way to start and grow your newsletter.