Building a Design System from Scratch
Learn how to create a scalable design system that grows with your product. From tokens to components, I'll share my process and lessons learned.
Why Build a Design System?
Every product team eventually hits a wall. Designers duplicate components with slight variations. Engineers rebuild the same button three different ways. Brand consistency starts to erode. I have been through this cycle enough times to know that a design system is not a luxury — it is infrastructure.
When I set out to build a design system from scratch for a mid-size SaaS product, the goal was clear: create a shared language between design and engineering that scales with the product. Here is what I learned along the way.
Starting with Design Tokens
Design tokens are the foundation. Before touching a single component, I spent two weeks defining tokens — the smallest, most atomic decisions that everything else builds on.
Colors
I structured the color system in three layers:
- Primitives — raw color values like
blue-500: #3B82F6. These never get used directly in components. - Semantics — purpose-driven aliases like
color-primary,color-error,color-text-secondary. These are what components reference. - Component-level overrides — occasionally a component needs a one-off token, like
button-primary-hover-bg. I keep these scoped and minimal.
This layered approach made theming straightforward later on. Swapping from a light to dark theme meant remapping semantic tokens to different primitives — no component changes required.
Typography
I defined a type scale using a modular ratio of 1.25 (major third). This gave me predictable, harmonious sizes:
text-xs: 12pxtext-sm: 14pxtext-base: 16pxtext-lg: 20pxtext-xl: 24pxtext-2xl: 30pxtext-3xl: 36px
Each size is paired with a default line-height and letter-spacing value. I also defined font-weight tokens (regular, medium, semibold, bold) to keep weight usage consistent across the product.
Spacing
I went with a 4px base unit. Spacing tokens follow a simple scale: 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 80, 96. This covers everything from tight icon padding to large section margins without anyone having to invent arbitrary values.
Component Architecture
With tokens locked in, I moved to components. I follow the Atomic Design methodology, adapted to fit real-world product work.
Atoms
These are the primitives: buttons, inputs, labels, icons, badges, toggles. Each atom is:
- Fully self-contained with no dependencies on other components
- Driven entirely by design tokens
- Documented with every state (default, hover, focus, disabled, error)
Getting atoms right is critical because every mistake compounds as you move up the hierarchy.
Molecules
Molecules combine atoms into functional units. A search bar is an input atom plus a button atom plus an icon atom. A form field is a label plus an input plus a helper text component.
The key discipline here is composition over customization. Instead of building a mega-component with dozens of props, I compose smaller pieces. This keeps each molecule focused and testable.
Organisms
Organisms are the larger, more opinionated sections: navigation headers, data tables, card grids, modal dialogs. These are where product-specific decisions live.
I found it helpful to draw a clear boundary: organisms can encode layout opinions and business logic assumptions, but atoms and molecules should remain generic. This boundary keeps the system reusable across different product surfaces.
Building in Figma and Code Simultaneously
One mistake I made early in my career was treating the Figma library and the code library as separate projects. They would inevitably drift apart, causing confusion during handoffs.
Now I build them in tandem:
- Design the component in Figma with proper auto-layout, variants, and component properties
- Build the React component the same week, matching every variant and state
- Cross-reference naming — Figma variant names match code prop names exactly (
size="sm"in code,Size=smin Figma) - Document together — one Notion page per component with Figma embeds and code examples side by side
This parallel workflow means the system stays synchronized from day one.
Versioning Strategy
A design system without versioning is a liability. Here is the approach that has worked well for me:
- Semantic versioning (major.minor.patch) for the code package
- Major bumps for breaking changes (renamed props, removed components)
- Minor bumps for new components or new variants
- Patch bumps for bug fixes, token adjustments, documentation updates
In Figma, I use branch-and-merge workflows. Changes go into a branch, get reviewed by at least one other designer, and then merge into the main library. I timestamp each Figma publish with the corresponding npm version.
For consumers, I maintain a changelog that explains not just what changed but why and how to migrate. Nobody reads changelogs that just say "updated Button component." Instead: "Button: replaced type prop with variant prop to align with our naming conventions. Find-and-replace type= with variant= in your codebase."
Lessons Learned
Start small, ship early. My first version had 12 components. That was enough to prove value and get buy-in. Trying to build 60 components before shipping would have killed the project.
Audit before you build. I ran a UI audit across the existing product and found 14 different button styles. Seeing that number convinced stakeholders that the system was worth investing in. It also told me exactly which components to prioritize.
Governance matters. A design system without a contribution model stagnates or fragments. I set up a simple process: anyone can propose a component, proposals get reviewed biweekly, accepted proposals go into a backlog with clear ownership.
Adoption is a people problem, not a technical one. The hardest part was not building components — it was getting teams to actually use them. I spent as much time on documentation, onboarding sessions, and office-hours support as I did on design and code.
Tokens are your secret weapon. When leadership asked for a rebrand six months after launch, the token layer saved us weeks of work. We updated the primitives, verified the semantic mappings, and the entire product updated. That single moment justified the entire system.
Final Thoughts
Building a design system from scratch is one of the most rewarding projects a product designer can take on. It forces you to think systematically, communicate clearly, and balance short-term needs against long-term scalability. If you are considering starting one, my advice is simple: define your tokens, start with the components your team needs most, and ship it before it is perfect. You will iterate — and that is exactly the point.
Salman Alfariesh
Product Designer specializing in web & mobile experiences