learn.aathan.in

Figma to Code: Worked Examples (Navbar, Cards, Stats Grid)

Three complete Figma-to-Tailwind conversions — a responsive navbar, a pricing card, and a dashboard stats grid — each from inspector specs to final HTML, plus the AI-assisted workflow.

The workflow doc covers the method — tokens first, Auto Layout → flexbox, snap to the scale. This one is pure practice: three components you’ll meet in almost every Figma file, converted end to end. Each starts from what the inspector actually shows and ends in complete, responsive HTML. All of them assume the @theme tokens from the workflow doc (bg, surface, border, fg, fg-muted, accent).

Example 1: Responsive navbar

What the inspector shows:

Frame "Nav" — horiz. Auto Layout, justify space-between, items center,
              padding 16/32, fill surface @ 80% + background blur,
              bottom border 1px border
├─ Logo — horiz. layout, gap 10 (mark 28×28 + wordmark 17/semibold)
├─ Links — horiz. layout, gap 32, text 15 fg-muted
└─ Actions — horiz. layout, gap 12 ("Log in" ghost + "Sign up" filled)

The mobile artboard collapses Links/Actions behind a hamburger. Translation — the one non-obvious choice is doing the mobile menu with zero JavaScript via the checkbox trick (a <details> or a peer checkbox both work; peer keeps the layout simplest):

<header class="sticky top-0 z-40 border-b border-border bg-surface/80 backdrop-blur">
  <nav class="mx-auto flex max-w-6xl items-center justify-between px-4 py-4 sm:px-8">
    <a href="/" class="flex items-center gap-2.5 font-semibold">
      <span class="grid h-7 w-7 place-items-center rounded-lg bg-accent text-sm text-white">A</span>
      Acme
    </a>

    <!-- Desktop links: hidden below md, exactly the Figma gap-8 (32px) -->
    <ul class="hidden items-center gap-8 text-[15px] text-fg-muted md:flex">
      <li><a class="transition-colors hover:text-fg" href="#">Product</a></li>
      <li><a class="transition-colors hover:text-fg" href="#">Pricing</a></li>
      <li><a class="transition-colors hover:text-fg" href="#">Docs</a></li>
    </ul>

    <div class="hidden items-center gap-3 md:flex">
      <a href="#" class="px-3 py-2 text-[15px] font-medium text-fg-muted hover:text-fg">Log in</a>
      <a href="#" class="rounded-lg bg-accent px-4 py-2 text-[15px] font-semibold text-white hover:bg-accent/90">
        Sign up
      </a>
    </div>

    <!-- Mobile: peer checkbox drives the drawer, no JS -->
    <input id="nav-toggle" type="checkbox" class="peer sr-only" />
    <label for="nav-toggle" class="cursor-pointer p-2 md:hidden" aria-label="Menu">☰</label>
    <div class="absolute inset-x-0 top-full hidden flex-col gap-1 border-b border-border
                bg-surface p-4 peer-checked:flex md:peer-checked:hidden">
      <a class="rounded-lg px-3 py-2 hover:bg-bg" href="#">Product</a>
      <a class="rounded-lg px-3 py-2 hover:bg-bg" href="#">Pricing</a>
      <a class="rounded-lg px-3 py-2 hover:bg-bg" href="#">Docs</a>
      <a class="mt-2 rounded-lg bg-accent px-3 py-2 text-center font-semibold text-white" href="#">Sign up</a>
    </div>
  </nav>
</header>

Conversion notes: Figma’s fill 80% + background blur is precisely bg-surface/80 backdrop-blur; “sticky on scroll” never appears in the inspector — it’s implied product behaviour you add (sticky top-0).

Example 2: Pricing card

What the inspector shows:

Frame "Card/Pro" — vert. Auto Layout, gap 24, padding 32, radius 16,
                   fill surface, border 1px accent (highlighted tier),
                   shadow 0 24 48 -12 black @ 40%
├─ Header — vert., gap 4  ("Pro" 20/semibold · "For growing teams" 14 muted)
├─ Price — horiz., gap 6, baseline  ("$24" 48/bold · "/mo" 16 muted)
├─ Divider — 1px border
├─ Features — vert., gap 12 — rows: horiz., gap 12 (check 20×20 accent + 15/fg)
└─ Button — fill width, padding 12, radius 10, fill accent, 15/semibold
<div class="flex w-full max-w-sm flex-col gap-6 rounded-2xl border border-accent
            bg-surface p-8 shadow-[0_24px_48px_-12px_rgba(0,0,0,0.4)]">
  <div class="flex flex-col gap-1">
    <div class="flex items-center justify-between">
      <h3 class="text-xl font-semibold">Pro</h3>
      <span class="rounded-full bg-accent-soft px-2.5 py-0.5 text-xs font-medium text-accent">
        Popular
      </span>
    </div>
    <p class="text-sm text-fg-muted">For growing teams</p>
  </div>

  <p class="flex items-baseline gap-1.5">
    <span class="text-5xl font-bold tracking-tight">$24</span>
    <span class="text-fg-muted">/mo</span>
  </p>

  <hr class="border-border" />

  <ul class="flex flex-col gap-3 text-[15px]">
    <li class="flex items-center gap-3"><span class="text-accent">✓</span> Unlimited projects</li>
    <li class="flex items-center gap-3"><span class="text-accent">✓</span> Priority support</li>
    <li class="flex items-center gap-3"><span class="text-accent">✓</span> Custom domains</li>
    <li class="flex items-center gap-3"><span class="text-accent">✓</span> Analytics dashboard</li>
  </ul>

  <a href="#" class="rounded-[10px] bg-accent px-4 py-3 text-center text-[15px] font-semibold
                     text-white transition-colors hover:bg-accent/90
                     focus-visible:ring-2 focus-visible:ring-accent focus-visible:outline-none">
    Start 14-day trial
  </a>
