How To Build A Design System

Mixed-media illustration: a real steel robotic arm clamped at the left edge sets a lime-outlined drawn "Button" block into a half-built drawn checkout screen labelled "Checkout", while behind it a tall, leaning orange-outlined stack of unused drawn components carries a tag reading "Everything, just in case".
Noah Chen
Product & Client Success Manager, ANODA
Published
11 min read
14 sections

In short

A design system is not a component catalogue drawn in advance. It is a set of shared design and code decisions built from the product you actually ship. Here is the whole build: adopt, adapt, build or repair; scope the flows; inventory design and code; fix broken UX before you standardise it; set tokens; build the repeated parts first; prove them on real screens; align code; document; govern. Then keep it alive, because it is never finished.

In this article
  1. What building a design system actually means
  2. Decide first: adopt, adapt, build or repair
  3. Scope the product before you scope the system
  4. Inventory what already exists, in design and in code
  5. Fix broken UX before you standardise it
  6. Lay the foundations as tokens
  7. Build the repeated primitives first
  8. Prove the system on real screens
  9. Make design and code the same system
  10. Document decisions, not just parts
  11. Govern without turning the system into a bottleneck
  12. What a first production-ready release looks like
  13. Why ANODA builds from the product journey
  14. Release it, then keep it alive

To build a design system, start from the product, not from a component list. Scope the flows you actually ship and the roadmap you actually believe, audit what already exists in design and code, fix the broken patterns, set the foundations as tokens, build the repeated parts first, prove them on real screens, ship them as code, document the decisions and give the whole thing an owner. Then keep going, because a design system is never finished. Anyone selling you a “complete” one is selling you a very tidy museum.

The popular version runs backwards. A designer disappears for a quarter and comes back with a design file holding every component anyone could ever want, every variant, every state, and a cover page with a gradient. Engineers nod politely and keep using the button they wrote three years ago. The file is gorgeous. It is also a dictionary for a language nobody speaks yet.

Meanwhile the product pays for everything twice: once to design the same modal again, once to build it again, and a third time in support tickets when the two versions behave differently. Stopping that leak is the whole job of a design system. A catalogue drawn in advance never stops it.

ANODA starts where that waste happens: the flows you ship, the conflicting patterns already in use and the handoff between design and engineering. We fix the UX before turning it into a reusable component, so the system spreads the right decisions. You get a scoped first release, representative screens and clear component guidance your engineers can carry into production.

What building a design system actually means

A design system is a set of shared decisions — colour, type, spacing, components, patterns and the rules for using them — that live in both the design tool and the production code, with someone responsible for keeping the two the same. A UI kit is the drawing. A design system is the drawing, the built thing and the agreement about when to use which.

The drawing is the cheap part. The expensive part is getting designers and engineers to reach for the same answer by default, on every new screen, without a meeting. If your system exists only in the design tool, engineers still make every decision alone, and you have paid for a very expensive reference image.

This guide covers the build process end to end. The anatomy of individual components — states, variants, slots — has its own guide to design system components and UI strategy, and if you want to see how big companies run theirs, start with these design system examples from top brands. Admire them. Don’t copy their scale. They fund whole teams to run those systems. You probably have one designer on Fridays.

Decide first: adopt, adapt, build or repair

Not every product needs a custom design system. Plenty of products pay for one they never needed.

Think of it as buying transport. Adopting an existing foundation — Material, a headless component library, a well-maintained open-source kit — is buying a reliable car. Adapting one is the same car with your paint, a roof rack and a tow bar. Building custom is designing a car from scratch, which makes sense for a racing team and very little sense for a trip to the shops. Repairing is admitting you already own a car. It just has four different tyres.

Illustrative example: a decision table with four options as columns — adopt an existing foundation, adapt one, build custom, repair what you have — and rows for products and platforms, interaction model, brand in the interface, current state and first spend, with the typical signal for each option.
Illustrative example: four honest options. Only one of them starts with a blank file.

