UI Parity

Measure how much of your design system is in code, and whether it matches Figma: coverage, parity and drift for every Storybook story.

View on GithubNew to Storybook?Get started

UI Parity for Storybook

How much of your design system is actually in code, and does it match Figma? UI Parity measures it from what renders, story by story, inside the Storybook you already have: coverage, parity and drift for the whole library, with what it didn't check stated.

npm version weekly downloads license Storybook 9+

The UI Parity Coverage overview: 161 of 462 stories checked, 60% match Figma, drift listed by component


Why

Your design system is the contract. When a button ships with the wrong brand color or 4px too much padding, nobody notices until it's in production, or a designer files a ticket weeks later. UI Parity checks every story against its Figma variant inside Storybook and answers three questions:

Number Question Example from a real run
Coverage How much of the library was checked? 161 of 462 stories
Parity Of what was checked, how much matches the design? 96 of 161 (60%)
Drift Where, and by how much? 65 stories in 16 of 58 components, worst first

Every difference is a named property with Expected vs. Actual, not a pixel diff to eyeball. No baselines to approve, no snapshots to maintain.

It won't guess. When a story could be several Figma variants, or none, it says "not compared" and why, instead of producing findings against the wrong design.

Install

Guided (recommended)

npx uiparity init

The CLI installs the addon, registers it in .storybook/main.ts, and hands you off to the in-Storybook setup wizard.

Manual

npm install --save-dev @uicopilot/storybook-addon

Register it in .storybook/main.ts:

export default {
  addons: ['@uicopilot/storybook-addon'],
};

That's it — no other config. Restart Storybook and you'll see a UI Parity tab in the addon panel and a toolbar button on every story.

Just want to see it work?

Scaffold a throwaway Storybook that's already wired up:

npx uiparity demo                    # prompts for a template
npx uiparity demo --template radix   # Radix UI Themes
npx uiparity demo --template mui     # Material UI
npx uiparity demo --template cbds    # CBDS Components (ships with a Figma kit)
npx uiparity list                    # see every template

Templates also exist for Ant Design, Mantine, Carbon (IBM), Chakra UI, and Paste (Twilio). The cbds template is the fastest end-to-end demo because it includes a matching Figma kit — no design file of your own required.

Quick start

  1. Open the UI Parity panel on any story and sign in with Google, or hit "Try without signing in" to start a guest session.
  2. Connect Figma and pick the pages to index. The setup wizard walks you through it: connect Figma, paste your file URL, choose which pages to sync, and it indexes the components on those pages.
  3. Auto-map. UI Parity links every story to its Figma variant. Stories it isn't sure about are flagged, never guessed.
  4. Confirm the unsure ones. The Confirm variants tab shows the story beside the Figma variant it might be, with the variant's properties as controls. One look, one click, and it sticks.
  5. Review all. One run checks every mapped story. The Coverage tab shows coverage, parity and the drifting components; each row opens its stories.

You can still map and check a single story from the panel (Map to Figma, then Compare to design).

What it checks

Compare to design is the free, deterministic check. It diffs the story's computed styles against the Figma spec — no screenshots, no AI, no credits:

Catches today Property
Wrong brand / token colors color, fill, background (banded by ΔE, so a barely-visible shift is a minor, a wrong brand color is a critical)
Off typography font-family, size, weight
Spacing drift padding, gap, margins (sub-10px diffs are downgraded a severity level)
Corner radius border-radius
Borders width, color, style
Part sizes icons, avatars, segments and fixed widths
Part gaps the distance between the parts of a component

Because it reads the computed styles off the rendered DOM, it doesn't care how you wrote them. Tailwind classes, CSS Modules, styled-components, vanilla-extract, plain CSS, inline styles — all produce the same computed values, and that's what gets compared.

Not drift: the same box built differently (padding on a child instead of the parent), 1–2 px rounding, and hover or focus states.

Not checked yet: which parts are present (icons, buttons, avatars) and content insets. A passing story says so instead of a bare green "matches".

