I often get asked how I go from a polished Figma prototype to production-ready HTML components that are accessible, maintainable, and styled with Tailwind. There’s a temptation to try to do everything at once — match the visuals, wire up interactions, and make it accessible in one go. I’ve found a much better approach: split the work into three focused passes. Each pass has a clear goal and outcomes you can ship or iterate on. Below I walk through my process, why it works, and practical tips you can apply immediately.
Why three passes?
Breaking the conversion into three passes helps you avoid cognitive overload and encourages incremental validation. In my experience, the three passes — structure, styling, and interaction & accessibility — map neatly to how browsers render pages and how design decisions evolve during development. You get usable HTML early, then progressively enhance it until it matches the prototype and user needs.
Pass 1 — Structure: semantically correct, content-first HTML
Goal: produce clean, semantic HTML that reflects the content and hierarchy from the Figma prototype.
What I do in this pass:
- Export assets sparingly. Only export icons, logos, or images that can’t be recreated with SVG/CSS. Prefer inline SVGs for icons because they’re easier to manage and style.
- Translate Figma frames to semantic elements. Headlines become
<h1>–<h6>, navigation becomes<nav>, sections are<section>or landmarks (<main>,<aside>). Don’t use generic<div>as a first choice. - Keep content real. Replace placeholder text with the actual content or realistic copy. This helps reveal layout issues and accessibility concerns early.
- Create small, reusable component skeletons. For example, a card component with
<article>, an image container, heading, and description. These skeletons are what you’ll style in pass 2.
Example output (conceptually):
| Component | Semantic root |
| Hero | <header role="banner"> / <h1> |
| Card | <article> |
| Form | <form> with <label> and <input> |
Why this matters: semantic HTML makes your site more resilient and is the foundation for accessibility and SEO. It also simplifies styling later because the structure matches the content’s meaning.
Pass 2 — Styling: Tailwind-first, utility-driven, design-faithful
Goal: implement the visual design from Figma using Tailwind classes while keeping components small and reusable.
How I approach styling with Tailwind:
- Start with a global design token mapping. Map Figma tokens (colors, spacing, fonts, radii) to your Tailwind config. This ensures consistency and keeps class names tidy.
- Style from outside in. Apply layout classes to containers, then refine typography and spacing on individual elements.
- Create component class snippets. I often make a small CSS or Tailwind layer where I group commonly used class combinations (e.g., .btn or Tailwind @apply rules). This avoids repeating long class lists in templates.
- Prefer composition over single massive classes. Keep components flexible: a card should be able to accept modifiers like compact or highlighted rather than having duplicate components for each variation.
Quick Tailwind tips I use:
- Use
container mx-auto px-4for responsive layouts. - Use
proseclasses (from @tailwindcss/typography) for long-form content to avoid manual typography tuning. - Favor utility variants like
md:grid md:grid-cols-2for responsive layouts rather than custom media queries.
At the end of this pass you should have components that visually match the Figma prototype enough to be reviewed by designers and stakeholders. They’re still basic in behavior, but they look right.
Pass 3 — Interaction & Accessibility: keyboard, screen reader, and edge cases
Goal: add the interactive behaviors, keyboard support, and ARIA attributes that make components accessible and production-ready.
This pass is where many teams shortcut, but it’s where the product becomes usable for everyone.
Key activities I perform:
- Make sure all interactive elements are native controls or keyboard-focusable (
<button>,<a>with href, ortabindexon custom controls). - Add visible focus states. Tailwind’s
focus:outline-noneis common, but always replace it with an accessible focus ring likefocus:ring-2 focus:ring-offset-2 focus:ring-indigo-500. - Use ARIA roles and labels judiciously. Prefer semantic markup over ARIA when possible; use ARIA to enhance, not replace, semantics.
- Test keyboard interaction patterns: tab order, enter/space to activate, and arrow-key support if appropriate (e.g., menus, carousels).
- Implement accessible form patterns: associate
<label>with inputs, expose error messages viaaria-describedby, and ensure validation messages are programmatically connected. - Progressively enhance complex interactions. For example, implement a dropdown with basic show/hide behavior controlled by CSS (e.g., via hidden classes) then add JS for keyboard and focus management.
Tools and techniques I rely on:
- axe DevTools or Pa11y for automated accessibility scans.
- VoiceOver (macOS) and NVDA (Windows) for screen reader spot checks.
- Lighthouse for performance and accessibility quick checks.
- Playwright or Cypress for end-to-end tests that cover keyboard flows and important interactions.
Practical checklist to ship a component
- Pass 1: Semantic HTML built, content verified, component skeletons created.
- Pass 2: Visuals implemented in Tailwind, tokens mapped, responsive styles validated.
- Pass 3: Keyboard support, focus management, ARIA or semantic improvements, automated checks passed.
Shadowing this checklist with regular reviews is helpful. I usually demo the component to a designer after pass 2, then do an accessibility walkthrough with a teammate or a dedicated reviewer after pass 3.
Common pitfalls and how I avoid them
- Over-styling early: Avoid pixel-perfect obsession in pass 1. Focus on structure first. Visual fidelity comes in pass 2.
- Ignoring keyboard users: Test keyboard interactions from the start of pass 3 — don’t wait until everything else is “done”.
- Large monolithic components: Break components into smaller, composable parts (header, body, footer) to make them reusable.
- Not mapping tokens: If your design tokens are inconsistent, Tailwind classes become chaotic. Invest time in a small token file in
tailwind.config.js.
I use this three-pass workflow for everything from marketing pages to product UIs. It keeps the process manageable, improves collaboration with designers, and leads to components that are visually faithful, accessible, and easy to maintain. If you want, I can share a starter repo structure or a Tailwind config example that maps common Figma tokens — say the one I use for Themebat projects — to help you get started faster.