A small product with one platform, one team and no unusual interactions should almost always adopt or adapt. Reinventing the wheel is a lovely hobby. As a line item, it is a quarter of engineering salaries spent on a date picker that already exists, works and is tested by people who do nothing else. Spend that money on the screens that earn it.

Custom starts to pay its way when several products or platforms must feel like one, when your core interactions exist in no library — dense trading tables, clinical workflows, specialist editors — or when the brand has to be recognisable in the interface itself. Even then, “custom” usually means your own tokens and patterns on top of accessible primitives somebody else already hardened.

Repair is the right answer more often than anyone likes to admit. The product already has a system. It has simply drifted. You don’t need a new one. You need to find out what the old one turned into.

Mixed-media illustration: a drawn family car stands on paper while a real steel robotic arm clamped at the left edge bolts a lime roof rack onto its roof; behind it, an orange-outlined drawn car chassis sits on bricks with no wheels under a tag reading “Custom, v0.1”.
One of these gets you to the shops this afternoon. The other has a very exciting roadmap.

Scope the product before you scope the system

Start with what the product does now and what it will credibly do in the next two or three releases. Not the vision deck. The roadmap you would bet your own money on.

List the core flows — sign-up, the main job, billing, settings, whatever pays the bills — and the platforms they run on. That list is the boundary of the first release. A component no current or near-term flow needs doesn’t get built yet. Designing a carousel “because we might need one” is biting off more than you can chew, then paying someone to maintain what you bit off.

Scoping is also where you decide who is in the room. A system built by design alone becomes a poster. One built by engineering alone becomes a component library with no opinions. The scope conversation needs a designer, a front-end lead and whoever owns the roadmap, together, from day one.

Inventory what already exists, in design and in code

Before anyone draws a new component, count the old ones. Capture every key screen, open the design files and — the step everybody skips — search the codebase. The design file tells you what someone intended. The code tells you what users get.

It’s like moving house. You have no idea you own four can openers until you empty every drawer.

Illustrative example: an inventory table for an invoicing product comparing versions in design files with versions in code — primary button 3 versus 7, brand blue 4 versus 11 hex values, date format 2 versus 4, confirmation modal 2 versus 5, empty state 0 designed versus 6 ad hoc, inline error 3 versus 3 — with a decision for each row.
Illustrative example: the design file is the brochure. The code column is the house you actually bought.

Sort what you find into three piles: duplicates to merge, gaps to fill, and inconsistent states — the loading, empty, error and disabled versions nobody designed on purpose. Each duplicate is a small tax. Together they are death by a thousand cuts: every new feature opens with a debate about which of the five modals is the real one, and every debate is billable hours.

Mixed-media illustration: a real steel robotic arm hanging from the top edge pulls open a drawn drawer packed with drawn buttons that all read “Save” in different shapes, sizes and shades of blue, with an orange tag tied to the handle reading “11 buttons, 1 job”.
Nobody decided to have eleven Save buttons. That is exactly the problem.

The inventory ends in one screen redrawn with a single set of decisions. It is the first proof that the system will make the product calmer, not just the file tidier.

Illustrative example: two phones showing the same invoice. Before, the screen mixes a rounded blue “Send invoice” button, a square outlined “SAVE” button and a blue text link, shows the due date as both 10/12/2026 and 12 Oct 2026, and flags a missing email in small red text. After, it shows one date format with “Due Mon 12 Oct 2026”, a warning “Add a billing email” with an “Add email” action, a disabled “Send invoice” button, a secondary “Save draft” button and a “Download PDF” text button.
Illustrative example: same invoice, same data. One screen argues with itself, the other made its mind up.

Fix broken UX before you standardise it

Here is the trap. The inventory shows a pattern used on forty screens, so the team crowns it the official component. Nobody checks whether the pattern works.

Standardising unresolved UX is putting a cast on a broken arm before anyone sets the bone. It heals. Crooked. Permanently, and now on every screen. A dialog that says “Are you sure? Cancel / OK” becomes the system’s official way to lose a customer’s data, complete with documentation and a version number.