On the roadmap, not shipped: coverage per Figma variant axis, every instance in a multi-instance story checked against its own variant, a drift check in CI, and an AI review as a second opinion on top of the deterministic check.

The verdict

Every review resolves to a single traffic light — the worst difference found sets the color, so it can never hide a real failure behind a bunch of clean checks:

Light Verdict What it means
🟢 Matches design No differences — or only tiny size diffs / near-identical colors
🟡 Minor differences Cosmetic only — barely-visible color or spacing drift, nothing visibly off
🔴 Major / critical differences At least one clearly-wrong value — wrong brand color, font, size, or spacing

Severities are perceptually adjusted first — color diffs by perceptual ΔE distance, dimension diffs by pixel distance — so a barely-visible shift stays a minor, and only a real, visible problem turns the light red.

Every difference is reported as a concrete Expected vs. Actual pair on a named property, so there's nothing to interpret: background-color — expected #ea580c, actual #f97316.

Where the verdict shows up

The result isn't buried in one panel. Once a story has been checked, you see it in three places:

  • The addon panel — the full verdict plus the property-by-property difference list.
  • The story sidebar — each checked story gets a colored marker in Storybook's tree, and stories with differences show a count badge in the worst severity's color. You can scan the whole sidebar and see where the drift is without opening anything.
  • The toolbar — a UI Parity button on every story, so a check is always one click away.

Review every story at once

Hit Review all in the Coverage tab, or Run in Storybook's Testing module (UI Parity registers a test provider there). It checks every mapped story in one pass and streams results into the sidebar as each one lands.

The Coverage tab then shows the library-wide answer:

  • Coverage: stories checked, out of all your stories.
  • Parity: of the stories checked, how many match.
  • Why only N checked? Every story that wasn't checked is listed with its reason: not mapped, several variants fit, no Figma variant, or not run yet.
  • Drift by component: worst first. Each row expands to its stories; click one to open it.

Run it after a design-system bump, a token change or a dependency upgrade. Results persist across reloads; results from an older run are labelled as such.

Works with any Storybook framework

The check reads the rendered DOM, not your source, so the renderer doesn't matter:

react · vue · angular · web-components · html · svelte · preact · react-native

React is optional as a peer dependency — the addon's own UI is bundled and does not force a React version on your project. It runs against React 16.8 through 19 in a React Storybook, and against non-React Storybooks with no React in your app at all.

How this differs from visual regression testing

Both are useful, and they answer different questions.

Visual regression (snapshots) UI Parity
Compares against The last approved screenshot of your own component The Figma frame — the design source of truth
Catches Any change from the previous build Divergence from what the design says, even if it's been wrong for months
Output A pixel diff image to eyeball Named properties with Expected vs. Actual values
Needs Baselines to approve and re-approve Auto-mapping, plus one click for each story it is unsure about
Blind spot A component that never matched the design still passes forever Anything not expressed as a comparable style property

Snapshot tools tell you something changed. UI Parity tells you it doesn't match the design — which is the question that comes up in design review, not code review. Teams run both.

Review history

Each story keeps its past checks. Open the history dropdown in the panel to pull up an earlier review and see what the verdict and differences were then — useful for confirming that a fix actually moved the light, or for finding when a component started drifting.

Requirements

  • Storybook 9.0 or newer (works with Storybook 9 and 10)
  • A Figma account with a Dev or Full seat — free/Viewer seats can't grant the design-read access UI Parity needs to index your components
  • A free UI Parity account — sign in with Google, or start as a guest and sign up to save your results
  • No Figma plugin to install. UI Parity reads your file through Figma's API — signed-in users connect via OAuth, guests paste a Figma personal access token.

Configuration

All configuration is optional. Set these through env in .storybook/main.ts:

