Figma to Tailwind: The Complete Conversion Workflow
How to turn a full Figma design into clean HTML + Tailwind CSS — reading Dev Mode, mapping tokens and Auto Layout to Tailwind, and a worked hero-section example with complete code.
Converting a Figma file to code goes wrong in a very specific way: you copy
pixel values one element at a time — margin-top: 37px, font-size: 15px,
color: #6E56CF — and end up with markup that looks right and is impossible
to maintain. The professional workflow is different: you translate the
design’s system, then express each screen in that system. Tailwind is
ideal for this because it is a design system in utility form. Here’s the
whole workflow, then a real conversion.
Step 0: Read the design like an engineer
Before writing any markup, spend ten minutes in Dev Mode (or plain inspect) answering four questions:
- What’s the spacing rhythm? Click around — if gaps are 8/16/24/32/48,
the file is on an 8-pt grid and maps straight onto Tailwind’s default scale
(
gap-2/4/6/8/12). If you see 13px and 27px, round them — odd values are almost always designer drift, not intent. - What are the real colors? Check the file’s Variables/Styles panel,
not individual fills. A good file has ~6–10 semantic tokens
(
bg,surface,text-primary,accent…). Those become your theme. - What’s the type scale? List every text style actually used — usually 5–7 (display, h2, h3, body, small, caption). Each becomes a utility combo you reuse, not a per-element font-size.
- What repeats? Cards, buttons, badges, nav items. Repetition = a component; you’ll build each once.
Step 1: Tokens → Tailwind theme
Move the file’s variables into Tailwind v4’s CSS-first config so every utility speaks the design’s language:
/* app.css */
@import 'tailwindcss';
@theme {
/* Figma color variables, verbatim */
--color-bg: #0b0d12;
--color-surface: #12151c;
--color-border: #232733;
--color-fg: #e8eaf0;
--color-fg-muted: #9aa1b2;
--color-accent: #6e56cf;
--color-accent-soft: #6e56cf1f;
/* Type + radius from the styles panel */
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
--radius-card: 1rem; /* Figma corner radius 16 */
}
Now bg-surface, text-fg-muted, border-border, rounded-card exist as
utilities. When the designer changes a variable, you change one line. This
step is what separates a conversion from a transcription.
Step 2: Auto Layout → Flexbox (the mechanical part)
Figma’s Auto Layout is flexbox with different names. The mapping is 1:1:
| Figma inspector says | Tailwind |
|---|---|
| Direction: horizontal / vertical | flex / flex flex-col |
| Gap between items: 16 | gap-4 |
Padding: 24 / 24 16 | p-6 / py-6 px-4 |
| Alignment: center | items-center justify-center |
| Distribution: space between | justify-between |
| Hug contents | (default — don’t set a size) |
| Fill container | flex-1 or w-full |
| Fixed width: 384 | w-96 (only when truly fixed) |
| Corner radius: 16 | rounded-2xl |
| Drop shadow (soft, large) | shadow-lg (tune once in @theme) |
Read each frame’s Auto Layout panel top-down and type the utilities in the same order. Resist absolute positioning — if the designer used Auto Layout, the layout is flex; if they didn’t, impose the grid they were eyeballing.
Rounding rule: Figma says 37 → use 9 (36px)? No — snap to the nearest scale
step (gap-9 exists, but gap-8/32 or gap-10/40 are likelier the intent).
Consistency beats fidelity-to-the-pixel; nobody can see 3px, everyone can see
inconsistency.
Step 3: A real conversion — hero section
Say Dev Mode shows this for a landing hero:
Frame "Hero" — vertical Auto Layout, gap 24, padding 96/24, items centered
├─ Badge — horiz. layout, gap 8, padding 6/14, radius full,
│ fill accent @ 12%, text 13/medium accent
├─ Heading — Inter 56/64, -2% tracking, weight 700, center, max-w 640
├─ Subtext — Inter 18/28, fg-muted, center, max-w 480
└─ Actions — horiz. layout, gap 12
├─ Button "Start free" — padding 12/24, radius 10, fill accent, 15/semibold
└─ Button "See demo" — same, but border 1px border, fill transparent
The translation, line for line:
<section class="flex flex-col items-center gap-6 px-6 py-24 text-center">
<!-- Badge: 6/14 padding → py-1.5 px-3.5; 12% accent fill → accent-soft token -->
<span class="flex items-center gap-2 rounded-full bg-accent-soft px-3.5 py-1.5
text-[13px] font-medium text-accent">
✦ New: v2 is out
</span>
<!-- 56/64 with -2% tracking → text-5xl/tight + tracking-tight, capped width -->
<h1 class="max-w-2xl text-5xl leading-[1.15] font-bold tracking-tight">
Ship interfaces straight from the design file
</h1>
<!-- 18/28 muted → text-lg + relaxed leading, narrower cap -->
<p class="max-w-md text-lg leading-7 text-fg-muted">
Convert Figma into clean, responsive Tailwind — without losing the system
behind the pixels.
</p>
<div class="flex flex-wrap items-center justify-center gap-3">
<a href="#" class="rounded-[10px] bg-accent px-6 py-3 text-[15px] font-semibold
text-white transition-colors hover:bg-accent/90">
Start free
</a>
<a href="#" class="rounded-[10px] border border-border px-6 py-3 text-[15px]
font-semibold transition-colors hover:bg-surface">
See demo
</a>
</div>
</section>
Notice what happened: every Figma property became a utility in the same
order the inspector listed it, odd values snapped to the scale, and the two
Figma-specific numbers worth keeping exactly (radius 10, text 15) used
arbitrary values rounded-[10px] / text-[15px] — sparingly, deliberately.
Step 4: Responsiveness — the part Figma doesn’t give you
A Figma file is usually 1–2 fixed artboards (1440 and maybe 390). The breakpoints in between are your design decisions. The reliable recipe:
- Build mobile-first from the 390 frame, then layer
md:/lg:utilities to reach the 1440 frame:text-4xl md:text-5xl,flex-col lg:flex-row,px-6 lg:px-8,py-16 lg:py-24. - Grids collapse by column count:
grid gap-6 sm:grid-cols-2 lg:grid-cols-3handles 90% of card layouts with no extra thought. - When only one artboard exists, derive the mobile version yourself: stack horizontals, halve big paddings, step display text down one size. Show the designer — this is collaboration, not guessing.
Pitfalls that mark a rushed conversion
- Absolute-positioning the layout because it matched the canvas. It shatters on the first real content change.
h-[812px]screens — Figma frames have fixed heights; web pages don’t. Height comes from content; usemin-h-screenonly for true full-bleed heros.- Baked-in text images — retype it; export only real artwork (SVG for icons/logos, compressed raster for photos).
- Skipping states — Figma shows the resting state. Hover, focus-visible,
disabled, and empty states are code’s job: add
hover:,focus-visible:rings, anddisabled:opacity-50everywhere interactive. - A hundred one-off arbitrary values —
mt-[37px] w-[213px]everywhere means step 0 was skipped. Go back and find the system.
For full worked components — navbar, pricing card, stats grid, and an AI-assisted workflow — continue to Figma to Code: worked examples.