From Figma to Production in One Workflow

Components that work on every screen. A workflow that works for every engineer.

Figma
Design token defined
Primary/default
React
Component consumes token
className="bg-primary"
Storybook Storybook
Visual verification
npm
Published package
import { Button }
from 'ralfy-ui'
ralfy-app.vercel.app
WORKSPACE
Dashboard
My Posts
Activity
Settings
Needs Reply
All
Drafts
New comment on your post
Alex mentioned you in "Design Systems at Scale"
Sarah liked your draft
Your post "Token Pipeline" got a reaction

Context

Ralfy is a LinkedIn feed management tool I built over the past two months. Custom feeds, AI comments, reply drafts. I handle design, code, and product on my own.

I built the whole thing with AI. Frontend, backend, database, deployment. Claude wrote most of the code while I focused on product decisions and design direction. Speed was the priority, so there was no design system, no shared tokens, no component library.

The app also has a Chrome extension that surfaces LinkedIn content alongside the main dashboard.

The Problem

Every component was built differently. Four screens, four button styles. Tabs built three ways. No shared patterns, no shared tokens, no reusable parts.

There was no single source of truth. Colors were hardcoded oklch values or CSS variables duplicated under different names for the same role. Changing the primary color meant searching the codebase and hoping I found every instance.

DraftCard.tsx
ProfileTab.tsx
DeleteDialog.tsx
CommentItem.tsx
CSS theme file with hardcoded oklch values, mixed hex colors, and annotated inconsistencies
Before globals.css
/* Hardcoded production colors */
:root {
--sidebar-bg: oklch(0.21 0.006 286);
--sidebar-hover: oklch(0.27 0.005 286);
--btn-primary: oklch(0.21 0.006 286);
--btn-hover: oklch(0.21 0.006 286 / 0.8);
--card-border: oklch(0.87 0.007 286);
--text-muted: oklch(0.55 0.014 286);
--danger-bg: oklch(0.58 0.22 27.3);
--success-bg: oklch(0.60 0.13 163);
}
/* 47 more hardcoded values... */
After globals.css
/* Token references from ralfy-ui */
@import 'ralfy-ui/tokens';
:root {
--sidebar-bg: var(--bg-primary-default);
--sidebar-hover: var(--bg-primary-default-hover);
--btn-primary: var(--bg-primary-default);
--btn-hover: var(--bg-primary-default-hover);
--card-border: var(--border-default);
--text-muted: var(--foreground-muted);
--danger-bg: var(--bg-destructive-default);
--success-bg: var(--bg-success-default);
}

I needed guardrails. Not just components, but a system that prevents this drift from happening again. So I gave myself a challenge. Build the full pipeline in 2 days with AI.

Approach

The goal was a complete workflow from Figma to production. Not just components. The pipeline that keeps everything in sync. Change a token in Figma and the production app reflects it. No component code touched.

Figma Design System Colors, typography, spacing, components Tokens Studio Export variables as JSON tokens.json W3C DTCG format Style Dictionary 5 pnpm tokens:build primitives.css light.css dark.css @import tokens.css @theme inline React Components Storybook Visual check vs Figma npm publish Production App import { Button } from 'ralfy-ui'

Trade-offs

  • Tokens first, not components first. Tokens constrain decisions. Without them, components drift.
  • Custom system, not a shadcn copy. I needed the full pipeline from Figma to production. Not just pre-built UI.
  • CSS custom properties, not runtime CSS-in-JS. Zero runtime cost. No bundle overhead. Tree-shakeable exports.

Token Architecture

Semantic tokens reference primitives. Components only use semantic tokens. Swap the primitives and every component updates. No code changes needed.

--background-primary-default: var(--primary-900);
--foreground-default: var(--zinc-950);
--border-default: var(--zinc-200);
Tokens Studio for Figma showing token sets (Light, Dark, Primitives) with semantic color tokens (Secondary, Destructive, Success, Warning, Disabled) and number tokens (Spacing, Padding, Radius)

CSS distribution was the hardest problem. Tailwind v4 resolves @source paths from external packages differently. The fix: a dedicated styles.css entry point that bundles token imports and @theme inline mappings. One import. Done.

Components

Figma to Storybook

Side by side comparison: Figma Alert component variants (left) matching Storybook Alert variants (right) — Default, Destructive, Warning, Success

Every component follows the same four steps:

  1. Read the design via Figma MCP. Pull variants, sizes, states, and tokens.
  2. Map tokens. Figma names map 1:1 to Tailwind classes. Background/Primary/default becomes bg-background-primary-default. No translation needed.
  3. Build. cva for variants. React.forwardRef. cn() for class merging. Every state gets a token. Keyboard nav, focus rings, semantic roles.
  4. Verify in Storybook. AllVariants story shows every combination. Autodocs page generates the props table. Visual check against the Figma source.

Component Deep Dive: Tabs

