I make Cursor build my Figma, pixel for pixel, or it does not merge
How I wired Figma, Playwright, and an accessibility reviewer into Cursor so the agent ships components that actually match the design.
I have a thing about handoff. You spend two days getting a card just right in Figma, the radius, the shadow that is barely there, the way the title sits exactly 12px off the image, and then it gets built and the shadow is gone and the radius is 8 instead of 10 and nobody but you can tell. For years that 1px was my problem to chase. Now I make the agent chase it. This is the Cursor setup I use to turn frames into front end that actually matches, and refuses to merge when it does not.
The build, plainly: Cursor running Claude Sonnet 4.6 in agent mode, with three MCP servers (Figma, Playwright, GitHub), two subagents (an agent-mode worker that builds and an a11y-reviewer that gates merges), and two hooks: format on save, visual-regression on commit. It is not a huge rig. It is just opinionated in the right places.
Figma is the source of truth, so the agent reads it
The whole thing falls apart if the agent guesses. The Figma MCP server is what stops the guessing. Cursor can pull the actual node: the fills, the auto-layout gaps, the text styles, the exact token names. I paste a frame link, the agent reads the structure, and it builds against real values instead of vibes. Here is my MCP config. It lives in .cursor/mcp.json at the project root.
{
"mcpServers": {
"figma": {
"command": "npx",
"args": ["-y", "figma-developer-mcp", "--stdio"],
"env": { "FIGMA_API_KEY": "${FIGMA_API_KEY}" }
},
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest", "--browser", "chromium"]
},
"github": {
"url": "https://api.githubcopilot.com/mcp/"
}
}
}The rules file does the nagging so I do not have to
Cursor reads .mdc rule files automatically based on which files you touch. This is the single highest-leverage part of the setup. My rules are short and mean. No essays. The agent does not need my design philosophy, it needs the 4px scale and a hard no on inventing colors.
---
description: Frontend rules for turning Figma frames into shipped components
globs:
- "src/components/**/*.tsx"
- "src/**/*.stories.tsx"
alwaysApply: true
---
# How we build UI here
- Match the Figma file as the source of truth. Read tokens from
src/styles/tokens.css, never eyeball a hex. If a value is not a
token, stop and ask before hardcoding it.
- Spacing is the 4px scale only: 4, 8, 12, 16, 24, 32, 48, 64.
No 13px, no 17px, no "looks about right".
- Every interactive element meets WCAG AA: visible focus ring,
4.5:1 text contrast, a real label or aria-label. No div soup
with onClick. Use a button.
- One Storybook story per component, with the variants that exist
in Figma (default, hover, disabled, loading). If Figma has it,
the story has it.
- Tailwind utilities for layout, CSS variables for theme. Do not
invent a new color. Do not add a UI dependency to get one shadow.That spacing rule alone killed about half of my old review comments. The agent used to drop a 13px gap because the screenshot kind of looked like 13px. Now it snaps to the scale or it asks. Much calmer.
An accessibility reviewer that can actually say no
Here is my real opinion: most AI-built UI is inaccessible garbage. Divs with click handlers, focus rings ripped out because they looked ugly, contrast that fails on the placeholder text. So I gave the build a second agent whose only job is to fail my first agent. It does not build anything. It reads one component and reports.
# a11y-reviewer
You review a single React component before it is allowed to merge.
You do not write features. You only check and report.
## Checklist (fail loudly on any miss)
1. Color contrast >= 4.5:1 for text, 3:1 for large text and icons.
Pull the actual token values, do not assume.
2. Keyboard path: can you reach and operate it with Tab + Enter +
Space? Is the focus ring visible against the background?
3. Semantics: button vs link vs div used correctly. Headings in
order. Form fields have associated labels.
4. State: aria-disabled / aria-expanded / aria-current set where
the visual state implies them.
## Output
A short pass/fail per item, then the exact diff to fix the fails.
End with one line: "AA: pass" or "AA: fail (N issues)".What a real session looks like
Two hooks run without me thinking about it: on save, the formatter cleans the file; on commit, Playwright snaps the rendered component and diffs it against the baseline. If a shadow changed or a gap drifted, the commit stops. That visual-regression step is the thing that finally let me trust the agent on the small stuff.
The numbers, and what they cost me
I am not going to pretend it is magic. Sonnet 4.6 in agent mode is fast and cheap enough that I leave it running, and the gating is what makes the speed safe. Rough averages from my last month of work:
| Metric | Value | What it means |
|---|---|---|
| Avg response | 1.2s | Fast enough to stay in flow |
| Cost / task | $0.24 | I do not watch the meter anymore |
| Pass rate | 83% | 1 in 6 still needs a real fix from me |
| Build score | 89 | A-tier on Setuproll, and earned |
That 83% is honest. The agent nails layout and tokens. Where it still loses is taste calls: when two designs are technically valid and one just feels better. That part is still my job, which is fine. I did not want a robot that designs. I wanted one that builds exactly what I designed.
Steal these
- Put your design tokens in one file and forbid the agent from hardcoding values. This fixes more than any prompt trick.
- Make a second agent whose only job is to reject the first one. Building and reviewing should not be the same context.
- Visual regression on commit. If you ship UI, this is the seatbelt.
- Keep rules short. Three crisp constraints beat a 600-word style guide the model skims.
28:05If you want the whole thing wired the way I run it, rules, MCP config, both agents, and the two hooks, grab it in one line: npx setuproll add cursor-frontend-figma. Then point it at a Figma frame and watch it sweat the 1px so you do not have to.