Everyone in this industry has lived this moment. You are either the designer who opened a staging link and felt their soul leave their body, or the developer who built exactly what the ticket described and got told it was wrong anyway. There is no third group. Both camps think the other one caused the problem, and they are both right, which is the most frustrating possible outcome.

The designer spent three weeks in Figma. Every spacing value is a multiple of 8. The type scale is harmonious. The interactions are annotated with little arrow diagrams that took longer to draw than the interaction itself. It is, genuinely, beautiful work. Then a developer translates it into code, and the staging build looks like someone described the design over a bad phone connection to a well-meaning cousin who tried their absolute best.

This is not because the developer is bad at their job. It is not because the designer lives in a fantasy world. It is because the design-to-development handoff has been broken for roughly as long as the web has existed, and the industry's best attempt at fixing it so far has been "write more detailed Jira tickets." We have been trying that approach for 20 years. It has not worked once. But someone in your organization will suggest it again next quarter, and everyone will nod politely, and nothing will change. We would bet money on this. We would bet a lot of money on this.

Why the Gap Exists

The gap between Figma and production is not a people problem. It is a tools problem, a mental models problem, and a physics problem, in roughly that order.

Figma and CSS do not think the same way. In Figma, everything is absolute. You place a frame, you set its dimensions, you position elements exactly where you want them. The design exists at one viewport size, in one state, looking exactly how you intended, because nothing is fighting you. Nothing moves. Nothing collapses. Nothing wraps. It is a beautiful, static lie.

CSS is the opposite of this. CSS is a negotiation between the content, the container, the viewport, the browser's interpretation of the word "auto," the ghost of every float-based layout from 2009 that still haunts the specification, and whatever Safari has decided to do differently this week. A designer draws a card that is 340px wide with 24px padding and a 16px gap. A developer builds that card in a responsive grid where the width is fluid, the image needs an aspect ratio to avoid collapsing into a 0px tall line of existential sadness, and the padding has to change on mobile because 24px on a 320px screen leaves approximately 12 pixels for actual content. The designer and the developer are building the same card. They are solving completely different problems. This is where the gap starts.

Responsive design is where "pixel-perfect" goes to quietly pass away. The mockup shows three breakpoints: desktop, tablet, mobile. Clean, orderly, sensible. The real world has approximately 847 screen sizes, and the design needs to not fall apart at any of them. That 8px grid system that looks immaculate at 1440px? At 1137px, something has to give. A gap gets weirdly wide. A card wraps to the next line earlier than expected. A heading that fit beautifully on one line at desktop becomes three lines on tablet, and the vertical spacing now looks like it was chosen by someone who has never seen the design.

Nobody designs for 1137px. Nobody should have to. But the build has to not look broken at 1137px anyway, and that is the developer's problem to solve with zero guidance and a lot of media queries.

Animations get cut. Every single time. The designer spec includes a beautiful stagger animation where cards fade up in sequence with a spring easing curve and a 0.15s delay between each item. It is gorgeous. It took three hours to spec. The developer looks at the remaining timeline, looks at the animation spec, looks at the remaining timeline one more time just to be sure, and ships it with opacity: 0 to 1, 300ms, ease-out. This is objectively the right call. The project ships on time. The animation is smooth and fast. And the designer will remember this betrayal for the rest of the engagement. We have been on both sides of this conversation. Neither side enjoys it.

The Telephone Game Problem

Even if CSS worked like Figma (it does not and it never will) and responsive design were straightforward (it is and always has been a trap), there would still be a more fundamental issue: the handoff itself is a game of telephone, and telephone has never once resulted in the original message arriving intact.

Designer creates a mockup. Designer explains the mockup to a product manager. PM writes a ticket. Developer reads the ticket. This is four steps, and each one loses fidelity like a JPEG that has been saved 15 times.

The designer's Figma file has hover states, focus states, loading states, empty states, error states, and a little annotation about what should happen when the user's name is longer than 30 characters. The PM's ticket says "Build the profile card (see Figma link)." The Figma link may or may not point to the correct frame. It was correct when the ticket was created three weeks ago. The designer has reorganized the file twice since then and renamed the page. The developer clicks the link, lands on a frame called "Profile Card v2 (OLD - DO NOT USE)," and builds that. Because it was the link in the ticket. Because the ticket is the source of truth. Except it was not.

