Roadrunner CRM — the design-to-code loop that finally closed.

THE PROBLEM

Two problems I'd spent 12+ years watching from the design side. One build to finally solve both.

Problem one — design and code were always two languages pretending to speak to each other. Figma handed off, engineering rebuilt visually-close-enough, tokens drifted, four sprints in the two files stopped resembling each other.

Problem one — design and code were always two languages pretending to speak to each other. Figma handed off, engineering rebuilt visually-close-enough, tokens drifted, four sprints in the two files stopped resembling each other.

Problem two — the one every CRM I shipped fought chronically at Madison Resources recruiting and PayFactors compensation — was dirty data. Duplicate records. Stale contacts. Sources nobody could trace.

Problem two — the one every CRM I shipped fought chronically at Madison Resources recruiting and PayFactors compensation — was dirty data. Duplicate records. Stale contacts. Sources nobody could trace.

I closed the design-to-code loop with Acme tokens and Figma MCP. I wired real AI suggestions on every intake surface — duplicate detection, auto-populate from eleven public sources, attribution per field. Record creation went from minutes to seconds. This is not a Figma prototype. It is a live working product. AI fires against real APIs. Any hiring manager can click through and try to break it.

Client / Self-initiated · Acme Design System portfolio proof

Data + AI / 11 free public providers · free-tier AI · zero paid APIs

Role / Product design · front-end build · deploy

Timeline / 3 days Acme · 5 days Roadrunner v1 · 1 day polish

Scope / Working CRM · 8 modules · live

Stack / Figma MCP · Claude Code · Next.js 16 · TypeScript · Tailwind

  • Figma MCP

    Figma MCP

  • Claude Code

    Claude Code

  • Figma

    Figma

  • Next.js 16

    Next.js 16

  • TypeScript

    TypeScript

  • Tailwind

    Tailwind

  • Zustand

    Zustand

  • TanStack Table

    TanStack Table

  • Phosphor Icons

    Phosphor Icons

  • Vercel

5 days

8 modules

11 sources

$0

EMPTY REPO TO LIVE WORKING CRM

SHIPPED ON ONE DOMAIN

FREE PUBLIC DATA PROVIDERS, LIVE

INFRA COST HOSTING · SSL  · DOMAIN

MY ROLE

One designer. Built the system. Built the app. Shipped the loop.

WHAT I OWNED

Acme design system — tokens, components, seven handoff docs. Every Figma frame. Every line of TypeScript in the Next.js 16 build. Eleven public data integrations. Vercel deploy, custom DNS on paulwentzellux.com. This case study. One designer — full stack of decisions and artifacts.

HOW I WORKED

Figma MCP read the design file. Claude Code generated code against the same tokens Figma used. I held the judgment seat. Every grid pattern came from twelve years at Madison Resources CRM, PayFactors, and financial services platforms. Claude handled the generation — experience handled the decisions Claude couldn't make.

RESEARCH - CAREER INQUIRY

Dirty data was the problem at Madison Resources. Dirty data was the problem at PayFactors. Every HR team lives inside it.

Recruiters at Madison Resources spent hours every week reconciling duplicate candidate records, stale contact info, and fragmented source attribution. Comp analysts at PayFactors fought the same fight against outdated company data and records that said different things in different systems. Different industries, same chronic failure mode.

Dirty data is the problem HR and comp professionals have asked for help with for as long as I've designed for them — and it is the exact problem Roadrunner's AI features are wired to solve.

So when my first pass shipped a fake duplicate detector querying a hardcoded name pool and labeling the fake results "Clearbit verified" — I was replicating the problem I'd spent twelve years trying to fix. The rebuild wired real AI suggestions on every intake surface: type a name, get a duplicate check before you save; type a company, get auto-populate from live public records with provider attribution per field; drop a resume, get parsed fields prefilled and the original attached. Clean record creation went from minutes to seconds.

WHAT USERS ASK FOR ON EVERY PROJECT - GRID EDITION

  • Saved views — "I want my layout back"

  • Column pinning — "keep Name visible when I scroll"

  • Density control — "let me fit more rows on screen"

  • Sticky row actions — "don't make me scroll to delete"

  • Per-column filters — "filter amount without leaving the grid"