</div>

Conversion notes: the fancy Figma shadow became one arbitrary value — legitimate, because shadows are exactly where the default scale runs out. The focus-visible: ring appears nowhere in Figma; interactive states are always your addition. Baseline-aligned price + “/mo” is items-baseline, the flex alignment people forget exists.

Example 3: Dashboard stats grid

What the inspector shows (a row of four KPI tiles — the pattern from the dashboard design docs):

Frame "Stats" — grid, 4 columns, gap 16
└─ Tile ×4 — vert. Auto Layout, gap 8, padding 20, radius 12,
             fill surface, border 1px border
   ├─ Label — 13/medium fg-muted, letter-spacing +4%
   ├─ Value — 30/bold
   └─ Delta — horiz., gap 4 (arrow 14 + "12%" 13/medium, green or red)
<dl class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
  <!-- Tile is one pattern, stamped four times — build it once -->
  <div class="flex flex-col gap-2 rounded-xl border border-border bg-surface p-5">
    <dt class="text-[13px] font-medium tracking-wide text-fg-muted uppercase">Revenue</dt>
    <dd class="text-3xl font-bold tracking-tight">$48.2k</dd>
    <p class="flex items-center gap-1 text-[13px] font-medium text-emerald-400">
      <span aria-hidden="true">↑</span> 12.4% <span class="font-normal text-fg-muted">vs last month</span>
    </p>
  </div>

  <div class="flex flex-col gap-2 rounded-xl border border-border bg-surface p-5">
    <dt class="text-[13px] font-medium tracking-wide text-fg-muted uppercase">Active users</dt>
    <dd class="text-3xl font-bold tracking-tight">1,284</dd>
    <p class="flex items-center gap-1 text-[13px] font-medium text-emerald-400">
      <span aria-hidden="true">↑</span> 3.1% <span class="font-normal text-fg-muted">vs last month</span>
    </p>
  </div>

  <div class="flex flex-col gap-2 rounded-xl border border-border bg-surface p-5">
    <dt class="text-[13px] font-medium tracking-wide text-fg-muted uppercase">Churn</dt>
    <dd class="text-3xl font-bold tracking-tight">2.3%</dd>
    <p class="flex items-center gap-1 text-[13px] font-medium text-red-400">
      <span aria-hidden="true">↑</span> 0.4% <span class="font-normal text-fg-muted">vs last month</span>
    </p>
  </div>

  <div class="flex flex-col gap-2 rounded-xl border border-border bg-surface p-5">
    <dt class="text-[13px] font-medium tracking-wide text-fg-muted uppercase">Avg. session</dt>
    <dd class="text-3xl font-bold tracking-tight">4m 32s</dd>
    <p class="flex items-center gap-1 text-[13px] font-medium text-fg-muted">— flat</p>
  </div>
</dl>

Conversion notes: Figma’s fixed 4-column grid became sm:grid-cols-2 lg:grid-cols-4 — the collapse order is your call, and 2-then-4 keeps tiles readable on tablets. Semantic dl/dt/dd costs nothing and makes the tiles legible to screen readers. Up-arrow on churn is red, not green — direction ≠ goodness; the design file gets this wrong surprisingly often.

The AI-assisted workflow (2025-era reality)

Modern conversion is increasingly agent-assisted, and it changes the job rather than removing it:

  • Figma’s Dev Mode MCP server lets an agent read the selected frame’s structure, variables, and Auto Layout directly — so you can prompt “convert this frame to Tailwind using my @theme tokens” and get a faithful first pass grounded in the real file, not a screenshot guess. (Screenshot-to-code tools work too, but inspector data beats pixel parsing for fidelity.)
  • What the model does well: the mechanical mapping — layout translation, utility ordering, stamping out repeated components.
  • What stays your job: step 0. Deciding the tokens, the breakpoint behaviour, the semantic HTML, the interactive states, and rejecting the 17 arbitrary values it will happily emit if the file was messy. Review the output exactly like you’d review the junior-dev version of this page.

The punchline of all three examples: the conversion is easy when the system is explicit. Extract tokens once, learn the Auto Layout mapping, and every frame in the file becomes twenty minutes of calm translation instead of an afternoon of pixel-nudging.