The production app had tabs built three different ways. Same concept, different code. One used divs with onClick. Another had custom keyboard handling. A third skipped focus states entirely.

UI audit showing three different tab implementations across the Ralfy production app

Tabs is built on Radix Tabs. Arrow-key navigation, aria-selected, roving tabindex, all handled by the primitive. I wrote zero keyboard logic. Zero ARIA attributes by hand. I focused on the tokens and variants that make it look right in the system.

One component. Every screen that needs tabs gets the same behavior, the same keyboard nav, and the same visual treatment.

What I Built

Five components, each following this workflow:

Component Pattern What This Proves
Button asChild via Radix Slot. Six variants, four sizes, loading state. Token-per-state system (default, hover, focus, disabled all tokenized)
Alert Four semantic variants with role="alert". Accessibility contract (screen readers announce status changes)
Sidebar Compound: Brand, Menu, Item, Group, Separator. Collapsed mode. Compound architecture scales (16 sub-components compose freely)
Tabs Radix Tabs: arrow-key nav, aria-selected, content panels. Platform primitives over custom JS (full keyboard nav for free)
TabItem Standalone role="tab", three sizes, icon support. Flexible composition (works inside or outside Tabs compound)

Storybook to Production

Using the system should be simple. A developer needs three lines:

// 1. CSS (once, in your app's entry point)
@import 'ralfy-ui/styles.css';

// 2. Use
import { Button } from 'ralfy-ui';

<Button variant="primary" size="md">Save</Button>

Tokens, dark mode, and styles come through automatically. No setup. No theme config. No extra CSS imports. Changes hot reload in Storybook for fast visual checks.

AI-Assisted Development

The project includes CLAUDE.md. It lists every token class, component API, variant, and accessibility rule. It works as documentation-as-governance. The AI follows the system by default.

Without CLAUDE.md:

<button className="bg-[#1a1a2e] text-white px-4 py-2 rounded-md">
  Save
</button>

With CLAUDE.md:

<Button variant="default" size="md">Save</Button>

The first creates a maintenance problem. Hardcoded values. No dark mode. No focus ring. No loading state. The second inherits all of that for free. This is how I built 5 components with 63 tests in 2 days.

I also created a figma-to-component skill. It reads a Figma design via MCP, maps tokens, generates the component, creates Storybook stories, and runs the build. It turns a multi-hour manual process into a repeatable workflow.

But it's not just one skill. I built a set of them covering the full component lifecycle. Each skill encodes a workflow that would otherwise live as tribal knowledge. Together they make the entire pipeline from Figma to production repeatable by anyone with access to the repo.

AI Skill Pipeline
$ figma-to-component --url figma.com/design/...
Read design via MCP → extract tokens → map to Tailwind → generate component
$ build-component Sidebar --compound
16 sub-components · cva + forwardRef · Storybook stories + autodocs
$ ralfy-testing
Generate scenarios → happy path + edge cases → run vitest → 63 passing
$ npm-publish
Version bump → build + typecheck → publish to npm → git tag + push
All governed by CLAUDE.md tokens, APIs, accessibility rules baked in

Results

Buttons

Before: 4 different button styles across files. After: 4 consistent Button variants from ralfy-ui

Colors

Before: hardcoded color values (zinc-800, hex, stone-900, indigo-950). After: semantic design tokens (bg-background-muted, bg-background-card, bg-background-primary-default)

Alerts

Before: 3 different alert patterns with inconsistent styling. After: 4 consistent Alert variants from ralfy-ui with proper icons

Tabs

Before: 3 different tab patterns (pill, underline, rounded). After: 3 identical Tabs components from ralfy-ui

Built in 2 days: 5 components from Figma, 63 tests, Chromatic deploy, and a repeatable workflow from design to production.

What this enables:

What's Next

The system and pipeline are in place. Next is a full UI pass across the app. Replace every hardcoded color with a token. Fix layout consistency. Make the design system the single source for all UI decisions. The audit already mapped what needs to change.

Reflection

At scale (30+ engineers, volunteer Component Librarians), this system would need component-level tokens and formalized contribution guidelines. Chromatic visual diff approval on every PR. No component ships without a visual review. Storybook becomes the reference that both designers and engineers trust.

Five Lessons

  1. Start with tokens, not components. Tokens constrain every decision downstream. Get them right first.
  2. Build what you use. Every component exists in production. Zero speculative work.
  3. The workflow matters more than the components. Anyone can build a Button. The value is in the pipeline that lets teams ship faster.
  4. AI needs guardrails. Without CLAUDE.md, AI generates hardcoded colors. With it, AI follows the system by default. The spec is the multiplier.
  5. A 2-day sprint can prove a system. The point was not to build every component. It was to prove the workflow end to end. Scaling is just repeating a solved process.

Let's talk design systems

I'm open to roles where I can build and scale systems like this.