Workflow · 9 min read

Build a Design System from Existing Code

Most teams ship code for years before anyone asks "where's the design system?" When that moment arrives, the answer isn't to start from scratch — it's to extract what's already there. Here's how to do it systematically.

HF

HTML to Figma Team

Last reviewed: April 2026

TL;DR

To build a design system from existing code: (1) audit CSS for recurring color, type, and spacing values; (2) compile a component inventory sorted by reuse frequency; (3) render each component in a browser; (4) import into Figma using html2design; (5) register token values as Figma Variables and Styles; (6) convert imported frames to Figma Components. Extracting from production code gives you a truthful baseline — every value reflects what actually ships. This approach is faster and lower-risk than designing a system from scratch, because it reconciles Figma with the real codebase rather than adding a second source of truth.

Why Extract Rather Than Build from Scratch?

The instinct when formalizing a design system is to open a blank Figma file and start designing. The problem with that instinct is that the codebase already exists. Designers who build from scratch invariably produce a system that diverges from production — sometimes subtly (slightly different font sizes), sometimes critically (a completely different color palette). Developers then have to decide whether to follow Figma or follow the code, and in practice they follow the code. The scratch-built system is abandoned within months.

Extracting from existing code avoids this trap. The starting point is a truthful audit of what actually ships. Figma becomes a reflection of production, not an alternative vision. Teams can then evolve the system deliberately — deprecating inconsistencies, introducing new patterns — from a foundation everyone agrees is accurate.

Key insight: Your codebase already contains a design system — it's just undocumented. CSS files, component libraries, and style configs encode hundreds of design decisions. The extraction process makes those decisions explicit and imports them into Figma as a workable library.

Rebuilding from scratch only makes sense in specific situations: a complete rebrand where the old visual language is being retired, a full framework migration where existing components aren't being carried forward, or a product in early development with too few components to warrant extraction. For every other situation — especially products that have been in production for more than a year — extraction is the right approach.

Step-by-Step: Extracting a Design System from Your Codebase

This workflow has six phases. They don't all happen in a single sprint — a real extraction project takes two to four weeks depending on codebase size — but they follow in order.

Phase 1

Token Audit

