The handoff nobody has fixed
Design-to-code is the most expensive, least reliable step in the delivery pipeline. It is done by hand, differently by every developer, and it drifts the moment the design changes.
The existing tools all produce a starting point that a human then rewrites. None produce a repeatable, updatable rail: a pipeline you re-run against an updated Figma and get a reviewable diff rather than a fresh pile of code. And not one of them publishes a substantiated accuracy benchmark. “98% pixel perfect” appears in marketing copy with no methodology, no dataset, and no definition of the metric.
CONCRA’s wedge is not generation. It is measurement.
How it works
-
Extract
Drives Figma directly and reads every frame, every node, every variable. No plugin to install, and no modification of Figma’s signed binary — that is circumvention, it breaks on every auto-update, and it fails enterprise review on sight.
-
Normalise
Everything becomes CIR, a layout algebra that projects onto flexbox, SwiftUI stacks, Compose, Flutter and Android without smuggling web assumptions into a native target. Constructs with no equivalent are recorded as lossy rather than quietly approximated.
-
Generate
Vanilla HTML/CSS/JS and React today; SwiftUI, Flutter and Android XML share the same layout algebra. Bound fills emit
var(--token), never a hex code — the difference between output a team adopts and output they regenerate once and abandon. -
Verify
Renders what it generated, diffs it against Figma’s own render of the same frame, and names the node behind every difference. Below threshold, the build fails. This is the part no competitor has.
-
Re-run
The design changes, you re-run, and your hand-written code survives. Generated regions are fenced; everything outside them is yours. Three-way merge against a snapshot of what was last generated, so a conflict means two people really did change the same thing.
The benchmark
Intervals are a percentile bootstrap over 10,000 seeded resamples for the means, and Wilson for the pass rate. Neither can report a value the data does not support: a t-interval on a score capped at 1.0 routinely claims a mean better than identical, and the normal approximation claims certainty from a perfect small sample. Measured on a real production design file — Momentum Bakari, 10 pages and 325 screens — not on a demo built to score well. 93 screens carry a reference render and are measured below.
Layout SSIM
| mean | 0.9978 |
|---|---|
| 95% CI | 0.9972 – 0.9983 |
| median | 0.9994 |
| 10th percentile | 0.9935 |
| worst | 0.9924 |
| best | 1.0000 |
Gaussian-blurred SSIM. Separates “the layout is wrong” from “the content differs”, which are not the same problem and should not share a number.
Detail SSIM
| mean | 0.9625 |
|---|---|
| 95% CI | 0.9560 – 0.9688 |
| median | 0.9702 |
| worst | 0.8409 |
Unblurred. Lower than layout by construction: two rendering engines never rasterise text identically, and any tool claiming otherwise is not measuring.
Structural
| nodes compared | 6685 |
|---|---|
| nodes off | 1186 |
| min coverage | 100.0% |
Generated DOM boxes against Figma’s own geometry. No image diff can name a node; this can, and it reports where an error was introduced rather than every element that inherited it.
Every frame, worst first
Published in full rather than as an average, because an average hides the frame that would have embarrassed us.
| Frame | Layout | Detail | Coverage | |
|---|---|---|---|---|
| Full Stack prototype / What is live now | 0.0000 | 0.0000 | 0.0% | unverifiable |
| Bakari UI kit | 0.0000 | 0.0000 | 0.0% | unverifiable |
| Mobile / Ideator / Capture Step 2 / Selected | 0.9924 | 0.9419 | 100.0% | pass |
| Mobile / Ideator / Capture Step 3 / File rejected | 0.9925 | 0.9248 | 100.0% | pass |
| Mobile / All / Capability guide / Loaded | 0.9929 | 0.9455 | 100.0% | pass |
| Mobile / All / Capability guide / Read expanded | 0.9932 | 0.9454 | 100.0% | pass |
| Mobile / Ideator / Capture Step 3 / Empty | 0.9933 | 0.9485 | 100.0% | pass |
| Mobile / Ideator / My work / Discard confirm | 0.9933 | 0.9489 | 100.0% | pass |
| Mobile / Ideator / Capture Step 1 / Valid | 0.9933 | 0.9295 | 100.0% | pass |
| Mobile / Ideator / Capture Step 1 / Saved | 0.9934 | 0.9339 | 100.0% | pass |
| Mobile / Ideator / Capture Step 1 / Save failed | 0.9934 | 0.9350 | 100.0% | pass |
| Mobile / Ideator / Capture Step 3 / Attached | 0.9935 | 0.9358 | 100.0% | pass |
| Mobile / Ideator / My work / Drafts | 0.9936 | 0.9453 | 100.0% | pass |
| Mobile / Ideator / Finished / Signal error | 0.9940 | 0.9360 | 100.0% | pass |
| Mobile / Ideator / Capture Step 2 / Error | 0.9940 | 0.9523 | 100.0% | pass |
| Mobile / Ideator / Finished / Signal loading | 0.9940 | 0.9404 | 100.0% | pass |
| Mobile / Ideator / Finished / No match | 0.9940 | 0.9398 | 100.0% | pass |
| Mobile / Ideator / Capture Step 1 / Empty | 0.9941 | 0.9534 | 100.0% | pass |
| Mobile / Ideator / Capture Step 1 / Below minimum | 0.9941 | 0.9544 | 100.0% | pass |
| Mobile / Ideator / Finished / One match | 0.9941 | 0.9371 | 100.0% | pass |
| Mobile / Ideator / Capture Step 3 / In production branch | 0.9944 | 0.9555 | 100.0% | pass |
| Mobile / Ideator / Capture Step 2 / Loading | 0.9945 | 0.9532 | 100.0% | pass |
| Mobile / Ideator / Capture Step 2 / Empty | 0.9946 | 0.9438 | 100.0% | pass |
| Mobile / Ideator / Capture Step 2 / Resumed | 0.9951 | 0.9507 | 100.0% | pass |
| Web / All / Explore / Error | 0.9952 | 0.9773 | 100.0% | pass |
| Mobile / Ideator / Finished / Three matches | 0.9955 | 0.9523 | 100.0% | pass |
| Mobile / Executive / Portfolio / Empty | 0.9960 | 0.9558 | 100.0% | pass |
| Mobile / All / Explore / Filtered empty | 0.9964 | 0.9722 | 100.0% | pass |
| Mobile / All / Explore / Filters applied | 0.9966 | 0.9455 | 100.0% | pass |
| Mobile / Executive / Portfolio / Error | 0.9966 | 0.9704 | 100.0% | pass |
| Mobile / All / Explore / Empty | 0.9967 | 0.9768 | 100.0% | pass |
| Mobile / All / Explore / Error | 0.9968 | 0.9769 | 100.0% | pass |
| Mobile / All / Capture Step 2 / Guide open | 0.9968 | 0.9628 | 100.0% | pass |
| Mobile / All / Explore / Filters expanded | 0.9973 | 0.9734 | 100.0% | pass |
| Mobile / All / Initiative / As found | 0.9973 | 0.9533 | 100.0% | pass |
| Mobile / Executive / Portfolio / Loaded | 0.9974 | 0.9657 | 100.0% | pass |
| Mobile / All / Explore / Loading | 0.9974 | 0.9801 | 100.0% | pass |
| Mobile / All / Initiative / Merged | 0.9975 | 0.9573 | 100.0% | pass |
| Mobile / All / Initiative / Three share the problem | 0.9975 | 0.9557 | 100.0% | pass |
| Mobile / All / Explore / Loaded | 0.9975 | 0.9463 | 100.0% | pass |
| Mobile / All / Initiative / Comments | 0.9978 | 0.9481 | 100.0% | pass |
| Web / All / Explore / Page 2 | 0.9981 | 0.9478 | 100.0% | pass |
| Mobile / Executive / Portfolio / Figure opened | 0.9982 | 0.9702 | 100.0% | pass |
| Mobile / Executive / Portfolio / Loading | 0.9984 | 0.9871 | 100.0% | pass |
| Bakari / Project context | 0.9990 | 0.9231 | 100.0% | pass |
| Web / Executive / Portfolio / Error | 0.9994 | 0.9953 | 100.0% | pass |
| Web / Executive / Portfolio / Loaded | 0.9994 | 0.9745 | 100.0% | pass |
| Web / All / Explore / Loaded | 0.9994 | 0.9565 | 100.0% | pass |
| Web / Executive / Portfolio / Loading | 0.9996 | 0.9971 | 100.0% | pass |
| Web / All / Explore / Multi-select open | 0.9996 | 0.9657 | 100.0% | pass |
| Bakari · Portfolio | 0.9996 | 0.8409 | 100.0% | warn |
| Web / Executive / Portfolio / Empty | 0.9996 | 0.9854 | 100.0% | pass |
| Web / Executive / Portfolio / Figure opened | 0.9997 | 0.9914 | 100.0% | pass |
| Web / AI lead / Merge / Ready | 0.9999 | 0.9859 | 100.0% | pass |
| Production system / As-is reference | 0.9999 | 0.9645 | 100.0% | pass |
| Web / AI lead / Merge / Reason required | 0.9999 | 0.9904 | 100.0% | pass |
| Web / AI lead / Merge / Choose target | 0.9999 | 0.9904 | 100.0% | pass |
| Web / AI lead / Merge / Refused, already merged | 0.9999 | 0.9938 | 100.0% | pass |
| The question set / What you sent us, read back | 0.9999 | 0.9562 | 100.0% | pass |
| Web / Ideator / Capture Step 3 / Empty | 1.0000 | 0.9891 | 100.0% | pass |
| Web / Ideator / Capture Step 3 / In production branch | 1.0000 | 0.9897 | 100.0% | pass |
| Bakari · Explore | 1.0000 | 0.8647 | 100.0% | warn |
| Bakari · Joining an initiative | 1.0000 | 0.8848 | 100.0% | warn |
| Web / All / Explore / Filtered empty | 1.0000 | 0.9871 | 100.0% | pass |
| Bakari · Merging a duplicate | 1.0000 | 0.8692 | 100.0% | warn |
| Web / Ideator / My work / Drafts | 1.0000 | 0.9936 | 100.0% | pass |
| — PAGE TITLE — | 1.0000 | 0.9013 | 100.0% | pass |
| Web / All / Explore / Empty | 1.0000 | 0.9945 | 100.0% | pass |
| Bakari · The capability guide | 1.0000 | 0.9053 | 100.0% | pass |
| Web / Ideator / My work / Discard confirm | 1.0000 | 0.9922 | 100.0% | pass |
| Web / Ideator / Finished / Three matches | 1.0000 | 0.9890 | 100.0% | pass |
| Web / Ideator / Capture Step 1 / Save failed | 1.0000 | 0.9899 | 100.0% | pass |
| Web / Ideator / Capture Step 1 / Saved | 1.0000 | 0.9897 | 100.0% | pass |
| Web / Ideator / Finished / One match | 1.0000 | 0.9888 | 100.0% | pass |
| Web / Ideator / Finished / No match | 1.0000 | 0.9902 | 100.0% | pass |
| Web / Ideator / Capture Step 2 / Error | 1.0000 | 0.9937 | 100.0% | pass |
| Web / Ideator / Capture Step 2 / Loading | 1.0000 | 0.9941 | 100.0% | pass |
| Web / Ideator / Capture Step 2 / Empty | 1.0000 | 0.9903 | 100.0% | pass |
| Web / Ideator / Capture Step 1 / Empty | 1.0000 | 0.9956 | 100.0% | pass |
| Web / All / Initiative / Merged | 1.0000 | 0.9848 | 100.0% | pass |
| Web / All / Initiative / Comments | 1.0000 | 0.9841 | 100.0% | pass |
| Web / All / Explore / Filters applied | 1.0000 | 0.9849 | 100.0% | pass |
| Web / AI lead / Initiative / Merge available | 1.0000 | 0.9871 | 100.0% | pass |
| Web / All / Capability guide / Explore not answering | 1.0000 | 0.9762 | 100.0% | pass |
| Web / All / Initiative / Three share the problem | 1.0000 | 0.9884 | 100.0% | pass |
| Web / All / Capability guide / Read expanded | 1.0000 | 0.9740 | 100.0% | pass |
| Web / All / Capability guide / Loaded | 1.0000 | 0.9738 | 100.0% | pass |
| Web / All / Explore / Search active | 1.0000 | 0.9872 | 100.0% | pass |
| Web / All / Initiative / As found | 1.0000 | 0.9891 | 100.0% | pass |
| Personas / Who needs to see what | 1.0000 | 0.9472 | 100.0% | pass |
| Web / All / Capture Step 2 / Guide open | 1.0000 | 0.9935 | 100.0% | pass |
| Web / AI lead / Duplicates avoided | 1.0000 | 0.9933 | 100.0% | pass |
| Web / All / Explore / Loading | 1.0000 | 0.9956 | 100.0% | pass |
What CONCRA does not claim
A ten-nines accuracy claim is not achievable and, more importantly, not measurable. On a 1440×900 frame that would mean fewer than 0.0001 pixels may differ; one antialiased glyph edge breaks it. There is also no oracle: Figma’s renderer and a browser’s disagree on text by construction, and neither is wrong.
- Visual line breaks are not exposed by any Figma API. Only explicit newlines are. Any wrapped text block is reconstructed, and the residual is measured rather than hidden.
-
absoluteRenderBoundsis null for text inside component instances — exactly where production text lives. A documented API gap, not a bug we can fix. - Some Figma features have no target-side equivalent at all. Noise and texture effects, progressive blur, variable-width strokes, several blend modes. These rasterise, and the report says so rather than counting the difference against codegen.
- SwiftUI cannot be pixel-verified off macOS. No cross-platform renderer exists. Off macOS it runs a structural gate, and the report never implies otherwise.
Every one of those limits is in the tool’s own output, not just on this page.
Targets
Vanilla HTML + CSS + JS
Auto-verified. The reference target.
React + JSX
Auto-verified. Component sets become one component with variants, not N copies.
SwiftUI
Codegen plus an opt-in macOS verification job.
Flutter
Golden tests in a pinned Linux container.
Android XML
Roborazzi on a plain JVM. No device, no emulator.
Design tokens
DTCG, CSS custom properties, and Style Dictionary transforms.
Use it from your agent
CONCRA ships an MCP server, so Claude Code, Cursor or your own tooling can drive the
whole pipeline. One concra_load_file spends the Figma quota once; every
call after that is local and free.
claude mcp add concra -- concra mcp serve
That quota arithmetic is the point. Figma’s own MCP server is capped at a few hundred reads a day and hands back opinionated React, not data.
Pricing
Team
$89/seat/month
- Every target
- Fidelity gate in CI
- Design tokens and the MCP server
- The update rail
Enterprise
Self-hosted
- Runs on your machines, in your session
- Your designs never leave your network
- Calibration against your own corpus
- Support and onboarding
The field sits at $24–$100 per seat. CONCRA sits near the top of it, because a measured guarantee is worth more than an unmeasured one.
See it against your own file
The interesting demo is not ours. Point CONCRA at a design your team is arguing about and read the report.