Design to code

Figma auto layout to flexbox: a practical CSS mapping

Map Figma auto layout to flexbox: direction, gap, padding, alignment, Hug, Fill and Fixed, with a full table, a worked card example and common pitfalls.

7 min read

Figma auto layout maps closely to CSS flexbox: a horizontal or vertical auto layout frame is a flex container with flex-direction: row or column, the gap is gap, the padding is padding, and the alignment box sets justify-content and align-items. The parts that need care are sizing (Hug, Fill and Fixed) and the defaults, which differ between the two.

Below: the full mapping, the alignment and sizing rules behind most bugs, a worked example, and how to hand this to a coding agent.

The mapping table

Figma auto layout CSS
Horizontal display: flex; flex-direction: row
Vertical display: flex; flex-direction: column
Wrap flex-wrap: wrap
Gap (a number) gap
Gap Auto: Between justify-content: space-between
Gap Auto: Around justify-content: space-around
Gap Auto: Evenly justify-content: space-evenly
Padding padding
Alignment box justify-content and align-items, depending on direction
Baseline alignment align-items: baseline
Hug contents Content size: the flex item default, or width: fit-content
Fill container flex: 1 and min-width: 0 on the main axis, align-self: stretch on the cross axis
Fixed width or height width or height, plus flex-shrink: 0
Minimum and maximum width or height min-width, max-width, min-height, max-height
Ignore auto layout position: absolute on the child, position: relative on the frame
Grid display: grid, a different mapping

The Auto gap options line up exactly. Between puts no space at the edges, like space-between. Around makes each gap twice the edge space, which is what space-around does with its half-size space at each end. Evenly spreads equal space across gaps and edges, like space-evenly. MDN’s basic concepts of flexbox describes each value.

Direction decides what alignment means

Figma’s alignment box shows nine positions, from top left to bottom right, whatever the direction. CSS has no such box: it has a main axis (the direction of the flow) and a cross axis, each with its own property.

  • justify-content aligns along the main axis.
  • align-items aligns along the cross axis.

So the same Figma position gives different CSS depending on direction:

Figma position Horizontal frame Vertical frame
Top left justify-content: flex-start; align-items: flex-start justify-content: flex-start; align-items: flex-start
Top center justify-content: center; align-items: flex-start justify-content: flex-start; align-items: center
Right justify-content: flex-end; align-items: center justify-content: center; align-items: flex-end
Bottom center justify-content: center; align-items: flex-end justify-content: flex-end; align-items: center

The other trap is the default. A new flex container has align-items: stretch, so its children stretch across the cross axis. In a vertical frame, a button that hugs its label in Figma becomes full width in CSS. Set align-items explicitly on every container, or give that child align-self: flex-start.

Hug, Fill and Fixed in CSS

Sizing is where most “almost right” layouts come from. Each Figma setting applies to one axis, so translate width and height separately.

Hug contents

A flex item sizes to its content on the main axis by default (flex: 0 1 auto), so a hugging child in a row often needs nothing. Three cases need code:

  • A container that hugs. display: flex makes a block-level box that takes the full width of its parent. Use width: fit-content, or display: inline-flex.
  • A hugging child on the cross axis. It stretches because of align-items: stretch. Use align-self: flex-start, or the alignment you want.
  • A hugging child in a crowded row. Flex items can shrink by default (flex-shrink: 1). Add flex-shrink: 0 when it must keep its size.

Fill container

On the main axis, Fill means “take the remaining space”: flex: 1, which expands to flex: 1 1 0% (see MDN on the flex shorthand). Add min-width: 0 in a row, or min-height: 0 in a column. The automatic minimum size of a flex item is its min-content size, so a long word or a wide table inside a filling child pushes it wider than its share instead of letting it shrink or truncate.

On the cross axis, Fill is align-self: stretch, which is also the default of align-items. It only works when the item has no fixed size on that axis: with a set height in a row, the item keeps its height.

Fixed

A fixed width is width (or flex-basis) plus flex-shrink: 0. Without it, the default flex-shrink: 1 lets the browser squeeze your 120 px button when the row runs out of space, which a Fixed layer in Figma never does.

Padding, strokes and box sizing

A Figma frame’s width is its outer width: the padding sits inside it. CSS uses content-box by default, where width covers the content only and padding and border are added on top. A card set to 320 px with 16 px of padding is then 352 px wide. Use border-box everywhere:

*, *::before, *::after {
  box-sizing: border-box;
}

With border-box, the width includes padding and border, as in Figma.

Strokes need one more decision. Figma lets you include an inside stroke in the layout or exclude it. A CSS border always takes space, so it matches “included”. For “excluded”, draw the line without moving anything: an outline, which never takes up space, or box-shadow: inset 0 0 0 1px, which does not change the size of the box.

Gap behaves the same in both tools: space between items only, none before the first item or after the last. With wrap on, gap accepts two values: the gap between rows, then the gap between columns.

A worked example: a card

A card specified in Figma:

  • Card: Vertical, padding 16, gap 12, width Fixed 320, alignment Top left.
  • Header: Horizontal, gap 8, alignment Left, width Fill.
  • Title: text, width Fill, truncated on one line.
  • Badge: Hug.
  • Button: Hug.

In CSS, with spacing tokens:

.card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;   /* Top left: children hug instead of stretching */
  gap: var(--space-3);       /* 12 */
  padding: var(--space-4);   /* 16 */
  width: 320px;              /* Fixed */
}

.card-header {
  display: flex;
  align-items: center;       /* Left: vertically centered */
  gap: var(--space-2);       /* 8 */
  align-self: stretch;       /* Fill the width of the card */
}

.card-title {
  flex: 1;                   /* Fill */
  min-width: 0;              /* lets a long title shrink */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card-badge {
  flex-shrink: 0;            /* Hug, never squeezed */
}
<article class="card">
  <div class="card-header">
    <h3 class="card-title">Quarterly report for the north region</h3>
    <span class="card-badge">Draft</span>
  </div>
  <p>Due Friday, shared with 4 people.</p>
  <button>Open</button>
</article>

The align-items: flex-start on the card is the line most translations miss: without it, the button stretches to 288 px. Tokens for gap and padding keep the numbers tied to your scale. See design tokens an AI coding agent can follow.

Handing auto layout to a coding agent

A coding agent translates auto layout well when it gets the properties as text instead of guessing them from a picture. For each frame, give the direction, gap, padding, alignment, and the sizing of each child on both axes. Then state the mapping:

Implement this card with flexbox. Mapping: Horizontal or Vertical = flex-direction
row or column, gap = gap, padding = padding, Fill = flex: 1 plus min-width: 0 on the
main axis and align-self: stretch on the cross axis, Hug = content size with
flex-shrink: 0, Fixed = width plus flex-shrink: 0. Set align-items explicitly on
every container. Use box-sizing: border-box and the spacing tokens in src/styles/tokens.css.

The mapping also works the other way. In agentappbuilder, the Design panel of the canvas uses Figma’s names on HTML prototypes: for an element laid out with flexbox, Auto layout sets the direction, the gap, the alignment on a 3 x 3 grid, space between and wrap, next to padding, fill, stroke and corner radius. Each change is written as an inline style on the element, and your agent receives the list of properties you changed, with the values before and after, to implement in your code. See edit a frame for the panel, implement your edits for the prompt, and edit a prototype visually, then let the agent code it for the whole workflow.

Summary

Treat each auto layout frame as a flex container: direction becomes flex-direction, gap and padding stay as they are, and the alignment box splits into justify-content and align-items according to direction. Translate sizing per axis: Hug is content size, Fill is flex: 1 with min-width: 0 or align-self: stretch, Fixed is a size with flex-shrink: 0. Set box-sizing: border-box and explicit alignment, and most “almost right” layouts go away.

Frequently asked questions

Is Figma auto layout the same as CSS flexbox?

Close to it. Horizontal and vertical auto layout behave like a flex container with flex-direction row or column, gap and padding. Sizing and default alignment differ, so map Hug, Fill and Fixed explicitly.

What is Fill container in CSS?

On the main axis, flex: 1 with min-width: 0 (min-height: 0 in a column). On the cross axis, align-self: stretch, which only works when the item has no fixed size on that axis.

What is Hug contents in CSS?

Content size. A flex item already sizes to its content on the main axis, and flex-shrink: 0 stops it from shrinking. A container on its own needs width: fit-content or display: inline-flex, or it takes the full width.

Try it on your own app

Tell Claude Code one sentence. It creates the project and gives you a link to see your screens.

Tell Claude Code:

Connect this project to agentappbuilder.com and show me its screens.