WHAT USERS ASK FOR ON EVERY PROJECT - APP EDITION

  • Light and dark mode — every industry, every time

  • Help that's there when I need it, not just on install

  • Saved state that survives a refresh

  • Undo on anything destructive

  • Keyboard shortcuts for power users

Every grid in Roadrunner ships with all five grid patterns. Every one of the app-level patterns ships too — because I've watched users at Madison Resources, PayFactors, and financial services platforms fight for them when they weren't there. Ten shipped patterns is not cleverness; it's pattern literacy earned in production.

KEY FINDING

The gap was never about tools. It was that design and code were two translations of each other.

On a normal project, a designer updates a color token in Figma. An engineer reads the hex. Types it into a stylesheet. Maybe misreads it. Maybe uses the old value because the variable was already defined. Every token is a handoff with a chance of loss. Multiply by every component, every state, every sprint. That is where the gap lives.

THE CLOSE

Acme tokens are defined once. Figma variables and Tailwind CSS custom properties reference the same values. Figma MCP reads the Figma file. Claude Code generates React using Tailwind classes that resolve to those same tokens. A change in one surface shows up in the other without translation.

"Design linked to code. Code linked to design. First time in twelve years that loop closed on anything I built."

DELIVERABLE

A running token library — not a style guide

Acme tokens are defined once. Figma variables and Tailwind CSS custom properties reference the same source. Engineering installs, imports, and ships — no translation step, no drift.

When a token changes in Figma, the CSS variable updates. When the CSS variable updates, every component using it retints. The loop holds in both directions.

TOKEN FILE - GLOBAL.CSS

TOKEN FILE - GLOBAL.CSS

THE CORE TENSION

System built by one designer vs. a system that survives a real build.

A three-day design system sounds like a shortcut. On every enterprise project I've shipped, design systems built too fast broke the first time a real product demanded them. Acme had to prove it could power a working CRM — not a portfolio screen.

THE AI LAYER

A central query pulse radiates out to all eleven providers. Each returns a result back to center. Repeats every 5 seconds.

One query. Eleven sources. Live.
QUERY"Tom Coffee"ClearbitGitHubWikidataSECGLEIFORCIDGravatarOpenCorpCos.HouseSAMHunter
One query fans out to eleven free public sources in parallel. Each returns independently. Source attribution is captured per field.

Placement in case study: Replaces the static provider badge grid in the AI Layer · Real APIs. Free tier. Nothing simulated. section.

SIX WAYS TO EXPERIENCE THE APP

1

Create a new contact

Type a real name and watch duplicate detection run against public sources in real time, before you save.

2

Drop in a PDF or DOCX resume

The candidate intake parses it, prefills the form, and attaches the original to the contact's Documents tab.

3

Customize a grid

Open the Columns dropdown, pin a column, flip density. Refresh — your widths, pins, and density stay exactly as you left them.

4

Delete something and undo it

Every destructive action fires a toast with an Undo button. Click it and the record returns with every nested entry restored.

5

Take a guided tour

Help → Tours → pick any section. Contextual walkthroughs teach the shared patterns every module uses.

6

Flip to dark mode

Settings → Appearance. Every Acme token re-resolves instantly. Zero component rewrites — the loop handles it.

About the session: the demo stores state in your browser so your session is yours alone. Nothing you create is sent anywhere, and a refresh keeps your work intact until you choose to reset.

DESIGN DECISIONS

Six decisions. Each one a direct response to something AI generated that experience had to correct — or something users have asked for in every product I've shipped.

DECISION 1

Kill the fake data pool. Rebuild every AI path against eleven real public sources.

FINDING

The first build queried a 2,847-entry hardcoded name pool and labeled the result "Clearbit verified." The attribution was a string constant, not an API call.

INSIGHT

This wasn't a data bug — it was a trust bug. Every AI claim has to be inspectable or the demo dies on the first real name.

DESIGN

Server-side fan-out across eleven public providers. Provider badges render on every suggested field. Any claim can be traced in one click.

DECISION 2

Column pinning — persisted, always visible.

FINDING

First pass hid pin icons behind column-row hover. Users who wanted pinning couldn't find it.

INSIGHT

Hover-reveal is right for rare controls. Pinning is a power feature users look for by name — Excel freeze-pane muscle memory is thirty years old. Hiding it buries the benefit they came for.

