Skip to content
Sudip KC writing notes in a journal at a desk
SK.
← All articles

UI/UX · · 6 min read

From Figma to Production: My UI/UX → React Workflow

  • Figma
  • React
  • Design Systems
  • Tailwind CSS
  • UI/UX

Most design handoffs fail quietly. Nobody fights about it; the build just drifts a few pixels and a few decisions away from the design until the product feels off. Here's the workflow I use to stop that drift.

I spent my early career on the design side. At GETS (Green Edge) I started as an intern and went full time as a UI/UX designer and frontend developer, which meant I designed more than 20 screens for Membersathi and then had to build them myself, along with 10+ reusable React components. There's nothing like implementing your own designs to make you honest about what a design file should contain.

This is the process I've settled on since then, whether I'm the designer, the developer, or now, as a technical lead, the person reviewing both.

Step 1: Design the system before the screens

The biggest mistake I made early on was designing beautiful individual screens. Screen one had 14px body text, screen five had 15px, and three different greys were all called "grey" in my head.

Now I start with a small foundation page in Figma before any real screen:

  • Color variables: brand, neutral scale, semantic (success, warning, danger, info), surface and text colors.
  • Type scale: five or six sizes, each with a line height, named by role (body, body-sm, heading-md) not by pixel value.
  • Spacing scale: a 4px-based scale, used for padding, gaps and layout.
  • Radius and shadow: two or three of each, no more.

These become Figma variables, and the names match what they'll be called in code. That last part is the whole trick. If the designer says text-muted and the developer writes text-muted, there's nothing to translate.

Step 2: Components with real states

A button in a design file is usually one rectangle. A button in production has default, hover, focus, active, disabled and loading states, and maybe an icon-only variant. If the design doesn't define them, the developer invents them, and now the product has two designers.

For every component, I design the states that will actually occur:

  1. Default, hover, pressed and focus-visible.
  2. Disabled and loading.
  3. Error and empty, for anything that holds data.
  4. The longest realistic content, like a Nepali restaurant name that wraps to two lines.

That fourth point catches more layout bugs than anything else. Lorem ipsum is always the perfect length. Real data never is.

Step 3: Auto layout that mirrors flexbox

I use auto layout everywhere, and I set it up the way CSS will work. Horizontal auto layout is flex-row, gap is gap, padding is padding. Fixed widths only where the real UI is fixed.

When a frame is built this way, the developer can read the layout panel and write the Tailwind classes almost directly. When it's built from absolutely positioned layers, they have to reverse-engineer intent, and they'll guess wrong some of the time.

Step 4: Map tokens into Tailwind CSS v4

Tailwind v4's CSS-first config makes this mapping very direct. The Figma variables become theme tokens:

@import "tailwindcss";

@theme {
  --color-brand-500: oklch(0.62 0.17 255);
  --color-surface: oklch(0.99 0 0);
  --color-text: oklch(0.22 0.01 255);
  --color-text-muted: oklch(0.52 0.01 255);

  --text-body: 1rem;
  --text-body--line-height: 1.6;
  --text-heading-md: 1.5rem;

  --radius-card: 0.75rem;
}

Now bg-brand-500, text-text-muted and rounded-card exist as utilities, and they mean exactly what they meant in Figma. When the brand color changes, it changes in one place in each tool.

Step 5: Build components from the outside in

I don't start with the page. I start with the smallest components the page needs and build upward.

components/
  ui/            Button, Input, Badge, Card, Dialog  (no business logic)
  patterns/      FormField, EmptyState, DataTable    (compose ui/)
features/
  orders/        OrderCard, OrderList                (domain-specific)

The ui folder is the design system in code. Those components accept variants that match the Figma variants by name:

type ButtonProps = React.ComponentProps<"button"> & {
  variant?: "primary" | "secondary" | "ghost" | "danger";
  size?: "sm" | "md" | "lg";
  loading?: boolean;
};

export function Button({ variant = "primary", size = "md", loading, disabled, children, ...props }: ButtonProps) {
  return (
    <button
      {...props}
      disabled={disabled || loading}
      aria-busy={loading || undefined}
      className={cn(base, variants[variant], sizes[size])}
    >
      {loading ? <Spinner aria-hidden /> : null}
      {children}
    </button>
  );
}

If Figma has a "danger" variant and the code calls it "destructive", someone will eventually build a third one. Same names, every time.

Step 6: Accessibility is part of the design, not a QA step

At Tonalist Color Revolution I worked on real-time color previews and accessible palette features where WCAG AA contrast was a hard requirement, not a nice-to-have. That changed how I design. Contrast gets checked in Figma while picking colors, not after launch when someone runs an audit.

  • Text contrast meets AA against every surface it can sit on, including dark mode.
  • Focus states are designed and visible, not left to browser defaults.
  • Touch targets are big enough for thumbs, not just mouse pointers.
  • Color is never the only signal; errors also get an icon and text.

The web.dev accessibility guides are a good reference if you want the reasoning behind each of these.

Step 7: Review the build against the design, together

The last step is the one teams skip. Before a feature is called done, the designer and developer look at it side by side, on a real phone, with real data. Not a screenshot in a chat thread.

A design review on a real device with real data catches in ten minutes what a week of screenshot comments never will.

I keep a short checklist for this review:

  1. Does spacing match the scale, or did someone type mt-[13px]?
  2. Do all designed states exist in code, including loading and error?
  3. Does the longest realistic content still fit?
  4. Does keyboard navigation reach everything, in a sensible order?
  5. Does it hold up on a cheap Android phone over a slow connection?

That last question matters a lot for products used in Nepal, where many users are on mid-range devices and patchy mobile data.

What changed when I started leading

As a Technical Project Manager at NovaNext, I'm not always the one in Figma or the editor anymore. My job in this workflow has shifted to protecting the shared vocabulary. When I review a PR, I look for arbitrary values and one-off components. When I review a design, I look for states that are missing. Most of the drift comes from those two places.

This workflow also fits with the tools I described in My Tech Stack in 2026: What I Use and Why: Figma variables, Tailwind v4 tokens and typed React components are three views of the same system.

The short version

Name things once and use the same name in Figma and in code. Design every state a component can be in. Build small components first. Check accessibility while designing, not after. And review the real build on a real device before calling it done. None of this is clever. It just removes the gaps where products lose their polish.