Search your codebase for recurring visual values. Run a grep across CSS files for hex colors, font-size declarations, border-radius values, and box-shadow definitions. If the codebase uses CSS custom properties or a token config (Tailwind's tailwind.config.js, a Style Dictionary source), start there — those files are the canonical token definitions and give you the full list in one place.

Build a spreadsheet with three columns: token name (if it has one), value, and frequency (how many times does this value appear in the codebase). Values that appear dozens of times are your core tokens. Values that appear once or twice are one-offs to audit later.

# Quick audit: find all hex colors in CSS files grep -roh '#[0-9A-Fa-f]\{3,8\}' src/ | sort | uniq -c | sort -rn | head -30

Phase 2

Component Inventory

List every UI pattern in the codebase. If you use Storybook, your stories are the inventory — sort them by usage metrics (which stories are in most features?). Without Storybook, walk through the live product screen by screen and catalogue every distinct component: buttons, inputs, cards, modals, nav bars, tables, badges.

For each component, note: How many variants exist? Are variants consistent or ad hoc? Is this a shared component or a page-specific one-off? This prioritization determines import order in Phase 4.

Phase 3

Build a Component Reference Page

Create a dedicated HTML page that renders every component you inventoried in Phase 2. This is the bridge between your codebase and Figma. Include all variants side by side — all button sizes, all input states (default, focus, error, disabled), all card layouts. The page doesn't need to be beautiful; it needs to be complete.

Group components by type on the page. Add section headings. This structure carries into Figma — html2design imports the page as Figma frames that mirror the layout, so a well-organized reference page becomes a well-organized Figma file. See the component library to Figma guide for a detailed example of this pattern.

Token reference section: Add a dedicated section to the page for your design tokens — a row of color swatches, a type scale sample, a spacing scale visualization. When you import with html2design, this section becomes your token reference frame in Figma. Read the computed values in the Inspect panel and register them as Figma Variables and Styles.

Phase 4

Import into Figma with html2design

Open the component reference page in a browser. Open Figma and install the html2design plugin. Run the plugin against the page — it reads the browser's computed styles and generates native Figma layers that reflect the actual rendered output.

Import in batches by component type. This gives you multiple import frames to work with rather than one enormous frame. After each batch, verify that font rendering, colors, and spacing match the live browser — spot-check a few values with the Inspect panel.

See the design system migration guide for a deeper walkthrough of this import step, including how to handle complex layouts and Storybook-rendered components.

Phase 5

Register Tokens in Figma

With your token reference frame imported, use Figma's Inspect panel to read the computed values. Create Figma Variables for color, number (spacing), and string (font family) tokens. Create Figma Styles for text styles and shadows. Name them to mirror your code token names — if the code has --color-primary, the Figma Variable should be color/primary.

Apply the Variables and Styles to the imported component frames. This step takes time but it's what makes the system maintainable — when a token value changes in code, you update one Figma Variable, and the change propagates across all components that reference it.

Phase 6

Convert Frames to Components

Select each imported component frame and convert it to a Figma Component (⌥ + ⌘ + K). Group related variants as a Component Set using Figma's Variant feature. Name components to match code component names — Button/Primary/Large should correspond to the React component <Button variant="primary" size="lg" />.

Publish the file as a Figma Library. Designers on the team can then enable the library in their files and use the components, which now accurately reflect production code.

Which Frameworks Work Best for Extraction?

The extraction workflow works across all major front-end frameworks because html2design reads browser output, not source code. What matters is how the browser renders the components — not whether they're built in React, Vue, Angular, or Svelte.

Framework / Stack Extraction quality Notes
Tailwind CSS Excellent Config-driven token structure maps cleanly to Figma Variables; utility classes resolve to standard CSS values in the browser
CSS custom properties Excellent Token names are explicit in code; computed values appear correctly in imported Figma layers
CSS Modules / Sass Good Compiles to standard CSS; extraction works well when variables are centralized in a shared file
CSS-in-JS (styled-components, Emotion) Good Dynamic styles computed at runtime; rendered output is accurate, but token names aren't recoverable from the import
Bootstrap / MUI / Ant Design Good Theme overrides import correctly; base framework styles appear as-is — focus extraction on customized values
Inline styles / no design system Fair Import works but produces inconsistent values; use the token audit phase to normalize before importing

Common Pitfalls

Pitfall: Importing before auditing. Skipping the token and component audit phases produces a Figma file full of inconsistencies that mirror the codebase's inconsistencies. The audit is where you decide which values to canonicalize and which to deprecate — doing it after import means double work.

Trying to import everything at once. A production codebase may have hundreds of components. Importing them all in a single session creates an unmanageable Figma file. Import in batches by component type and priority. High-traffic, shared components first; edge cases later.

Naming Figma Components without matching the code. If Figma has Card / Featured and the code has FeaturedCard, developers searching for design specs during handoff won't find the connection. Align names from the start — it pays off every sprint.

Skipping token registration. Importing component frames into Figma and not registering the token values as Variables/Styles leaves you with a static snapshot that can't be updated systematically. The extra step of creating Variables is what turns the import into a maintainable library.

One-and-done extraction. The extraction produces a baseline, not a permanent truth. Code will continue to evolve after the import. Without a resync cadence — even a monthly check where recently changed components are reimported — the Figma library drifts back into inaccuracy.

How html2design Fits Into This Workflow

html2design is the bridge between the browser and Figma. It reads the browser's computed layout and style output — after JavaScript has run, after CSS custom properties have been resolved, after fonts have loaded — and generates native Figma layers that reflect what users actually see. This is the key technical property that makes the extraction workflow possible.

Without a tool that reads rendered output, the alternative is manual recreation: inspecting computed values in DevTools and manually entering them in Figma. For a production codebase with dozens of components, that takes weeks. html2design compresses Phase 4 from days to hours.

The plugin preserves layer names from your HTML structure, so well-named HTML elements (semantic tags and BEM class names) translate into readable Figma layer trees. It handles complex layouts — flexbox, grid, sticky positioning — with fidelity to the rendered output. And because it reads from the browser, it works regardless of which build tool, framework, or CSS methodology the codebase uses.

Best practice: Use Storybook as your component reference page. Storybook already renders every component variant in isolation in the browser — which is exactly what Phase 3 asks you to build manually. If your team has a Storybook, it's the fastest path to a complete component import. See the component library guide for step-by-step Storybook → html2design → Figma instructions.

Maintaining the System After Extraction

The extraction establishes a truthful baseline. Maintaining it requires two ongoing practices.

First, a resync cadence. When a component changes significantly in code — a new variant, a redesigned interaction state, a token value update — reimport it with html2design and update the corresponding Figma Component. Teams typically do this at sprint boundaries. The design handoff best practices guide covers a bidirectional sync workflow that formalizes this cadence.

Second, a token governance rule. Any new token added to the codebase gets added to the Figma Variable set at the same time — not after the next resync. Assign this responsibility to whoever reviews the PR that introduces the new token. If you use CSS custom properties, this is a two-minute Figma step that prevents an hour of reconciliation later.

With these two practices in place, the Figma library stays current without heroic effort. The extraction was the hard part — maintenance is a lightweight ongoing process.

Related guides