DESIGN

Pin buttons always visible in the Columns dropdown. State persists across refresh via Zustand persist middleware. Left and right edges both supported.

DECISION 3

Tabbed help panel.

FINDING

The first help panel was eleven tour cards stacked in a 600-pixel side panel. Most tours sat below the fold and got skipped.

INSIGHT

A scrollfest in a fixed-height surface is a discoverability failure, not a cosmetic one. A count badge without units is decoration, not information.

DESIGN

Pill-segmented Tips / Tours / Resources tabs — matching the List/Card and All/Organizations/People pills elsewhere. Current-page tour pinned at top as "Recommended for this page." No count.

DECISION 4

Tours live in the help panel — always available, not a one-time install cutscene.

FINDING

Most products ship guided tours that fire once on first login and then disappear forever. Users who needed help later — a month in, returning after a break, training a new hire — had no way to find the walkthrough again.

INSIGHT

Help is most useful when the user is already frustrated, not when they're fresh. Burning tours on install wastes them on the moment users need them least. This came from watching support-ticket patterns at PayFactors and Madison Resources — tickets spiked in week three, not week one.

DESIGN

Nine contextual tours live permanently in the help panel, reachable from any page, filtered to the current surface first. Every tour can be re-run any time. No cutscenes, no dismiss-forever, no buried "restart tutorial" setting. The help icon is the entry point; the tour is always behind it.

DECISION 5

Light and dark mode, defined at the token layer so every surface retints for free.

FINDING

Light and dark mode is the single most-requested feature across every CRM I've shipped — financial services, HR staffing, compensation analytics. Users ask for it in week one. It ships in year three because teams retrofit it component by component.

INSIGHT

Dark mode isn't a styling pass. It's a token problem. If colors are defined per-component, you retrofit forever. If colors are defined as semantic tokens (surface, text-primary, border) in an Acme-style system, dark mode is one theme swap away.

DESIGN

Every Acme color is a semantic token mapped to a light and a dark value. The theme switch in Settings flips a root-level data attribute; every surface in every module retints instantly. No per-component work, no missed states. Shipped on day one, not year three.

DECISION 6

Toast confirmations with Undo on every destructive action.

FINDING

Initial delete handlers fired silent success. Users re-clicked delete because they were unsure the first click had registered.

INSIGHT

Silent success is disorienting. Gmail, Slack, and Figma anchor destructive actions on toast-with-Undo because reversibility reduces the fear tax on irreversible controls.

DESIGN

Four-severity toast system. Snapshot-and-restore Undo on every contact, deal, and candidate delete. Caught the missed contact-delete case when I asked, "If I delete a contact will I see a toast?"

WHAT I DELIVER TO ENGINEERING

Because the loop closes, I hand off deliverables engineering can consume — not specs to translate.

A senior designer who has closed the design-to-code loop doesn't move stories from "in design" to "in dev." They move them from "in design" to "merged" — because the artifacts engineering receives are already the artifacts engineering would build. That's the difference between one sprint and three.

The loop that finally closed
FIGMAVariables · componentsvia Figma MCPACMETOKENSone sourceCODETailwind CSS varsvia Claude CodeREAD · WRITEREAD · WRITE
One token. Rendered in Figma. Rendered in code. The token moves between both surfaces in real time — no handoff, no drift, no translation.

WITHOUT THE LOOP

What engineering gets today on most projects

  • Figma file with redlines to measure

  • PNG exports of states

  • Hex codes copy-pasted into CSS

  • Slack threads clarifying what a component means

  • Design drift discovered three sprints later

  • QA tickets retrofitting accessibility

  • Coordination meetings to align tokens

WITH THE LOOP

What engineering gets from me

  • Running token library — install and import

  • Live React components next to Figma frames

  • Figma-to-code prop mapping, one page per component

  • Validation rules authored on the design side

  • Accessibility baked into the system

  • AI-generated first drafts with judgment applied

  • One master doc, not thirty Slack threads

DELIVERABLE 01

A running design system, not a style guide

Acme is a working token library. Figma variables and Tailwind CSS custom properties wired to the same source of truth. Engineering installs, imports, and ships.

DELIVERABLE 02

Live component references, not static specs

Every component lives in both Figma and a running React surface. Engineers click from the handoff to the rendered component with exact props and states.

