---
name: visual-design-loop
description: Build and refine web UI — landing pages, marketing sites, dashboards, app screens — so it looks deliberately designed instead of AI-generated. Use this whenever the user asks to build a landing page or web page, wants to improve, polish, restyle, or "make better" an existing frontend, says a design feels generic, templated, or like AI slop, shares design inspiration (screenshots, Dribbble/Pinterest/Awwwards links), or asks for a screenshot-driven iteration pass with Playwright. Use it even when the request is vague ("this page looks off", "clean this up") and even when the user never mentions design systems, references, or tokens.
---

# Visual Design Loop

Most AI-built UI fails for the same three reasons: the model starts coding from a brief it never pinned down, it invents styling values ad hoc so nothing coheres, and it never looks at what it rendered. This skill closes those gaps with a fixed sequence — extract a real brief, lock a token system before writing CSS, then iterate against actual screenshots until the page holds up.

**Companion skill:** `frontend-design` (Anthropic's, if installed) covers aesthetic judgment — typography pairing, signature elements, restraint. This skill covers the process around it: reference intake, token lock, screenshot critique, convergence. If `frontend-design` is available, read it at the start of Phase 2 and use its judgment inside this process. They are complementary, not alternatives.

**Bundled resources — read these when the phase calls for them:**
- `references/reading-references.md` — turning inspiration images into a transferable brief
- `references/critique-rubric.md` — the seven critique passes run against each screenshot
- `references/anti-slop.md` — the specific defaults that read as AI-generated, and what to do instead
- `scripts/shoot.mjs` — Playwright capture + automated audit (overflow, contrast, tap targets, token sprawl, console errors)

---

## Phase 0 — Workspace and safety net (30 seconds, do not skip)

Iteration only works if you can compare passes and revert regressions. Set up:

```
.design/
  DESIGN.md      # brief + locked tokens — the single source of truth
  refs/          # inspiration images the user provided
  pass-01/       # screenshots + audit.json for each iteration
  pass-02/
```

Commit (or `git stash` a clean baseline) before touching anything. Commit at the end of every pass with the pass number in the message. Visual work regresses constantly — the ability to say "pass 3 was better, go back" is worth more than any single clever change.

---

## Phase 1 — Get a brief worth building from

Three inputs, in priority order.

**1. The subject.** What is this page for, who lands on it, and what single thing should they do? If the user hasn't said, name your best guess concretely and state it — never design for a generic "SaaS product." A page for "an API for scraping court records, sold to paralegals" produces different, better decisions than a page for "a tech startup."

**2. The references.** If the user supplies inspiration images or links, read `references/reading-references.md` before doing anything else. The critical move: **do the read-back yourself.** Don't ask the user to explain why the references work — they often can't articulate it, and asking makes them do your job. Instead, state what you extracted ("all three use a two-column asymmetric hero with the type set flush left against a hard vertical rule; none use rounded corners; the accent color appears exactly once per screen") and ask them to correct you. A wrong read-back gets corrected in ten seconds; an unasked question costs ten minutes.

**3. The constraints.** Ask for these in one batch, not one at a time:
- Existing brand, colors, fonts, or component library to stay inside?
- Anything explicitly banned? (Ask this. Users have strong negative preferences — an overused accent color, a competitor's look, a font their last agency used — and volunteering them is the cheapest way to avoid a wasted pass.)
- Stack and constraints: framework, CSS approach, dark mode, browser support, real content or placeholder?

**If the user has no references**, do not send them off to hunt for some. Propose three genuinely divergent directions instead — each a named concept, a 4–6 value palette, a type pairing, and a five-line ASCII wireframe — and ask them to pick one or splice two. This takes you two minutes and beats a Pinterest session, because the options are already grounded in their subject.

---

## Phase 2 — Lock the direction before writing any CSS

Write `.design/DESIGN.md` and get explicit sign-off on it. This is the step that separates coherent design from drift: every later pass derives from this file, so "make it feel more like reference 2" resolves to a concrete diff instead of a fresh guess.

```markdown
# Design direction — [subject]

## Thesis
One sentence: what this page is and the one feeling it should produce.

## Signature
The single element this page is remembered by. Everything else stays quiet.

## Palette (4–6 values, each with a job)
--bg / --surface / --ink / --ink-muted / --accent / --line
Hex + where each is allowed to appear. The accent has a usage budget: name it.

## Type
Display: [family, weight, tracking, where used]
Body: [family, weight, measure in ch]
Scale: exact values, 5–7 steps max. Name them.

## Space
Base unit + the ramp. Section rhythm. Max content width.

## Shape & depth
Radius: one value, or a deliberate two. Borders vs shadows — pick one primary.

## Motion
What animates, on what trigger, how long. Or "none" — a valid answer.

## Explicitly not doing
The defaults rejected for this brief, and why.
```

Before you show it, run a self-check: **would this exact token set be a plausible answer for a completely different product?** If yes, it's a default rather than a choice. Revise the part that's generic and say what you changed. Check the result against `references/anti-slop.md`.

---

## Phase 3 — Build to the spec

- Tokens become CSS custom properties (or theme config) at the top of the file. **No magic values below that line.** Every color, size, and radius in the code resolves to a token. This is what makes later passes mechanical instead of archaeological.
- Write real copy, not lorem and not marketing mush. Generic copy makes a good design look templated. Specific nouns from the subject's actual world do more for perceived quality than any gradient.
- Build the mobile layout as a real layout, not a squeezed desktop one.
- Ship the quality floor without narrating it: keyboard focus visible, reduced-motion respected, images sized, semantic landmarks.
- Watch CSS specificity collisions — element-level and class-level rules that cancel each other out on section padding are the most common source of "why won't this gap change."

---

## Phase 4 — The loop (the part everyone skips)

One pass = **shoot → audit → critique → batch-fix → commit**. Do not show the user intermediate states; show them at checkpoints.

### Shoot

Preferred: this skill's `scripts/shoot.mjs` — captures three viewports full-page and runs the audit in one command. Copy it to the project once (`cp <skill-dir>/scripts/shoot.mjs scripts/`) so later passes and the user can rerun it easily.

```bash
npm i -D playwright && npx playwright install chromium   # first run only
node scripts/shoot.mjs http://localhost:3000 .design/pass-01
node scripts/shoot.mjs http://localhost:3000 .design/pass-02 --dark --wait "main"
```

Alternatives, in order of preference:
1. **Playwright MCP** — better when you need to interact (open menus, submit forms, hover states, scroll-triggered animation). Navigate, resize, screenshot, and read console errors.
2. **chrome-devtools MCP** — fine substitute.
3. **`npx playwright screenshot --full-page --viewport-size=1440,900 <url> out.png`** — no install ceremony beyond the browser download.
4. **No browser access at all** — say so plainly and ask the user to paste screenshots. Don't fake this step; a critique of code you haven't seen rendered is guesswork.

Capture at minimum: 390×844, 768×1024, 1440×900, full-page. Add dark mode if it exists, plus any state that isn't visible at rest — hover, focus, open menu, form error, empty state, long-content overflow.

### Audit

`shoot.mjs` writes `audit.json` with mechanical failures: horizontal overflow, text below WCAG contrast, sub-44px tap targets, console errors, and **token sprawl** (counts of distinct font sizes, families, colors, radii, shadows actually computed on the page). Sprawl is the best objective proxy for "looks unplanned" — 14 font sizes and 9 grays is a design that was assembled rather than designed. Fix every mechanical failure before spending any judgment on aesthetics.

### Critique

Read `references/critique-rubric.md` and run the seven passes against the screenshots. Write findings as a numbered diff list, each item naming the element, the problem, and the specific change — "hero h1 is 48px at 1440, one step above body; take it to the display step at 76px and drop tracking to -0.02em," not "improve hierarchy." If you can't state the fix concretely, you haven't finished looking.

Then check the list against DESIGN.md. Any fix that requires a value not in the spec means either the fix is wrong or the spec is incomplete — resolve that explicitly, and update DESIGN.md if the spec loses.

### Batch-fix

Apply the whole diff list in one edit round, then re-shoot. Fixing one thing at a time burns passes and hides interactions between changes. Cap each pass at the top 5–8 items by impact; a long tail of nitpicks belongs in a later pass or nowhere.

### Commit

`git commit -m "design pass 02: hero hierarchy, spacing ramp, mobile nav overflow"`. Keep `.design/pass-NN/` — before/after screenshots are how you and the user tell whether a pass actually helped.

---

## Stopping

Run **3–5 passes**, then stop and show the user. Signs you've converged:
- The audit is clean and the critique list is down to preferences rather than problems.
- Two consecutive passes produce changes you'd struggle to defend to the user.
- You start adding decoration rather than removing it — the reliable sign that iteration has turned into fidgeting.

Then hand back: the page, before/after screenshots, what changed, and the one or two judgment calls you'd like a human opinion on. Ask a narrow question ("the accent on the pricing CTA — too loud?"), not "what do you think?"

If the user says it's good, it's good. Don't keep polishing past their sign-off.

---

## Notes that matter in practice

- **Reasoning model for layout, any model for edits.** Composition, hierarchy, and the token system reward the strongest available model; applying a settled diff list doesn't. If the user is switching models, do Phases 1–2 and the critique step on the strong one.
- **The user's words always win.** If they ask for the thing `anti-slop.md` warns about, build it well and say nothing.
- **Never claim you looked when you didn't.** "I took a screenshot and the spacing looks better" without an actual capture is the single fastest way to lose the user's trust in this whole loop.
