From Figma to Code: How I Close the Design–Engineering Gap

Damola Oladipo
··10 min read

The handoff between design and engineering is where quality goes to die. Here's the workflow I use to keep intent intact from the first frame to the final commit.

RustProduct ThinkingOpen Source
From Figma to Code: How I Close the Design–Engineering Gap

The design–engineering gap isn't a people problem. It's a process problem. Designers make decisions in a tool that doesn't run in a browser. Engineers implement in a tool that doesn't think in pixels. Somewhere between the two, detail gets lost, intent gets diluted, and the product that ships looks like a photocopy of a photocopy of the original design.

I've spent a lot of time on both sides of that gap. Here's the workflow that's helped me close it.

Start With Decisions, Not Screens

The most common mistake I see in design processes is going straight to high-fidelity screens before the decisions that underpin them have been made.

What decisions need to be made first?

  • What are the states this component can be in?
  • What happens at each breakpoint?
  • What's the empty state? The error state? The loading state?
  • What are the edge cases — long text, no data, slow network?

A Figma frame shows the happy path. The work is in the states you didn't design for. I use a simple checklist before marking any component design as complete:

□ Default / rest state
□ Hover / focus state
□ Active / pressed state
□ Loading state
□ Empty state
□ Error state
□ Mobile (375px)
□ Tablet (768px)
□ Desktop (1280px)
□ Long content (50+ char labels, overflow)
□ No content (zero items in list)

If any of these are unresolved, the design isn't done.

Design Tokens First

Design tokens are the contract between design and engineering. They're the named values — colours, spacing, typography scales, border radii — that both sides reference.1

In Figma, I use variables to define tokens. In code, I use CSS custom properties that map to the same names:

/* CSS tokens — mirror Figma variable names exactly */
:root {
    --color-foreground: #0a0a0a;
    --color-background: #fafafa;
    --color-muted: #f4f4f5;
    --color-border: #e4e4e7;

    --space-1: 4px;
    --space-2: 8px;
    --space-4: 16px;
    --space-6: 24px;
    --space-8: 32px;

    --radius-sm: 6px;
    --radius-md: 12px;
    --radius-lg: 20px;
}

When the designer changes the primary colour in Figma variables and the engineer updates the CSS custom property, every component using that token updates automatically. This is what makes large-scale changes safe instead of terrifying.

Component Anatomy Before Implementation

Before I write a single line of component code, I map the anatomy:

Props: What inputs does this component need? What's required vs. optional?
Variants: What visual variations exist? (Size, intent, state)
Slots: Where does content get injected? Children, icons, labels?
Behaviour: What interactions does it have? What events does it emit?

This sounds like documentation overhead. It's actually faster than discovering these questions mid-implementation and having to refactor.

interface ButtonProps {
    // Visual variants — map directly from Figma component properties
    intent?: 'primary' | 'secondary' | 'ghost' | 'destructive';
    size?: 'sm' | 'md' | 'lg';

    // Content slots
    children: React.ReactNode;
    leftIcon?: React.ReactNode;
    rightIcon?: React.ReactNode;

    // State
    isLoading?: boolean;
    disabled?: boolean;

    // HTML passthrough
    type?: 'button' | 'submit' | 'reset';
    onClick?: () => void;
}

When Figma component properties map directly to TypeScript props, the handoff is a translation, not an interpretation.

Responsive Implementation

Responsive design should be designed, not improvised in code. Before I implement any component, I want to see how it behaves at every breakpoint — not just the two extremes.

My breakpoint scale:

const breakpoints = {
    sm: '640px', // Large phones
    md: '768px', // Tablets
    lg: '1024px', // Small laptops
    xl: '1280px', // Desktops
    '2xl': '1536px', // Large screens
};

With Tailwind, responsive variants are explicit and co-located:

<div className="
  flex flex-col        /* mobile: stack */
  md:flex-row          /* tablet: side by side */
  gap-4 md:gap-8       /* spacing scales up */
  px-4 md:px-8 lg:px-16  /* padding scales up */
">

The rule I follow: design for mobile first, then add complexity at larger breakpoints. It's easier to add than to subtract.

The QA Pass

Before any design handoff is considered complete, I do a QA pass against the implementation. My checklist:

Visual accuracy

  • Spacing matches (use the browser inspector's box model)
  • Typography matches (size, weight, line height, letter spacing)
  • Colours match (not "close enough" — use the eyedropper)
  • Border radius, shadow, opacity match

Behaviour

  • All hover/focus states implemented
  • Keyboard navigation works
  • Touch targets are adequate on mobile
  • Animations match the design (duration, easing)

Edge cases

  • Long content doesn't break layout
  • Empty states are handled
  • Error states are handled
  • Works with browser default font size increased to 200%

Accessibility

  • Colour contrast passes WCAG AA
  • Focus indicators are visible
  • ARIA labels on icon-only buttons
  • Logical reading order in the DOM

This is thorough because quality compounds. A component that's 90% right is a tax on every place it's used.2

The Mindset Shift

The biggest change in my practice has been thinking of Figma and code as two representations of the same thing, not as separate artifacts.

When I design, I'm thinking about how this will be implemented. When I implement, I'm thinking about whether the detail from the design is preserved. The translation is always lossy — the goal is to minimise the loss.

Design engineering isn't about being good at two things. It's about understanding the gap between them and caring enough to close it.

Footnotes

  1. The concept of design tokens was popularised by Salesforce with their Lightning Design System in 2014. The W3C Design Tokens Community Group is currently working on a standard format for design token files (.json), which would allow tokens to be shared across tools without manual translation.

  2. This is the compound interest argument for quality. A button component that renders correctly in 90% of cases will produce visible inconsistencies in every product surface that uses it. Fix the component once, and you fix every instance simultaneously. The QA investment pays back immediately and continues paying indefinitely.

Our Newsletter

Subscribe now so you don't miss any of the latest updates, and you'll also receive a 20% discount code.

Agree Terms and Conditions

Damola Oladipo - Product and Design Engineer exploring ML and NLP research.