Illustrative example: two phones. Before, a system dialog over a client screen reads “Are you sure? This action cannot be undone.” with Cancel and OK. After, a bottom sheet asks “Delete Harbor & Finch?”, explains that 3 open invoices totalling $9,615.00 will be archived and stop sending reminders while paid invoices stay in reports, with the buttons “Delete client” and “Keep client”.
Illustrative example: standardise the left one and you ship the same accident to every screen in the product.

So before a pattern becomes a component, test it against its hardest real case: the long name, the destructive action, the error, the empty account, the person on a phone with one free thumb. If it fails, fix the UX first and standardise the fix. If nobody can say what happens after the user clicks, that is a product question, and no component will answer it for you.

Mixed-media illustration: a drawn user-flow arrow lies bent at a sharp angle on a drawn X-ray lightbox; a real steel robotic arm from the right edge straightens it against a lime splint, while an orange drawn plaster cast labelled “Component v1” waits unused beside it.
Set the bone first. The cast only makes the shape permanent.

Lay the foundations as tokens

Foundations are the decisions everything else is made of: colour, typography, spacing, radius, elevation, motion, breakpoints. Tokens are those decisions with names, stored once and read by both the design tool and the code.

Hard-coded values are prices written on every tin with a marker. Change the price and someone walks the aisles with a pen for a week and still misses half a shelf. Tokens are the barcode: change it once at the till and every tin is right.

Tokens work best in layers. Primitive tokens hold raw values: a specific blue, a 16-pixel step. Semantic tokens say what a value is for: the colour of a primary action, the text of an error. Component tokens, where you need them, bind those meanings to specific parts. Rebrand, add dark mode or ship a white-label version, and you change the mapping instead of hunting through the codebase.

Illustrative example: a three-column diagram of token layers. Primitive tokens such as blue.600 #35587a, red.700 #a23b2a, space.4 16 px and radius.2 8 px map to semantic tokens such as color.action.primary, color.text.danger, space.stack.md and radius.control, which map to component tokens such as button.primary.background, input.error.message, form.field.gap and button.radius.
Illustrative example: what the value is, what it is for, where it goes. Change the middle layer and the product follows.

There is now a shared format for this. On 28 October 2025, the W3C Design Tokens Community Group announced the first stable version of its Design Tokens specification, 2025.10, a vendor-neutral way to store tokens so tools can exchange them. A standard format won’t design your palette. It will stop your tokens being locked inside whichever plugin was fashionable the year you started.

Illustrative example: two panels for rebrand day. Hard-coded: four code lines in files such as InvoiceTable.tsx, SendButton.css, Modal.scss and Chart.tsx, one of them a near-miss #305e8d, plus “…and 210 more places”. Tokens: one changed line, blue.600 from #2f5d8c to #35587a, and color.action.primary pointing to it, read by every component.
Illustrative example: 214 edits and a near-miss nobody will find, or one line and one review.

Don’t overdo it. A token for every value is painting yourself into a corner: nobody can change anything without renaming half the file. Name the decisions the product actually makes. Add more when a real screen needs them.

Build the repeated primitives first

With foundations in place, build in the order of how often something appears and how much it costs when it’s inconsistent. Buttons, inputs, selects, checkboxes, links, type styles, icons, basic layout: the parts on almost every screen. Then the patterns your product lives on: tables, forms, modals, navigation, notifications, empty and error states.

You teach a puppy to sit before you teach it to fetch the newspaper. The rare, shiny component — the 3D chart, the onboarding carousel — waits until the boring ones hold up under real load.

Illustrative example: a priority table for an invoicing product’s first release listing how many of 36 core screens each component appears on and the cost of inconsistency — button 36, text input 22, table 14, select 13, modal or sheet 9, date picker 7 (adopted), all in v1; toast 5 in v1.1; onboarding carousel 1, not yet.
Illustrative example: frequency times cost decides the order. The carousel can wait. It always could.

Each primitive ships with its states designed on purpose: default, hover, focus, pressed, disabled, loading, error. A first release that nails ten common parts beats one that sketches ninety. Ninety sketches is a mood board with a changelog.

Design systems

Custom build, adaptation or a repair job? Find out before you fund one.

