Home / Articles / Design Engineering
Design EngineeringFrom Figma to production: our design-engineering handoff
Tokens, Code Connect, and the handoff ritual that kills the 'that's not what I designed' loop.
TL;DR — The “that’s not what I designed” loop is a handoff problem, not a talent problem. Kill it with three things: shared design tokens (variables, not screenshots), a component contract that both sides commit to, and Figma Code Connect so the design file points to the real code. Then review the built UI against the design before anyone says “done.”
Most design-to-code friction looks like a taste argument. It usually isn’t. It’s two teams reading two different sources of truth — a Figma frame and a React tree — and discovering the gap only after the build is “finished.” By then the fix is expensive, the designer feels overruled, and the engineer feels nitpicked. We’ve shipped enough client work to know the loop is preventable. Here’s the handoff ritual we actually use.
Tokens are the contract, screenshots are a rumor
The single biggest source of drift is values that live in two places. A designer sets a button radius to 10px; an engineer eyeballs it from a screenshot and ships 8px. Multiply that across spacing, color, type, and shadow and you get a UI that’s close — which is the worst possible outcome, because nobody can point at what’s wrong.
The fix is to stop transcribing values by hand. Design tokens live once, as Figma variables, and get exported to code as the same names. Not similar names. The same names.
:root {
--color-bg-surface: #0e0f12;
--color-accent: #18b6a6;
--space-3: 12px;
--radius-md: 10px;
--shadow-card: 0 1px 2px rgb(0 0 0 / 0.4);
}
When the designer changes --radius-md, it changes everywhere — in Figma and in code — and there’s no value left to argue about. Color, spacing, radius, type ramp, elevation: all tokens, all shared, all named identically on both sides.
If a value exists in the design but not as a token, it’s not a decision yet — it’s an accident waiting to ship.
This is the discipline part: no “magic numbers” in components. A one-off margin-top: 13px is a smell on both sides of the fence. If it’s intentional, it earns a token. If it’s not, it gets deleted.
A component is a contract, not a picture
A Figma component and a code component should agree on the same surface area: the same variants, the same states, the same prop names. When the design has a Button with variant (primary / secondary / ghost), size (sm / md / lg), and a disabled state, the code should expose exactly those props — spelled the same way.
This is where naming discipline pays rent. We agree on the vocabulary before the build:
- Variants map to props, not to copies. Three button variants are one component with a
variantprop, never three separate components calledButtonPrimary,ButtonBlue,ButtonCTA. - States are explicit. Hover, focus, disabled, loading — if the design didn’t draw them, we ask before we invent them.
- Names match the domain, not the layout.
card-product, notbox-3. The name survives the redesign; the pixel position doesn’t.
When the contract is written down — even as a short shared doc — the “that’s not what I designed” conversation moves to before the code exists, where it’s cheap.
Code Connect: make the design file point at the real code
Tokens align the values; the component contract aligns the structure. Figma Code Connect closes the last gap by linking each Figma component to its actual implementation, so when an engineer inspects a component in Figma they see your code — the real import, the real props — instead of generic auto-generated markup.
import figma from "@figma/code-connect";
import { Button } from "../src/Button";
figma.connect(Button, "https://figma.com/file/…?node-id=…", {
props: {
variant: figma.enum("Variant", {
Primary: "primary",
Secondary: "secondary",
Ghost: "ghost",
}),
label: figma.string("Label"),
},
example: ({ variant, label }) => <Button variant={variant}>{label}</Button>,
});
Now the handoff is no longer “here’s a frame, go figure out the props.” It’s “here’s the frame, and it tells you exactly which component and which props to use.” Engineers stop guessing; designers stop getting surprised. The mapping lives in the repo next to the component, so it’s reviewed and versioned like everything else.
Review the built UI against the design — on purpose
The last step is the one teams skip when they’re tired: a deliberate side-by-side review of the running build against the Figma frame, before the ticket closes. Not a vibe check — a checklist.
We diff against tokens (are the actual computed values the token values?), check every interactive state the design specified, and verify responsive behavior at the breakpoints the design defined. The designer signs off on the build, not the PR description. If something drifted, it’s caught here — minutes of work — instead of in a client review three weeks later.
This review is also where the system improves. A recurring “we needed a state the design didn’t have” becomes a note in the component contract. A repeated token request becomes a real token. The ritual feeds itself.
The takeaway
The “that’s not what I designed” loop isn’t about designers being precious or engineers being careless. It’s about two teams trusting two different sources of truth. Collapse them into one: tokens as shared variables, a component contract you both sign, Code Connect wiring the design file to the real code, and a built-vs-designed review before “done.” Do that and the handoff stops being a negotiation — it becomes a transfer. The design ships as designed, the first time, because there was never a second version to disagree about.
The engineering & R&D notebook of Hagumi Studio. We write what we learn building and self-hosting the tools behind our work.
HAGUMISTUDIO.COM · X · RSS