DELIVERABLE 03

Grid pattern library from 12 years of CRMs

The grid toolbar — saved views, pinning, density, zebra, sticky actions, per-column filters — shipped as one reusable SharedDataGrid. One component to maintain, eight grids to use it.

DELIVERABLE 04

Tokens that survive refactors

Because tokens are defined once and referenced in both surfaces, engineering can rename, restructure, or reorganize without a coordination meeting. Figma updates when tokens do.

DELIVERABLE 05

Working prototypes instead of redlines

When I propose a pattern, I can ship a working prop-typed React component alongside the Figma frame. Engineering reviews behavior and props, not pixel measurements.

DELIVERABLE 06

Figma-to-code mapping documents

One page per component mapping Figma props to React props and CSS variables. No guessing, no "what does size=lg mean in the real file." Engineering reads, writes glue, ships.

DELIVERABLE 07

AI-assisted first drafts — with judgment applied

For rare patterns, I generate a first-draft React component with Claude Code and hand it to engineering with naming cleaned up, states verified, accessibility checked, tokens wired.

DELIVERABLE 08

Validation rules as design, not copy

Intake forms ship with required fields, format checks, error messages, and disabled states defined as props. Engineering implements the glue; rules live where the form lives.

DELIVERABLE 09

A running design system, not a style guide

Color contrast, focus states, keyboard navigation, and screen-reader labels are baked into Acme components. Use the button, get the a11y. No retroactive QA tickets.

DELIVERABLE 10

One artifact, not thirty Slack threads

The Acme master document — foundations, components, tokens, patterns, Figma-to-code mapping, grid library, validation, accessibility — in one navigable reference. Engineering bookmarks one URL.

OUTCOMES

A live product, not a portfolio screenshot.

3

20

3

Days to Acme
vs. weeks prior

Components

Days to Acme
vs. weeks prior

5

27

5

Days to v1
empty repo → live

Buttons

Days to v1
empty repo → live

8

30

8

Modules live
one domain

Badges

Modules live
one domain

11

20

11

Public providers
replacing fake data

Components

Public providers
replacing fake data

13

27

13

Polish decisions
each with precedent

Buttons

Polish decisions
each with precedent

$0

30

$0

Infra cost
hosting, SSL, domain

Badges

Infra cost
hosting, SSL, domain

The Loop Held

Figma tokens and Tailwind variables resolved to the same values. Zero handoff artifacts were written during the build.

Every AI claim is inspectable

Provider badges render next to every suggested field. Any claim can be traced to a real public source in one click.

Polish was disciplined

13 UX decisions shipped in one day. Average rationale: two sentences per decision. No feature creep.

Real AI, not a Figma prototype

The AI fires against real APIs. Custom subdomain, open-source repo. Anyone can click through and try to break it.

LIVE NOW · REAL DATA · REAL AI

Roadrunner CRM — interactive portfolio demo

Ten free modules. Eleven free public data providers wired live. Sample HR staffing data seeded so the first click already has something to do. Every visitor gets a private browser-local sandbox, so you can explore freely without affecting anyone else.

roadrunner-crm.paulwentzellux.com

What I'd do differently

I'd wire the real providers from day one instead of letting the fake pool ship first — the rebuild was instructive but expensive. I'd put validation on every intake surface before writing a single AI feature, not after. I'd ship the Postgres backend alongside v1 instead of letting "browser-local only" be the story for a week; the sandbox framing was convenient for a portfolio sprint and wrong for a CRM. On tours, I'd write the first one by walking through the app as a new user instead of starting mid-flow — four rewrites taught the same lesson I could have gotten for free.


And on state-management refactors: I'd verify the change in the browser before committing. One pass at making column reorder, sort, filters, and saved views survive a refresh shipped a TanStack table state refactor that caused an infinite render loop in the data grid — eight commits, two reverts, the change rolled back to a known-good baseline. The lesson costs nothing in retrospect: visual-only edits are safe to batch and ship; state-engine changes go in one isolated commit with a browser-verified diff before git push. The pattern that held best was pairing every Claude-generated decision with a cited precedent from my career. The pattern I'd drop was trusting first-pass toast wiring, first-pass tour wiring, and first-pass state wiring to be complete; all three missed cases I caught only by running the flow cold.