From Figma to Code: Why Auto Layout Is Just Flexbox (And Why You Should Use gap over margin)
This is the first in a two-part series for designers and frontend developers, drawn from one of the most valuable lessons I've picked up translating hundreds of web application components from Figma into HTML and CSS.
Designers have all too often had to do extra legwork to protect their designs from incorrect spacing — spacing that renders their thoughtfully crafted negative space and design rhythm just slightly off, in that hard-to-articulate way that makes something feel not quite right. What follows is usually additional effort: reviewing screens and components, annotating screenshots with spacing corrections, or waving through a UI with a nagging sense that it lacked the attention to detail needed to feel as premium as the original designs.
As features near completion and designers are asked to feedback on the implementation, there is a risk that there will be extra work needed to address design inconsistencies and in our case inaccurate spacing — the kind that make thoughtfully crafted negative space and rhythm feel slightly off in that hard-to-articulate way. What usually follows: reviewing the screen or components and painstakingly annotating corrections on screenshots.
As developers, receiving feedback in this form is painful. We find ourselves manually sifting through annotated screenshots, trying to dig for the actual intention behind the comment. Did I understand this correctly? Is this what they meant? Worst of all, there may eventually be a compromise with a UI going into production with the lingering sense it lacks the attention to detail that made the original designs feel premium.
If you have experienced this while implementing a design from Figma, you are not alone. But the question I keep coming back to is this: if closing the gap between design intent and implementation is one of the core problems Figma set out to solve, why do we still so often see feature development cycles end with a round of manual designer QA — right at the moment you thought you were done and ready to move on?
Put simply, Figma's Auto Layout is flexbox. Not inspired by flexbox, not analogous to it — it is, in nearly every meaningful way, the same layout model wrapped a designer-friendly UI.
The thesis I want to set out here is that this isn't a failing of Figma as a product — it's an often missing piece of understanding among both designers and engineers about one of the tools Figma already offers to solve exactly this problem. The answer is more prosaic than you might expect, and it lies in Auto Layout: a feature that is, intentionally in my view, a near-exact mapping to CSS flexbox properties.
It's one of those things that, once you see it, you can't unsee — and it fundamentally changes how you approach the design-to-code workflow. Put simply, Figma's Auto Layout is flexbox. Not inspired by flexbox, not analogous to it — it is, in nearly every meaningful way, the same layout model wrapped in a designer-friendly UI.
Once you internalise this, the translation from design to code stops feeling like legwork and starts feeling like transcription. And we can develop this understanding into a rule: if your spacing comes from Auto Layout in Figma, it should come from
gap in CSS — not margins.Let's walk through the mapping, property by property.
The Direct Property Mapping Direction → flex-direction
In Figma, every Auto Layout frame has a direction: Horizontal or Vertical. In CSS:
/* Figma Horizontal */
flex-direction: row;
/* Figma Vertical */
flex-direction: column;CSSThis is the first and most important conceptual bridge. Figma treats horizontal and vertical stacking as the same type of thing — a sequence of items along an axis. CSS does too. The direction just changes which axis is the primary one.
Alignment → align-items and justify-content
Figma exposes two alignment controls on an Auto Layout frame: alignment along the primary axis (the direction items flow) and alignment along the counter axis (perpendicular to flow).
/* Figma: Align items — Start, Center, End */
align-items: flex-start | center | flex-end;
/* Figma: Justify content — Start, Center, End, Space Between */
justify-content: flex-start | center | flex-end | space-between;CSSThe naming differs slightly — Figma calls the primary-axis control "Spacing" and the counter-axis one "Alignment" — but the underlying model is identical.
Spacing Between Items → gap
This is the heart of it. When you set a spacing value between items in a Figma Auto Layout frame, you are setting
gap./* Figma: Gap 16 */
gap: 16px;
/* Figma: Gap 16 on a vertical layout */
gap: 16px; /* same property, value may be px, em, rem etc */CSSIt doesn't matter whether the layout is horizontal or vertical. Figma doesn't distinguish, and neither does
gap. The property works identically on both flex-direction: row and flex-direction: column.
Padding → padding
Auto Layout frames support padding on all four sides, and this maps directly to CSS padding on the container:
/* Figma: Padding Top/Right/Bottom/Left: 16 24 16 24 */
padding: 16px 24px;CSSWrapping → flex-wrap
Figma's "Wrap" option in Auto Layout maps to:
flex-wrap: wrap;CSSWhen set, Figma also exposes a second gap value for the cross-axis — this maps to the two-value form of
gap:/* Figma: Horizontal gap 16, Vertical gap 8 (in a wrapping layout) */
gap: 8px 16px; /* row-gap column-gap */CSSFill Container vs. Hug Contents → flex-grow and width/height
Each child in a Figma Auto Layout frame has a sizing behavior. "Fixed" means a set width or height. "Hug contents" means the element sizes to fit its children — the default shrink-wrap behavior of a flex item. "Fill container" is the big one:
/* Figma: Fill container (on a child element) */
flex: 1;
/* or more explicitly: */
flex-grow: 1;CSSThis is one of the most common Figma → CSS translations and also one of the most commonly mishandled. "Fill container" in Figma is not
width: 100%. It's flex-grow: 1, which means "take up remaining space within the flex container."
A Complete Example
Here's a typical card component as it might be structured in Figma Auto Layout, and its CSS equivalent:
Figma structure:
- Frame: Vertical, Gap 16, Padding 24, Width 384
- Image container - Fill container (width), Hug (height), Horizontal, Center Top
- Image: Fixed 6rem×6rem
- Header - Fill container (width), Hug (height), Vertical, Center Top
- Heading: Text Node
- Tags - Fill Container (width), Hug (Height), Horizontal, Center Top, gap 4, Wrap
- Tag - Hug (width), Hug (Height), Horizontal, Center Center
- Tag Text: Text Node
- Body: Fill container (width), Hug (height), Horizontal, Top Left
- Body Text: Text Node
- Button: Fill container (width), Hug (height), Horizontal, Center Top
CSS:
main {
padding: 1rem;
display: flex;
justify-content: center;
}
.card {
border-radius: 0.5rem;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1.5rem;
width: 24rem;
}
.card__image-container {
display: flex;
justify-content: center;
flex: 1;
}
.card__image {
border-radius: 50%;
height: 6rem;
object-fit: cover;
width: 6rem;
}
.card__header {
align-items: center;
display: flex;
flex: 1;
flex-direction: column;
}
.card__title {
font-size: 1.25rem;
font-weight: 7x00;
line-height: calc(1.75 / 1.25);
}
.card__tag-container {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: center;
}
.tag {
align-items: center;
background-color: #e7e7e7;
border-radius: 0.375rem;
display: inline-flex;
font-size: 0.75rem;
font-weight: 500;
justify-content: center;
line-height: calc(1 / 0.75);
padding: 0.125rem 0.5rem;
text-align: center;
}
.card__body {
flex-grow: 1;
}
.card__body-text {
text-align: center;
font-size: 0.875rem;
line-height: calc(1.25 / 0.875);
}
.card__button {
background: #000;
border-radius: 0.375rem;
color: #f5f5f5;
display: inline-flex;
justify-content: center;
line-height: calc(1.25 / 0.875);
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 500;
}
CSSHTML:
<main>
<div class="card">
<div class="card__image-container">
<img
src="https://images.unsplash.com/photo-1620049644455-278ce458370f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtYWxlJTIwYXV0aG9yJTIwd3JpdGVyJTIwcG9ydHJhaXQlMjBwcm9mZXNzaW9uYWx8ZW58MXx8fHwxNzcxMjc1MjYwfDA&ixlib=rb-4.1.0&q=80&w=1080"
alt="{authorName}"
class="card__image"
/>
</div>
<div class="card__header">
<h2 class="card__title">James Anderson</h2>
</div>
<div class="card__tag-container">
<span class="tag">Mystery</span>
<span class="tag">Thriller</span>
<span class="tag">Crime Fiction</span>
</div>
<div class="card__body">
<p class="card__body-text">
Award-winning author with over 15 years crafting
gripping psychological thrillers. Known for intricate plot twists, compelling characters that keep readers on the edge of their
seats.
</p>
</div>
<button class="card__button" type="button">Learn More</button>
</div>
</main>HTMLWhy `gap` — and Why It Matters Even for Vertical Layouts
Here's where convention and best practice tend to diverge. Many developers reach for margins when they want spacing between elements. A list of items?
margin-bottom on each one, perhaps with :last-child { margin-bottom: 0 } to remove it. A row of buttons? margin-right on each, minus the last.This works, but it creates a subtle problem: spacing becomes the responsibility of the child, not the parent. Each child has to know where it sits in the layout and apply compensation accordingly. This makes layouts brittle. Reorder the items, add a new one, remove the last one — and suddenly your spacing logic breaks.
every layout decision in Figma has a direct, mechanical equivalent in CSS. Nothing is lost in translation.
gap solves this at the source. The container owns the spacing, and the children are unaware of it. You don't need :last-child workarounds. You don't need to worry about collapsing margins. You don't need to remember which direction to apply the margin. The container simply says: "put this much space between every item I contain."
This is especially valuable in vertical layouts, where developers are most likely to default to
margin-bottom. The instinct is understandable — vertical flow feels like document flow, and margins feel natural there. But once you're using display: flex; flex-direction: column, you're not in document flow anymore. You're in a flex formatting context, and gap is the right tool. This is the biggest cognitive barrier I had to overcome after years of simply replying on document flow for vertical layouts. Now? I use flex by default for laying out content both vertically and horizontally./* ❌ The old way: children responsible for spacing */
.stack > * {
margin-bottom: 16px;
}
.stack > *:last-child {
margin-bottom: 0;
}
/* ✅ The flexbox way: container responsible for spacing */
.stack {
display: flex;
flex-direction: column;
gap: 16px;
}CSSThe second version is shorter, more predictable, and accurately reflects how the design was specified in Figma. To think about this this another way, every layout decision in Figma has a direct, mechanical equivalent in CSS. Nothing is lost in translation.
The Consistency Argument
There's also a readability and consistency argument. When you adopt
gap as the universal spacing mechanism between flex children — regardless of whether the layout is horizontal or vertical — you get a codebase where all inter-element spacing reads the same way. A designer looking at Figma and a developer looking at CSS are looking at the same mental model. The gap is the gap.
Mixing in margins for vertical layouts and
gap for horizontal ones introduces an inconsistency that has no real benefit. It's a holdover from a pre-flexbox world where column layouts were built in document flow. Once you commit to flexbox for all layouts, you should commit to gap for all spacing.Browser Support Is a Non-Issue
If you've avoided
gap on flexbox in the past due to browser support concerns, that ship has long sailed. The gap property for flexbox has had broad support across all major browsers since mid-2021. There is no longer a practical reason to avoid it.The Takeaway
Figma Auto Layout and CSS flexbox are the same layout model. Learning to see them as equivalent — not analogous, but equivalent — removes a major source of friction from the design-to-code workflow. The direction, alignment, padding, wrapping, and spacing you see in Figma have direct, one-to-one counterparts in CSS.
And when it comes to spacing, the lesson Figma has already taught us is the right one: spacing belongs to the container, not the child. Whether your layout is horizontal or vertical,
gap is the correct, consistent, and intentional way to express the space between your elements. Let the layout own its own spacing. Your CSS will be cleaner, your components more composable, and your translation from design to code a whole lot more faithful.