We wish we were making this up for comedic effect. We are not. We have watched this exact scenario play out on projects with six-figure budgets and dedicated project managers whose entire job was preventing exactly this from happening.

The real loss is intent. The designer knows why the card has 32px bottom padding instead of 24px. The card sits above a section with a dark background, and the extra 8px creates visual breathing room at the transition. It is a deliberate, thoughtful design decision. The ticket says "padding: see Figma." The developer opens Figma, measures 32px, thinks "that does not match our spacing scale, probably a mistake," uses 24px, and ships it. Nobody is wrong. The designer had a reason. The developer had a system. The information just did not survive the trip. It rarely does.

What Actually Closes the Gap

We are going to tell you the answer, and you are going to think it is too simple, and then you are going to spend six months trying more complicated solutions that do not work before coming back to this one.

The designer and the developer need to be the same team. Not "aligned." Not "in close collaboration." Not "synced in a biweekly design review that everyone is 10 minutes late to." The same team, working on the same thing, at the same time, in the same room. Ideally the same person, but at minimum within shouting distance. Every custom web application we build works this way. Design and development happen under one roof because we tried the other way first and the staging links were genuinely upsetting.

When the person writing the CSS was in the room when the spacing decision was made, they know the 32px is intentional. They do not have to guess. They do not have to read a ticket that does not explain it. They were there. They might have been the one who suggested it. That is the whole secret to closing the design-to-dev gap. It is not a plugin. It is proximity.

Design tokens are the shared language. If your Figma file uses one set of values and your codebase uses another, you are maintaining two sources of truth. Two sources of truth is a polite way of saying zero sources of truth, because the moment they disagree (and they will disagree, around week three), nobody knows which one is correct and the argument that follows will last longer than the actual fix would have taken.

/* Figma variables become CSS custom properties */
/* One source. Same values. Zero arguments about whether it should be 24 or 32. */
--space-xs: 4px;
--space-sm: 8px;
--space-md: 16px;
--space-lg: 24px;
--space-xl: 32px;
 
--font-size-body: 1rem;
--font-size-heading-3: 1.25rem;
--font-size-heading-2: 1.5rem;
--font-size-heading-1: 2rem;
 
--color-surface-primary: #ffffff;
--color-surface-secondary: #f8f9fa;
--color-text-primary: #1a1a1a;
--color-border-default: #e2e2e2;

When the designer picks space-lg in Figma and the developer types var(--space-lg) in CSS, they are using the same value because it is literally the same token. There is nothing to misinterpret. There is nothing to lose in translation. The gap between "what was designed" and "what was built" is exactly zero pixels for every property that has a token. Which is why we tokenize everything. Everything. If it has a value, it has a token. We are not flexible about this and we are not sorry about it.

Component-level thinking from the start. Designers who think in pages create beautiful pages. Designers who think in components create beautiful design systems that produce beautiful pages at scale, across every page, forever. The difference is whether your Figma file has a "Homepage" frame or a "Card" component that happens to appear on the homepage. If the developer is building the Card component, and the designer designed the Card component, and they both agreed on the component's props and variants before anyone opened a code editor, then the handoff is not really a handoff. It is a shared understanding. Handoffs lose information. Shared understanding does not.

Storybook is the contract. We build every component in Storybook before it goes into a page. The designer can see the component at every variant, every state, every breakpoint, in a real browser, running real CSS, before it touches a layout. If it does not match, we fix it right there. Not after launch. Not in a "design polish sprint" that gets deprioritized four consecutive times and then quietly removed from the roadmap when nobody is looking. Right now, while we are both looking at the same screen.

The Figma-to-Code Workflow We Actually Use

Theory is nice. Here is the actual pipeline, in the order we actually do it, with the parts that actually matter.

Step 1: Figma variables, not raw values. Every spacing, color, font size, border radius, and shadow in the Figma file is a variable. Not a raw number that somebody typed in and hoped was correct. If the designer types "16" instead of selecting the space-md variable, we catch it in design review the same way we would catch a hardcoded string in a code review. This sounds strict because it is. It also eliminates an entire category of "is this 16px or 14px, I cannot tell from the mockup" conversations, and we do not miss those conversations at all.