We go through your screens, design files and existing code, show where the duplicates and broken patterns are, and scope the smallest system release your team can actually use.

Scope your design system with us

Prove the system on real screens

Components that look perfect on their own page are ingredients tasted one at a time. The flour is fine. The eggs are fine. You still don’t know whether it’s a cake.

Pick a handful of representative screens — the busiest table, the longest form, the most important flow on mobile — and rebuild them only from system parts, with real data. Real client names are long. Real amounts have six figures. Real statuses say “Partially paid · overdue”, not “Active”. This is where the table row that looked elegant with “Acme” falls apart.

Illustrative example: two phones listing invoices with real data. Before, a row built for short sample names cuts “Harbor & Finch Architectural Lighting Ltd” to “Harbor & Finch Archi…”, squeezes $128,440.00 and pushes the status chip out of the row. After, the name wraps to two lines, the amount sits on its own line and the status reads “Partially paid · overdue”.
Illustrative example: the component was tested with “Acme”. Your biggest client is not called Acme.

Check responsive behaviour at the widths people actually use, not just the three breakpoints in the file. Then check accessibility at two levels, because they are not the same thing.

Primitives can be built to meet criteria from WCAG 2.2, the W3C Recommendation — for example, a 4.5:1 minimum contrast for normal text and 24-by-24 CSS pixel pointer targets, with defined spacing and other exceptions. Automated checks help detect component issues; walk the assembled task with a keyboard and screen reader to check focus order, error announcements and modal behaviour. Build those decisions into the system so each new screen starts from a stronger foundation.

Mixed-media illustration: a drawn fire door stamped with a lime “All checks passed” badge is held open by an orange drawn wedge labelled “Keyboard trap”; a real steel robotic arm from the bottom edge pulls the wedge out.
Every part passed. The door still doesn't close.
Illustrative example: a table comparing automated component checks with a keyboard and screen reader walkthrough of sending an invoice. Text contrast, pointer target size and input labels pass in both. Focus moving into the modal, the error being announced and finishing the task without a mouse are not checked automatically and fail in the walkthrough.
Illustrative example: green on every component, red on the one task that pays the bills.

Make design and code the same system

A system that exists twice — once in the design library, once in code — is keeping two sets of books. They match on the day you publish. Six months later the design button has an 8-pixel radius, the production one has 6, and every handoff opens with an argument about which book is true.

Mixed-media illustration: two drawn ledgers lie open side by side, labelled “Design” and “Code”; the same drawn button sits on both pages, marked “radius 8” on one and “radius 6” on the other in orange; a real steel robotic arm hanging from the top edge lays a real steel ruler across both pages.
Two sets of books never end well. Ask any accountant, or any auditor.
Illustrative example: a drift table comparing the design library with production code — button radius 8 px versus 6 px, disabled button designed but not built, input error with a message versus border only, loading button not designed versus a spinner replacing the label, table row height 56 px versus three different heights — with the decision taken for each.
Illustrative example: every row is a decision somebody was making alone.

Designers and developers have to work together at four moments at minimum: when tokens are named, when a component’s API and states are defined, when representative screens are validated, and when anything is changed or deprecated. Between those moments they can work in parallel. At those moments, working apart is how you get two systems with one name.

The goal is one source of truth per decision: tokens generated from one place, coded components that match the design library property for property, and a visible status on each — in design, in code, stable, deprecated — so nobody builds a feature on something that exists only as a drawing. That feature gets rebuilt later, and you pay for it twice.

Document decisions, not just parts

Documentation that lists the parts but never says when to use them is a script with all the lines and no stage directions. Everyone knows what to say. Nobody knows when to walk on. So people grab the nearest part, and a modal turns up where a toast belonged.

Good documentation is short and opinionated. For each component: when to use it, when not to, which variants exist and why, content rules — how to word the button, how long an error can be — accessibility behaviour, the code reference and its current status. Show one right example and one wrong one. Keep it next to the code, so it changes when the code does.

