Visual acceptance before frontend implementation
PLVisual acceptance is a gate between intent.md and frontend code. An agent turns the intent into an inspectable mock, a named product or design owner signs a versioned acceptance.md, and the built screen is later checked against the accepted frames with screenshots and a separate review pass. A mock changes in minutes; a merged screen does not.
This page is for the developer who builds user interfaces with Claude Code, Codex, or Cursor, and for the tech lead who wants that loop to look the same across a team. The situation it fixes: you ask the agent for a settings screen, it ships a polished page in 20 minutes, and the product review sends it back because the hierarchy is wrong, the error state is blank, and nobody agreed on the mobile layout. If you do not have an intent.md yet, start with Plan: capture intent.md.
Scorecard Q7: How do you prototype and validate user interfaces before implementing code?
Max-score answer: a visual prototype loop, in which
intent.mdproduces an inspectable mock through an approved design tool, iterated before code generation.
What you’ll walk away with from visual acceptance
Section titled “What you’ll walk away with from visual acceptance”- A way to pick the mock medium (Figma frame, repository HTML prototype, generated image, or fixture route) by the design risk you need to retire
- Setup for design input in Claude Code, Codex, and Cursor, with the Figma and Playwright MCP servers where they help
- Three copy-paste prompts: propose directions, build the inspectable prototype, and compare the implementation with the accepted frames
- An
acceptance.mdtemplate with states, breakpoints, criteria, approver, and date - A verification path that proves the built screen matches the mock without anyone reading the component code
- The failure modes of agent-made mocks, and how to recover from each
For large, multi-screen work, this single artifact grows into the four-artifact design-to-code pipeline. This page is the lightweight version that earns the Q7 maximum on everyday features.
How is Q7 scored, and what moves you up?
Section titled “How is Q7 scored, and what moves you up?”The developer scorecard awards zero to three points. Each step adds something a person other than the agent can inspect.
| Points | Your answer today | The next move |
|---|---|---|
| 0 | No visual prototyping; code goes straight into frontend files | For the next UI change, ask the agent for a mock before any component code |
| 1 | Static screenshots pasted into chat as one-off references | Save the references in design/<feature>/ so the next session and the reviewer see the same input |
| 2 | Figma designs inspected through the Figma MCP server or image attachments | Add the missing half: states, breakpoints, and a named approver recorded in acceptance.md before code starts |
| 3 | intent.md produces an inspectable mock through an approved design tool, iterated before code generation | Keep it honest: check the implementation against the accepted frames with evidence, not by eye |
Which mock medium fits the design risk?
Section titled “Which mock medium fits the design risk?”Pick the cheapest medium that answers the question you are unsure about. A mock that looks finished but answers the wrong question is the most expensive option.
| Medium | Answers | Weak at | Use when |
|---|---|---|---|
| Figma frame, read through the Figma MCP server | Design-system fit: components, tokens, spacing | Real interaction; seat limits on some plans | Your team designs in Figma and the frame already exists |
| HTML prototype in the repository, built from your real tokens | Interaction, focus order, responsive behaviour | Pixel polish; it is thrown away | The risk is how the screen behaves, not how it looks |
Generated image (Codex image_generation, any image model) | Visual direction and hierarchy | Everything else: text, states, feasibility | You need three different directions in minutes, before anyone commits |
Fixture route inside the app (/prototype/...) | High fidelity with real components | Cost; it drifts toward production code | The screen is mostly existing components in a new arrangement |
A generated image is a direction, never the accepted artifact. Its text is often wrong and its components rarely exist in your design system, so accept an HTML prototype or a Figma frame built from it instead.
Connect design input in Claude Code, Codex, and Cursor
Section titled “Connect design input in Claude Code, Codex, and Cursor”All three tools read image files, and all three can use the official Figma MCP server and Playwright MCP. The install commands differ; the loop below is the same.
# Figma: the plugin bundles the remote MCP server and Figma's skillsclaude plugin install figma@claude-plugins-official# or the server alone, then run /mcp in a session to log inclaude mcp add --transport http figma https://mcp.figma.com/mcp
# Playwright, so the agent can open and screenshot the prototypeclaude mcp add playwright -- npx @playwright/mcp@latest --isolatedReference exported frames by path in the prompt (design/settings/refs/current.png); Claude Code reads image files directly. Version 2.1.283 also bundles a /design skill, marked research preview in the skills documentation. Treat whatever it produces as a mock to accept, not as the acceptance record. The --chrome flag enables the Claude in Chrome integration for exploring a running app.
codex mcp add figma --url https://mcp.figma.com/mcpcodex mcp login figma
codex mcp add playwright -- npx @playwright/mcp@latest --isolatedAttach images with -i / --image, interactively or with codex exec (0.157.1). image_generation, view_image, and in_app_browser are all stable and on in codex features list for 0.157.1, so Codex can draft direction images and open the prototype without extra setup.
Add both servers to .cursor/mcp.json in the repository so the whole team shares them:
{ "mcpServers": { "figma": { "url": "https://mcp.figma.com/mcp" }, "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--isolated"] } }}This shape comes from the vendors’ READMEs and was not tested in a Cursor binary for this page (2026-09-26). Reference frames and screenshots with @ in the Agent prompt.
Run the visual acceptance loop
Section titled “Run the visual acceptance loop”The loop produces one folder per feature. Everything the approver and the implementing agent need lives there:
design/settings/ refs/ # current screen, competitor references, brand examples directions/ # two or three direction mocks, rejected ones kept prototype.html # the inspectable mock accepted/ # frames captured from the accepted prototype, per breakpoint and state acceptance.md # the signed record-
Add the visual section to
intent.md. Before the agent draws anything, write three facts it cannot guess: the one task the screen must make obvious, the states that exist (empty, loading, error, success, permission denied, destructive confirmation), and the breakpoints that matter. If you cannot name the states, the mock will only show the happy path. -
Ask for materially different directions. Directions should differ in hierarchy, density, or navigation, not in colour. The agent writes no production code in this step.
The owner picks one. Record the choice and one line per rejected direction; the reasons stop the same idea from returning during implementation.
-
Build the inspectable prototype. Turn the chosen direction into
prototype.html, a single file that loads your real token stylesheet and shows every state behind a switch. It is inspectable because anyone can open it in a browser, resize it, and tab through it.The prompt serves the prototype over HTTP because Playwright MCP blocks
file://navigation and limits file access to the workspace roots by default (checked on@playwright/mcp0.0.83). Serving from the repository root lets the prototype load/src/styles/tokens.cssexactly as the app does, and the same URL is used for the signed frames in step 4. -
Capture the frames the approver signs. Screenshot each state at each breakpoint into
accepted/. Run this in a terminal from the repository root, against the same server as step 3;npx playwright screenshotships with Playwright (checked on 1.63.0). Install the browser once withnpx playwright install chromium:Terminal window python3 -m http.server 8123 & # skip if the step 3 server is still runningfor state in ready empty loading error permission-denied confirm-delete; dofor vp in "375,812" "1280,800"; donpx playwright screenshot --full-page --viewport-size="$vp" \"http://localhost:8123/design/settings/prototype.html?state=$state" \"design/settings/accepted/$state-${vp%%,*}.png"donedoneTwelve images are faster to review than one live prototype, and they are the fixed reference the implementation is compared with later.
-
Iterate, then sign
acceptance.md. The owner reviews the frames and the prototype, not a chat transcript. Feedback goes back to the agent as numbered changes; the agent regenerates the frames. When the owner accepts, record it:# Visual acceptance: settings screenIntent: intent.md#settings (commit a1b2c3d)Accepted artifact: design/settings/prototype.html (commit e4f5a6b)Frames: design/settings/accepted/*.png (6 states x 2 breakpoints)Figma: none (or a version-pinned frame link)## Acceptance criteria1. The primary action "Save changes" is the only filled button in every state.2. empty: explains how to add the first member and offers one action.3. error: keeps the user's input and names the failed field.4. permission-denied: shows who can grant access; no disabled form.5. confirm-delete: names the workspace and needs the typed name.6. 375px: no horizontal scroll; the primary action stays in view.7. Keyboard: every control reachable by Tab in visual order; focus visible.8. No serious or critical axe violations.Approved by: APPROVER_NAME (product design), 2026-09-26Out of scope: dark theme (next iteration)APPROVER_NAMEis the person who owns the decision, not the agent or the implementing developer. Numbered criteria are the point. Each one becomes a check in the next step or an executable acceptance criterion inspec.md. -
Hand the folder to the build. Commit
design/settings/with the feature branch. The implementing agent readsacceptance.mdand theaccepted/frames during planning, and the plan names which criteria each test covers.
How do you prove the built screen matches the mock without reading the code?
Section titled “How do you prove the built screen matches the mock without reading the code?”You check the screen against the accepted frames and the numbered criteria. The component code is judged by gates, not by a human reading it line by line.
Run a comparison in a fresh, read-only context. The session that built the screen is the worst judge of it. A separate reviewer opens the running app at each state and breakpoint, compares it with accepted/, and reports only mismatches it can show.
Save the prompt as design/settings/review-prompt.md. Start the app with its fixtures in another terminal first (for example npm run dev); the reviewer has no shell and cannot start it. Then run the review without write access:
claude -p --permission-mode dontAsk \ --allowedTools "Read Grep Glob mcp__playwright" \ < design/settings/review-prompt.mddontAsk denies every tool call that would prompt and is not in --allowedTools, so the reviewer can read files and drive the browser but cannot edit them. The prompt arrives on stdin because --allowedTools takes a variadic list: a prompt placed after it is read as one more tool name, and claude -p exits with “Input must be provided”.
codex exec -s read-only \ -i design/settings/accepted/*.png \ < design/settings/review-prompt.mdThe shell glob attaches all twelve frames. The prompt comes from stdin, because -i accepts several files and would otherwise read a trailing prompt as another image path. Codex can also read other images from disk with the stable view_image tool, but attaching the frames makes the reference explicit.
Start a new Agent chat, not the one that built the screen. Paste the prompt, @-reference acceptance.md and the accepted/ frames, and keep the Playwright MCP server enabled so the reviewer can open the app.
Lock the result with screenshot baselines. Turn each state into a Playwright test with toHaveScreenshot() and an axe scan; the four-artifact pipeline has a complete spec pattern. The first run creates the baselines. The approver compares them once with accepted/, side by side, and from then on any visual change fails CI until a human approves it.
Who signs what:
| Check | Proves | Signed by |
|---|---|---|
acceptance.md | The mock answers the intent, in every state and breakpoint | Product or design owner |
| Fresh-context review report | The built screen matches the accepted frames, criterion by criterion | Approver accepts or rejects each finding |
| Screenshot baselines | Nothing changes visually without approval after merge | Design (via CODEOWNERS on the snapshot directory) |
| axe scan and focus assertions | Criteria 7 and 8 hold | Code owner |
Put the snapshot directory under a CODEOWNERS entry owned by design, and treat an agent-run npx playwright test --update-snapshots as a failed task. The general rule is on protecting the oracle from the agent. Attach the review report and the passing run to the pull request’s evidence bundle.
What breaks when agents make the UI mock?
Section titled “What breaks when agents make the UI mock?”Only the happy path was mocked. The owner approved a beautiful ready state; the error state ships blank. Recover by listing the states in intent.md first, regenerating the prototype with the state switcher, and refusing to sign acceptance.md while any accepted/ frame is missing.
The mock uses components you do not have. The generated direction shows a segmented control and a colour that exist nowhere in the design system, and the implementation invents both. Recover by adding “reuse only src/components/ui/, flag anything new” to the direction prompt, and grep the prototype for raw colours (rg -n ':\s*#[0-9a-fA-F]{3,8}\b' design/settings/prototype.html matches a property value that starts with a hex colour and must print nothing).
The accepted version lives only in chat. The owner said “option B, but tighter” in a thread, and the next session cannot see it. Recover by treating anything not in acceptance.md as not accepted. Summarise the thread into numbered changes, regenerate the frames, and sign the file.
The approver compares by eye. A review of “looks close enough” misses a missing field label at 375px. Recover by making every criterion in acceptance.md checkable, and letting the fresh-context review report mismatches per criterion instead of giving an overall impression.
The agent that built the screen reviews it. It reports “matches the design” because it remembers its intent, not because it looked. Recover by running the comparison prompt in a new read-only session and requiring a screenshot path for every finding.
The prototype drifts into production code. The fixture route grows real data calls and becomes the feature. Recover by keeping prototypes in design/ or behind a /prototype/ route that the build excludes, and by starting implementation from acceptance.md, not by editing the prototype.
Check visual acceptance before you merge
Section titled “Check visual acceptance before you merge”-
intent.mdnames the task, the states, and the breakpoints. -
accepted/holds a frame for every state at every breakpoint. -
acceptance.mdhas numbered criteria, a commit reference, an approver, and a date. - The fresh-context review report has no open blockers.
- Screenshot baselines were approved against
accepted/by a human, not generated and accepted by the agent. - Design tools and image models stayed inside the approved data boundary.
Where to go next with visual acceptance
Section titled “Where to go next with visual acceptance”Back to the developer scorecard answer key.
Frequently asked questions
What is visual acceptance before frontend implementation?
Visual acceptance is a gate between intent.md and frontend code where an agent produces an inspectable UI mock, a named product or design owner signs a versioned acceptance.md, and the implemented screen is checked against those frames before merge.
What media can represent an inspectable UI mock for Scorecard Q7?
Depending on the design risk you need to retire, the inspectable mock can be a Figma frame, a repository HTML prototype, a generated image, or a fixture route covering required states and breakpoints.