Step 2: Variables export to CSS custom properties. The Figma variables map 1:1 to CSS custom properties. Same names. Same hierarchy. Same values. When the design changes, the token changes, and the code gets it automatically. There is no step where a developer manually squints at a Figma file, measures a pixel value, and types it into a CSS file while hoping they read the right layer. That step is where mistakes live. We evicted it.

Step 3: Components get props that match variants. If the Figma component has three variants (default, active, disabled), the React component has three variants. Same names. Same behavior. If a designer says "use the active variant" and a developer types variant="active", they are describing the exact same thing. This only works if you plan it from the beginning. Retrofitting variant names after the component is built and used on 14 pages is like trying to rename a child after they have started school. Technically possible. Logistically a nightmare.

Step 4: Storybook review before integration. Every component gets reviewed in Storybook against the Figma source. Side by side. In a browser. At mobile, tablet, and desktop viewports. If it does not match, we adjust. If it cannot match because CSS physics will not allow it (CSS physics are real and they are not negotiable), we adjust the design. This conversation happens once, at the component level, instead of 47 times after launch when someone screenshots the staging site next to the Figma file and sends it to a Slack channel called #design-review that everyone has muted.

When "Close Enough" Is Actually Fine

Not every pixel matters. This is heresy in some design circles, and we are saying it anyway because we have been doing this long enough to know it is true.

What matters: spacing rhythm, type hierarchy, color consistency, interaction feel. These are what users actually perceive. If your grid is consistent and your type scale creates clear hierarchy, the product feels designed. It feels intentional. Users cannot tell you why it feels good. They just know it does.

What does not matter: a 2px difference that no human eye will detect without a measurement tool. Whether the border radius is 6px or 8px on a card that appears exactly once on a settings page that 3% of users will ever visit. The precise easing curve on a hover transition that lasts 200 milliseconds. These details matter in Figma at 800% zoom, where you can measure anything against anything and declare victory or defeat. They do not matter at 100% in a browser, where the user is trying to finish checkout and is thinking about literally anything other than your border radius.

The mockup had a beautiful 8px grid system. The build has a padding: 13px that nobody can explain. You know what? The build looks great. Users are happy. The 13px exists because a line of text at that particular font size and line height created an optical misalignment at 16px that looked objectively worse than the "wrong" number. Sometimes the mathematically correct value is not the visually correct value, and a good developer knows to trust their eyes over the spec. A great designer knows this too, and appreciates it instead of filing a bug report about 3 pixels.

The goal is not "identical to Figma." The goal is "faithful to the design intent." Those sound similar. They produce very different products, and the second one is better.

The Real Fix

Stop treating design and development as separate phases with a handoff in between. They are the same work, done by people who need to understand each other's constraints, using shared tools and shared language.

The handoff problem is not a tooling problem. Figma plugins will not solve it. AI-powered design-to-code tools will not solve it, although they will definitely have a Product Hunt launch that claims otherwise. A more detailed Jira ticket template will absolutely not solve it, although someone on your team will create one with 14 required fields and everyone will fill out 3 of them. We have seen this play out enough times to set our watch by it.

The fix is structural. The people making visual decisions and the people implementing those decisions need to be close enough that a question gets answered in 30 seconds, not in a ticket comment three business days later. Shared tokens, shared components, shared context for why things are the way they are.

We know this works because we build this way. Not because we read a Medium article about cross-functional collaboration and thought it sounded nice. Because we tried the handoff approach for years, and it produced staging links that made our designers stare silently at their monitors in a way that was uncomfortable for everyone in the room. Now design and development happen together, on the same team, with the same tokens, reviewing components together in Storybook. The staging links look like the designs. Not because we found a trick. Because we eliminated the game of telephone that was breaking everything, and it turns out that was the only thing that needed to happen.

That is the whole fix. It is not a tool. It is not a process. It is certainly not a Jira ticket template. It is just putting the right people in the same room and letting them solve the problem together instead of throwing files over a wall and hoping for the best. Every organization wants a solution that does not require changing the org chart. That solution does not exist. We looked.