Illustrative example: a documentation page for a Modal component, status Stable v1.3, with sections “Use when”, “Don’t use when”, “Content”, “Accessibility”, “Code” and “Design”, including the rule that the title names the object and buttons say what happens, never “OK”.
Illustrative example: documentation that makes the decision, so the next designer doesn't have to guess it.

Govern without turning the system into a bottleneck

Design systems die in two ways. With no rules, every team adds its own version and you are back to the inventory you started with. With too many rules, every new need waits weeks for a committee, so teams quietly build around the system and you get the duplication anyway, plus resentment.

It’s refereeing. A referee who whistles every touch kills the game. No referee, and it’s a brawl by half-time. Governance is deciding which fouls matter.

The practical version fits on one page:

  • Contribution. Anyone can propose a component or a change. The first question is always whether something existing already does the job, or could with a variant.
  • Exceptions. A team can ship a one-off when a real flow needs it, as long as it is logged and reviewed on a date. Exceptions that keep coming back are the plan for the next release.
  • Versioning. Releases carry version numbers and release notes, so product teams upgrade on purpose instead of waking up to a different button.
  • Deprecation. Old components get a replacement, a date and a migration note before they disappear. Removing things is part of the job, not a failure of it.
  • Ownership. A named person or team owns the system, with real time in their week for it. Everybody talks about the design system, but nobody does anything about it — until somebody’s job description says so.
Illustrative example: a contribution flow. A team needs something new; if an existing component does the job, use it; if a variant would, extend it in a minor version; if it’s needed in only one flow, ship a logged local exception reviewed on Mon 4 Jan 2027; if it’s needed in two or more flows, it goes from proposal through design and code review to release in v1.4 on Mon 19 Oct 2026, and the component it replaces is deprecated in v1.4 with a migration note and removed in v2.0.
Illustrative example: one path for every request. Fast for the obvious ones, deliberate for the rest.

It is still herding cats. Governance just makes sure the cats are heading roughly the same way.

What a first production-ready release looks like

The first release is not the complete system. It is the smallest set of parts that lets the team ship the verified core flows without inventing anything new. It’s ready when:

  • the foundations exist as tokens used by both design and code;
  • the repeated primitives and core patterns are built in code, with all their states;
  • representative screens are rebuilt from system parts, with real data, at real widths;
  • automated checks pass on the parts, and key tasks have been walked through with a keyboard and a screen reader;
  • every component has usage guidance, a version and a status;
  • someone owns it, and there is a written way to contribute, make exceptions and deprecate.

If it can’t carry the product’s main flow today, it isn’t a release. If it waits until every component on the wishlist is drawn, it never will be one.

Why ANODA builds from the product journey

For Fusion, ANODA designed the user journey and a full component library with states for the client development team. Signup, feed and chat were connected to the same interface direction. A library that serves those real flows is useful from the first handoff; a catalogue built in isolation leaves the hardest decisions to someone else.

Bring the same discipline to your product: eliminate repeated choices, give design and engineering the same guidance and reserve custom work for the interactions that make your product valuable.

Release it, then keep it alive

After release, the system evolves with the product. New flows bring new patterns, research kills old ones, platforms change underneath you. It’s the Ship of Theseus with a changelog: replace every plank over three years and it’s still your ship, as long as somebody keeps the log.

Mixed-media illustration: a drawn wooden sailing ship whose planks are drawn interface components, several already replaced with lime planks; a real steel robotic arm clamped at the right edge fits a new lime plank labelled “v2.4” into the hull while an old orange plank lies discarded on the paper.
Every plank replaced, still the same ship. As long as somebody writes it down.

A design system is never finished. It is either maintained or decaying, and decay is quieter and more expensive. Budget for upkeep the way you budget for the product itself, because a system nobody tends starts drifting on launch day and becomes the next inventory’s problem. For more on the product decisions around it, the rest of the ANODA UX blog is a good place to keep reading.

Design systems

Your product doesn’t need every component. It needs the right ones, in code, with an owner.

Bring us your screens, your design files and your code. We’ll tell you whether to adopt, adapt, build or repair, then design the first release with your engineers in the room, so it ends up in code instead of in a drawer.

Plan your design system with ANODA

Related reading

All articles