export default {
  addons: ['@uicopilot/storybook-addon'],
  env: (config) => ({
    ...config,
    STORYBOOK_UICOPILOT_URL: 'https://your-dashboard.example.com',
  }),
};
Variable Purpose
STORYBOOK_UICOPILOT_URL Point the addon at your own dashboard deployment instead of https://app.uiparity.com.
STORYBOOK_UICOPILOT_FIGMA_KIT A reference Figma community-file URL to surface in the project setup screen as a copyable starting kit. Set automatically by npx uiparity demo templates that ship one.
STORYBOOK_UICOPILOT_NO_PUPPETEER Set to true to disable server-side screenshot capture and fall back to in-browser capture. Debug/troubleshooting flag.
STORYBOOK_UICOPILOT_CHROME_PATH Absolute path to a Chrome/Chromium executable. Only needed when your browser is somewhere the addon does not look — a CI image, or a non-standard install. Takes precedence over auto-discovery.

Self-hosting the dashboard

The addon talks to https://app.uiparity.com by default. STORYBOOK_UICOPILOT_URL (above) is the only change needed to point it at your own deployment — the addon has no other hardcoded endpoint.

What leaves your machine

Worth knowing before you point it at a private design system:

  • The free spec-diff check sends a compact tree describing the rendered component — element tag names, class/id selectors, any short visible text (truncated), and the element's computed style values (colors, sizes, spacing, fonts) — along with the story ID and the mapped Figma node ID. It does not upload your source files or stylesheets, and it does not send screenshots.
  • Figma data is fetched server-side using the credentials you connected, and only for the pages you explicitly chose to sync during setup.
  • Screenshots, when captured, are taken locally — the addon runs a capture endpoint inside your own Storybook dev server (via Puppeteer, if it's installed in your project).
  • Self-hosting the dashboard keeps all of the above inside your own infrastructure.

Troubleshooting

The UI Parity panel doesn't appear. Confirm the addon is in the addons array of .storybook/main.ts and restart Storybook. If you've changed the addon version, clear the cache: rm -rf node_modules/.cache .cache storybook-static.

"Compare to design" is disabled. The story isn't mapped to a Figma variant yet. Run auto-map from the Coverage tab, or map it from the panel.

A story says "not compared". Several Figma variants fit it, or none does. Open Confirm variants, pick the right one, and it is checked from the next run on.

Figma won't connect / no frames show up after sync. Two usual causes: a Figma Viewer or free seat (a Dev or Full seat is required for design-read access), or the frames live on a page you didn't select during setup. Re-run the Figma step and include the page.

Everything comes back as a difference. This is almost always a mapping problem — the story is pointed at the wrong Figma variant. Open the mappings list and confirm the story is mapped to the frame you actually meant.

The sidebar markers look like Storybook's own test statuses. They share the same status glyph. UI Parity only recolors markers for stories it actually has a status for, so anything untouched is your test runner's, not ours.

Screenshots fail or time out, or stories come back with no code DOM. Server-side capture runs a headless browser inside your own Storybook dev server, and it needs one of:

  • puppeteer — bundles its own Chromium (~200MB download), works with no browser installed; or
  • puppeteer-core — ~1MB, and uses a Chrome/Chromium you already have. The addon looks in the standard locations for macOS, Linux and Windows (including per-user Windows installs); point it elsewhere with STORYBOOK_UICOPILOT_CHROME_PATH.

Both are optional peer dependencies, so neither is installed for you. puppeteer-core is the lighter choice if you already have Chrome — which you probably do.

If the addon reports "No Chrome or Chromium found", that is the missing-browser case: install Chrome or set STORYBOOK_UICOPILOT_CHROME_PATH. If it reports "found a browser … but could not drive it", the browser and puppeteer-core versions disagree — update one of them. The two are reported separately on purpose, because the fixes are different.

In CI, use an image that ships Chrome (or install it in a setup step) and set STORYBOOK_UICOPILOT_CHROME_PATH if it lands somewhere unusual.

Failing all of that, set STORYBOOK_UICOPILOT_NO_PUPPETEER=true to use the in-browser fallback.

Links

License

MIT © UI Parity