diff --git a/stack/.claude/agents/design-review.md b/stack/.claude/agents/design-review.md new file mode 100644 index 0000000..9ada577 --- /dev/null +++ b/stack/.claude/agents/design-review.md @@ -0,0 +1,96 @@ +--- +name: design-review +description: >- + Expert design reviewer for web UI. Use PROACTIVELY after any front-end change and before + calling UI work complete, or when the user asks to review/audit a page, screen, or PR for + visual quality, responsiveness, or accessibility. Drives a real browser (Playwright MCP) + across viewports, checks WCAG 2.1 AA, and returns ranked, evidence-based findings. +tools: mcp__playwright, mcp__chrome-devtools, Read, Grep, Glob, Bash +model: sonnet +--- + +You are a senior product design reviewer — the kind who has shipped and audited interfaces at +the level of Stripe, Linear, and Airbnb. You do not guess from the code; you **open the page in +a real browser and observe it**. Every finding is backed by something you saw (a screenshot, a +console message, a measured value), never by assumption. + +## Operating principle: assess the live experience first + +Before reading a single line of source, interact with the running UI like a user would. Read +code only to explain a defect you already observed or to locate its fix. Screenshots and +observed behavior are your primary evidence. + +## Inputs you need + +- A **URL** (preferred, e.g. `http://localhost:3000/pricing`) or a **file path** to open. +- If neither is given, ask for the dev-server URL, or fall back to + `node scripts/design-audit.mjs` against the file/URL for a heuristic-only pass. + +## The 7-phase review + +Work through every phase. Take a screenshot at the start of each visual phase so findings are +anchored to evidence. + +**Phase 0 — Setup.** Open the page in Playwright at 1440×900. Confirm it renders and capture a +baseline screenshot. Note any console errors/warnings immediately (they often explain visual +bugs). + +**Phase 1 — Interaction & user flows.** Exercise the primary flow. Click buttons, open menus +and modals, submit forms (valid and invalid), toggle tabs/accordions. Verify: hover, active, +and disabled states exist and differ; destructive actions are guarded; loading/empty/error +states are handled, not blank. + +**Phase 2 — Responsiveness.** Resize through the tiers and screenshot each: **375 (mobile), +768 (tablet), 1024 (laptop), 1440 (desktop), 1920 (wide)**. Look for horizontal scroll, +clipped/overlapping content, images that shrink instead of reflow, tap targets < 44×44 px on +mobile, and navigation that doesn't collapse. + +**Phase 3 — Visual polish.** Judge spacing rhythm and alignment, a consistent type scale, +consistent radii/shadows/borders (design-token discipline), image quality, and color harmony. +Flag misalignment, inconsistent spacing, and decoration that serves nothing. + +**Phase 4 — Accessibility (WCAG 2.1 AA).** Tab through the whole page: focus must be visible +and follow a logical order, with no keyboard traps. Check semantic structure (one `h1`, ordered +headings, landmarks), labels on all controls, `alt` on meaningful images, and text contrast +(≥ 4.5:1 body, ≥ 3:1 large text / UI). Use the accessibility snapshot from the MCP; verify +`prefers-reduced-motion` is respected. + +**Phase 5 — Robustness / edge cases.** Stress it: very long strings, empty data, slow network +(loading states), and (if forms) invalid input. Content should degrade gracefully, never break +layout. + +**Phase 6 — Console & health.** Re-check the console/network for errors, failed requests, 404 +assets, layout-shift warnings, and oversized payloads. Chrome DevTools MCP for perf/CLS if the +change is performance-sensitive. + +## How to report + +Return Markdown with this structure. Rank by severity; lead with what to fix, not a lecture. + +``` +## Design Review — +**Verdict:** · Viewports checked: 375/768/1024/1440/1920 + +### Blockers (breaks usability or fails AA — must fix) +- [What you observed] → [why it fails] → [the fix] · evidence: + +### High (significant, fix before merge) +- ... + +### Medium (polish; noticeably better if fixed) +- ... + +### Nitpicks (prefix each with "Nit:") +- ... + +### What's working +- Call out genuinely good decisions so they're preserved. +``` + +Rules: +- Start every problem with the **observation**, then the principle, then the fix. Assume + competence — explain the *why*, don't prescribe pixel values unless asked. +- Distinguish "broken" from "I'd prefer." Only Blockers/High should gate merging. +- If you could not open the page, say so plainly and report only the heuristic-script results — + never invent findings. +- Keep it evidence-based: no finding without something you observed. diff --git a/stack/.claude/commands/design-plan.md b/stack/.claude/commands/design-plan.md new file mode 100644 index 0000000..92611ee --- /dev/null +++ b/stack/.claude/commands/design-plan.md @@ -0,0 +1,26 @@ +--- +description: Generate a concrete design system (tokens, style, type, UX) before building UI +argument-hint: , e.g. "fintech dashboard trustworthy data-dense" +--- + +Before writing any markup, produce a design system for: **$ARGUMENTS** + +1. Run the `ui-ux-pro-max` design-system generator to get style + color tokens + typography + + UX anti-patterns: + ```bash + python3 /scripts/search.py "$ARGUMENTS" --design-system -p "Project" + ``` + (Resolve `` from the installed skill — see CLAUDE.md.) + +2. Pull anything the brief needs specifically, e.g.: + - `--domain color " "` for the palette / semantic tokens + - `--domain typography ""` for font pairing + imports + - `--domain web-vitals ""` for the performance budget + - `--domain ux ""` for do/don't guidance + +3. Then apply the **frontend-design** lens: state purpose / tone / constraints / differentiation, + pick ONE tone, choose a single signature element, and reject any choice that reads like a + generic AI default. + +Output a compact token block (4–6 named colors, 2–3 type roles, spacing scale, one signature +element) and a one-paragraph rationale. Do **not** start building until the tokens are decided. diff --git a/stack/.claude/commands/design-review.md b/stack/.claude/commands/design-review.md new file mode 100644 index 0000000..d2a5988 --- /dev/null +++ b/stack/.claude/commands/design-review.md @@ -0,0 +1,15 @@ +--- +description: Run the 7-phase design review (WCAG AA, responsive, interaction) on a page or URL +argument-hint: [url-or-file] [optional focus, e.g. "mobile nav" or "checkout flow"] +--- + +Use the **design-review** subagent to audit the target below. Drive a real browser via the +Playwright MCP, screenshot each viewport tier, and return ranked findings (Blockers → Nitpicks). + +Target: $1 +Focus (optional): $2 + +If no target was given, ask for the running dev-server URL (or a file path). If a browser +cannot be opened, fall back to `node scripts/design-audit.mjs` and report only the heuristic +findings, clearly labeled as such. Fix Blockers and High-severity findings before reporting the +work as complete. diff --git a/stack/.claude/settings.json b/stack/.claude/settings.json new file mode 100644 index 0000000..09360cc --- /dev/null +++ b/stack/.claude/settings.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "enableAllProjectMcpServers": true, + "permissions": { + "allow": [ + "Bash(node scripts/design-audit.mjs:*)", + "Bash(npm run audit:*)", + "Bash(npx playwright:*)", + "Read(//home/**)", + "mcp__playwright", + "mcp__chrome-devtools", + "mcp__shadcn" + ] + } +} diff --git a/stack/.github/workflows/design-review.yml b/stack/.github/workflows/design-review.yml new file mode 100644 index 0000000..e23b9d0 --- /dev/null +++ b/stack/.github/workflows/design-review.yml @@ -0,0 +1,47 @@ +name: Design Review + +# Runs the heuristic design audit on PRs that touch UI. The full taste/interaction review is +# the /design-review subagent inside Claude Code; this CI gate catches the mechanical defects +# (overflow, focus, contrast, unsized media, missing meta) automatically on every PR. + +on: + pull_request: + paths: + - '**/*.html' + - '**/*.css' + - '**/*.jsx' + - '**/*.tsx' + - '**/*.vue' + - '**/*.svelte' + - '**/*.astro' + +jobs: + audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install deps + Chromium + run: | + npm install + npx playwright install --with-deps chromium + + # Point --url at your preview/deploy URL, or --file at a built static entry point. + # Replace the target below with how your app serves a page in CI. + - name: Run design audit + run: | + node scripts/design-audit.mjs --file ./index.html --out audit-output || true + + - name: Upload audit report + screenshots + if: always() + uses: actions/upload-artifact@v4 + with: + name: design-audit + path: audit-output/ + + - name: Fail on high-severity findings + run: node scripts/design-audit.mjs --file ./index.html --out audit-output diff --git a/stack/.gitignore b/stack/.gitignore new file mode 100644 index 0000000..4185f0d --- /dev/null +++ b/stack/.gitignore @@ -0,0 +1,27 @@ +# Dependencies +node_modules/ +package-lock.json +pnpm-lock.yaml +yarn.lock + +# Audit / Playwright output (regenerate locally; examples/ is committed on purpose) +audit-output/ +test-results/ +playwright-report/ +.playwright/ + +# Local Claude Code state +.claude/settings.local.json +.claude/.session/ + +# Env / secrets +.env +.env.* +!.env.example + +# OS / editor +.DS_Store +Thumbs.db +*.log +.idea/ +.vscode/ diff --git a/stack/.mcp.json b/stack/.mcp.json new file mode 100644 index 0000000..f1e2fe2 --- /dev/null +++ b/stack/.mcp.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json.schemastore.org/mcp.json", + "mcpServers": { + "playwright": { + "command": "npx", + "args": ["-y", "@playwright/mcp@latest"] + }, + "chrome-devtools": { + "command": "npx", + "args": ["-y", "chrome-devtools-mcp@latest"] + }, + "shadcn": { + "command": "npx", + "args": ["-y", "shadcn@latest", "mcp"] + } + } +} diff --git a/stack/CLAUDE.md b/stack/CLAUDE.md new file mode 100644 index 0000000..85906db --- /dev/null +++ b/stack/CLAUDE.md @@ -0,0 +1,73 @@ +# CLAUDE.md — Website Design Stack + +This repository is a **ready-to-use environment that makes Claude Code good at website +design**. When you (Claude) do any UI, web page, landing page, component, or visual-polish +work in a project that uses this stack, follow the workflow below. It combines a *knowledge* +layer (what to build), a *taste* layer (making it distinctive), and a *feedback* layer +(actually seeing the rendered result and fixing it). + +## The design loop (follow in order) + +1. **PLAN with data — `ui-ux-pro-max`.** Before writing markup, get a concrete design + system. Run the generator, then pull specifics per surface: + ```bash + python3 /scripts/search.py " " --design-system -p "Project" + python3 /scripts/search.py "" --domain style|color|typography|ux|landing|web-vitals + ``` + Use it for: product-type patterns, color tokens, font pairings, UX anti-patterns, + landing structure, and Core Web Vitals budgets. Treat its output as the source of truth + for tokens (color, type, spacing). + +2. **COMMIT to an aesthetic — `frontend-design`.** Do not sample the safe center of the + training distribution. Answer four questions first — *purpose, tone, constraints, + differentiation* — pick ONE tone and execute it precisely. Avoid the three AI-slop + defaults (cream + serif + terracotta; near-black + acid accent; hairline broadsheet) + unless the brief explicitly asks. Spend boldness in **one** signature element; keep the + rest quiet. + +3. **BUILD.** Implement with the chosen tokens. Match the surrounding code's conventions. + For component-driven stacks, use the **shadcn** MCP to search/add components instead of + hand-rolling primitives. + +4. **SEE IT — Playwright / Chrome DevTools MCP.** You are not done when the code compiles. + Open the page in a real browser, screenshot it, read the console, exercise interactive + states (hover, focus, open menus, submit forms), and resize the viewport. Fix what you + see — z-index, animation timing, layout shift, overflow. This feedback loop is the whole + point of the stack; a change you have not looked at is not finished. + +5. **REVIEW — `/design-review` (the `design-review` subagent).** Before you call a UI change + complete, run the design-review subagent. It drives Playwright across mobile→ultrawide + viewports, checks WCAG 2.1 AA (contrast, focus order, keyboard traps), responsive + integrity, and interaction states, and returns ranked findings. Fix Blocker/High + findings before finishing. + +## Quality floor (never ship below this) + +- **Responsive:** no horizontal scroll at 375 / 768 / 1024 / 1440 px; content reflows, not shrinks. +- **Accessible:** visible `:focus-visible` on every interactive element; WCAG AA contrast + (4.5:1 text, 3:1 large text / UI); semantic landmarks; labelled controls; `prefers-reduced-motion` respected. +- **Performant:** stable layout (no CLS from unsized media/fonts), lazy-load below-fold + images, `font-display: swap`, avoid render-blocking. Check against the `web-vitals` domain. +- **Intentional copy:** active voice, sentence case, name things by what users recognize. + +## What's wired in this repo + +| Layer | Tool | Where | +|-------|------|-------| +| Knowledge | `ui-ux-pro-max` skill | installed via `scripts/setup.sh` (plugin) | +| Taste | `frontend-design` skill | installed via `scripts/setup.sh` (plugin) | +| Component gen | `shadcn` MCP | `.mcp.json` | +| Visual feedback | `@playwright/mcp` + `chrome-devtools-mcp` | `.mcp.json` | +| Automated review | `design-review` subagent + `/design-review` command | `.claude/agents`, `.claude/commands` | +| Standalone audit | `scripts/design-audit.mjs` (multi-viewport screenshots) | `scripts/`, CI in `.github/workflows` | + +See `docs/STACK.md` for why each tool is here, `docs/SETUP.md` to install, and +`docs/WORKFLOW.md` for a worked end-to-end example. + +## Notes + +- `` above resolves to the installed skill path (e.g. `.claude/skills/ui-ux-pro-max` + or the plugin location). The skill's own `SKILL.md` tells you the exact command form. +- Optional: 21st.dev **Magic** MCP (`@21st-dev/magic`) generates React components from a + prompt but needs an API key — see `docs/SETUP.md`. It is intentionally left out of the + default `.mcp.json` so the stack works with zero secrets. diff --git a/stack/README.md b/stack/README.md new file mode 100644 index 0000000..107c463 --- /dev/null +++ b/stack/README.md @@ -0,0 +1,96 @@ +# Claude Website Design Stack + +A ready-to-clone Claude Code project that makes the agent **genuinely good at website +design** — not by hoping for better output, but by wiring in the three things generic AI +design is missing: a **knowledge** layer, a **taste** layer, and a **visual feedback** layer. + +> Most AI design fails because the model never *sees* what it built and has no opinionated +> point of view. This repo fixes both: Claude plans with a real design database, commits to +> a distinctive aesthetic, then opens the page in a browser and iterates on what it sees — +> and self-reviews against WCAG AA before finishing. + +## The stack + +| Layer | Tool | What it adds | +|-------|------|--------------| +| 🧠 **Knowledge** | [`ui-ux-pro-max`](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill) | 84 UI styles, 192 color palettes, 73 font pairings, 99 UX rules, Core Web Vitals, 22 stacks — searchable, turned into a concrete design system | +| 🎨 **Taste** | [`frontend-design`](https://github.com/anthropics/claude-code/tree/main/plugins/frontend-design) (official Anthropic) | Forces a real aesthetic commitment before any CSS; kills "AI slop" defaults | +| 🧩 **Components** | [`shadcn` MCP](https://ui.shadcn.com/docs/mcp) | Search/add production components by natural language | +| 👁️ **Visual feedback** | [`@playwright/mcp`](https://github.com/microsoft/playwright-mcp) + [`chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp) | Claude opens a real browser, screenshots, reads the console, exercises states, and fixes what it sees | +| ✅ **Automated review** | `design-review` subagent + `/design-review` | 7-phase audit across viewports: WCAG AA, responsive, interaction states | + +## Quickstart + +```bash +# 1. Clone and enter +git clone https://github.com/YMungerDev/claude-website-design-stack +cd claude-website-design-stack + +# 2. Install the skills/plugins + Playwright browser +./scripts/setup.sh + +# 3. Open Claude Code — the MCP servers and CLAUDE.md workflow load automatically +claude +``` + +Then just ask for UI work. Claude will plan → commit to a look → build → **screenshot and +iterate** → run `/design-review`. See `docs/WORKFLOW.md` for a full walkthrough. + +## Try it in 30 seconds + +``` +> Build a pricing section for a developer tool. Plan it with ui-ux-pro-max, +> commit to a distinctive look, then screenshot it at mobile and desktop and fix any issues. +``` + +## What's in here + +``` +. +├── .mcp.json # Playwright + Chrome DevTools + shadcn MCP servers +├── CLAUDE.md # The design loop Claude follows (the "brain") +├── .claude/ +│ ├── settings.json # Enables the MCP servers + safe permissions +│ ├── agents/design-review.md # 7-phase design-review subagent +│ └── commands/ +│ ├── design-review.md # /design-review — audit a URL/page +│ └── design-plan.md # /design-plan — generate a design system first +├── scripts/ +│ ├── setup.sh # Installs skills/plugins + Playwright chromium +│ └── design-audit.mjs # Standalone multi-viewport screenshot + heuristic audit +├── .github/workflows/ +│ └── design-review.yml # Runs the audit on every PR that touches UI +├── docs/ +│ ├── STACK.md # Why each tool is in the stack +│ ├── SETUP.md # Install details + optional add-ons (Figma, Magic) +│ └── WORKFLOW.md # End-to-end worked example +└── examples/juniper-audit/ # Real audit run against a production site +``` + +## Standalone audit (no MCP needed) + +The same checks are available as a plain script for local runs and CI: + +```bash +node scripts/design-audit.mjs --url http://localhost:3000 --out audit-output +# or audit a static file +node scripts/design-audit.mjs --file ./index.html +``` + +It screenshots 7 viewport tiers (360 → 1920 px) and reports heuristic findings +(horizontal overflow, missing focus styles, unsized media, contrast risks, tap-target size). +See `examples/juniper-audit/` for a real report. + +## Requirements + +- Node 18+ and Python 3.x +- Claude Code with plugins enabled (`/plugin`) + +## License + +MIT. The bundled tools keep their own licenses — see `docs/STACK.md` for links. + +--- + +Built as a starter for teams who want Claude Code to ship distinctive, accessible, +production-grade web UI instead of generic AI output. diff --git a/stack/docs/SETUP.md b/stack/docs/SETUP.md new file mode 100644 index 0000000..a0be5a6 --- /dev/null +++ b/stack/docs/SETUP.md @@ -0,0 +1,82 @@ +# Setup + +## Prerequisites + +- **Node 18+** and **Python 3.x** +- **Claude Code** with plugins enabled (run `/plugin` once to enable) + +## 1. One-shot setup + +```bash +./scripts/setup.sh +``` + +This installs the audit dependencies (Playwright + Chromium) and the `ui-ux-pro-max` skill, +then prints the two steps that must happen inside Claude Code. + +## 2. Install the taste plugin (inside Claude Code) + +``` +/plugin install frontend-design@anthropics/claude-code +``` + +## 3. Approve the MCP servers + +Open Claude Code in this directory. It reads `.mcp.json` and prompts you to approve the project +MCP servers (**playwright**, **chrome-devtools**, **shadcn**). Approve them. `.claude/settings.json` +already sets `enableAllProjectMcpServers: true`, so they load on start. Verify with `/mcp`. + +That's it — `CLAUDE.md` defines the design loop and loads automatically. + +## Verify it works + +``` +> /design-plan portfolio site for a photographer, editorial and minimal +> Build the hero, then screenshot it at 375px and 1440px and fix anything that breaks. +> /design-review http://localhost:3000 +``` + +You should see Claude pull tokens from `ui-ux-pro-max`, open a browser, screenshot, and return +ranked findings. + +## Standalone audit (no Claude needed) + +```bash +npm run audit -- --url http://localhost:3000 +npm run audit -- --file ./index.html +``` + +Outputs `audit-output/report.md` + per-viewport screenshots. Exit code is non-zero when there +are high-severity findings (useful for CI — see `.github/workflows/design-review.yml`). + +--- + +## Optional add-ons + +### Figma Dev Mode MCP +Requires the Figma desktop app. Enable Dev Mode → toggle the MCP server on. Then add to +`.mcp.json`: + +```json +"figma": { "url": "http://127.0.0.1:3845/mcp" } +``` + +Guide: + +### 21st.dev Magic MCP +Generates React components from a prompt. Get an API key at , then add: + +```json +"magic": { "command": "npx", "args": ["-y", "@21st-dev/magic@latest"], "env": { "API_KEY": "" } } +``` + +Keep keys out of git — prefer `.claude/settings.local.json` or your shell env, both gitignored. + +## Troubleshooting + +- **`/mcp` shows a server failed** — run its command manually to see the error, e.g. + `npx -y @playwright/mcp@latest`. Usually a Node version or network/proxy issue. +- **Audit can't find Chromium** — run `npx playwright install chromium`, or set + `PW_EXECUTABLE_PATH` to your Chromium binary. +- **ui-ux-pro-max command not found** — re-run `npx ui-ux-pro-max-cli init --ai claude` and + check the printed skill path; use that path in the `python3 …/search.py` commands. diff --git a/stack/docs/STACK.md b/stack/docs/STACK.md new file mode 100644 index 0000000..8672fa0 --- /dev/null +++ b/stack/docs/STACK.md @@ -0,0 +1,67 @@ +# The Stack — why each tool is here + +AI design fails in three predictable ways. Each layer of this stack fixes one. + +| Failure mode | Fix | Tool | +|--------------|-----|------| +| Generic, templated look ("AI slop") | Force an aesthetic commitment | `frontend-design` | +| Vague, inconsistent tokens & patterns | Ground decisions in a real database | `ui-ux-pro-max` | +| Never sees the result → ships broken UI | Give the agent eyes | Playwright / Chrome DevTools MCP | +| Reinventing primitives | Pull proven components | shadcn MCP | +| "Looks fine to me" self-assessment | Independent, rigorous review | `design-review` subagent | + +## 🧠 Knowledge — `ui-ux-pro-max` + +A searchable design-intelligence toolkit: **84 UI styles, 192 color palettes, 73 font +pairings, 99 UX guidelines, 25 chart types, a Core Web Vitals dataset, and 22 tech stacks**, +plus a design-system generator that turns a product brief into concrete tokens. It's the +answer to "what should this actually look like, and what are the anti-patterns?" + +- Repo: · License: MIT +- Install: `npx ui-ux-pro-max-cli init --ai claude` +- Use: `python3 /scripts/search.py "" --design-system` and `--domain ` + +## 🎨 Taste — `frontend-design` (official Anthropic) + +~50 lines of markdown that stop Claude from sampling the safe center of its training data. It +forces four decisions — *purpose, tone, constraints, differentiation* — before any CSS, names +three "AI-slop" defaults to avoid, and pushes boldness into a single signature element. + +- Repo: +- Install: `/plugin install frontend-design@anthropics/claude-code` + +`ui-ux-pro-max` decides *what's correct*; `frontend-design` decides *what's distinctive*. Use both. + +## 🧩 Components — shadcn MCP + +Browse, search, and install shadcn/ui components (and other registries) by natural language, +instead of hand-rolling accessible primitives. Best for React/Next/Vue/Svelte projects. + +- Docs: · Command: `npx shadcn@latest mcp` + +## 👁️ Visual feedback — Playwright MCP + Chrome DevTools MCP + +The single biggest lever. Claude connects to a **real Chromium**, navigates, clicks, resizes, +screenshots, reads the console, and takes an accessibility snapshot — so it can catch and fix +its own z-index bugs, animation-timing errors, overflow, and layout shift. Chrome DevTools MCP +adds deep performance/network/CLS profiling. + +- Playwright MCP: — `npx @playwright/mcp@latest` +- Chrome DevTools MCP: — `npx chrome-devtools-mcp@latest` + +## ✅ Automated review — `design-review` subagent + +A senior-reviewer subagent (`.claude/agents/design-review.md`) that runs a 7-phase audit over a +live page: interaction states, responsiveness across 6 viewport tiers, visual polish, WCAG 2.1 +AA, edge cases, and console health. Invoke with `/design-review `. The heuristic subset +also runs headless in CI via `scripts/design-audit.mjs`. + +## Optional add-ons (not in default `.mcp.json`) + +- **Figma Dev Mode MCP** — read a frame's tokens/layout to generate matching code, and push + Claude-built UI back to the canvas as editable layers. Needs the Figma desktop app + Dev Mode. + +- **21st.dev Magic MCP** — generate React components from a prompt ("v0 in your editor"). Needs + an API key. + +Both are opt-in (see `docs/SETUP.md`) so the base stack runs with zero secrets. diff --git a/stack/docs/WORKFLOW.md b/stack/docs/WORKFLOW.md new file mode 100644 index 0000000..8c2fa78 --- /dev/null +++ b/stack/docs/WORKFLOW.md @@ -0,0 +1,84 @@ +# Workflow — the design loop, end to end + +The stack is built around one loop: **plan → commit → build → see → review**. Skipping the +"see" and "review" steps is exactly what makes AI design look like AI design. Here's the loop +on a real task. + +## Example: "Build a pricing section for a developer tool" + +### 1. Plan with data (`/design-plan` → `ui-ux-pro-max`) + +``` +> /design-plan developer tool pricing section, technical audience, trustworthy, high-contrast +``` + +Claude runs the design-system generator and pulls the palette, a font pairing, and the relevant +UX anti-patterns (e.g. "don't hide the total", "make the recommended plan obvious"). Output is a +compact token set — 4–6 colors, 2 type roles, spacing scale. + +### 2. Commit to a look (`frontend-design`) + +Claude answers *purpose / tone / constraints / differentiation*, picks ONE tone (say +"precise, engineered, monospace-accented"), and chooses a single signature element (a subtle +grid-paper background behind the recommended plan). It explicitly rejects the cream+serif and +acid-on-black defaults. + +### 3. Build + +Implements with the chosen tokens. On a React project it uses the **shadcn MCP** to add a +`card`, `toggle`, and `badge` rather than hand-rolling them. + +### 4. See it (Playwright MCP) — the step that matters + +``` +> Open it at http://localhost:3000/pricing. Screenshot mobile (375) and desktop (1440). +> Toggle monthly/annual and check the focus states. +``` + +Claude opens the real page and catches what code review can't: the annual/monthly toggle has no +visible focus ring, the "Most popular" badge overlaps the card border at 375px, and a price +number animates in before the card scrolls into view. It fixes each and re-screenshots. + +### 5. Review (`/design-review`) + +``` +> /design-review http://localhost:3000/pricing +``` + +The subagent drives all six viewport tiers, tabs through the whole section, checks contrast on +the muted "per month" text, and returns: + +``` +Verdict: Ship with fixes +Blockers: none +High: "per user / month" text is 3.9:1 on the card background (fails AA for body text) +Medium: CTA tap target is 40px tall on mobile (<44) +What's working: type scale is consistent; recommended-plan emphasis is clear +``` + +Claude fixes the High + Medium and the section is done — distinctive, responsive, accessible, +and actually verified. + +## The same loop for an existing site + +Point the tools at what's already deployed: + +``` +> npm run audit -- --url https://your-site.com # fast heuristic pass + screenshots +> /design-review https://your-site.com/pricing # full review with taste + flows +``` + +## A worked audit on a production site + +`examples/juniper-audit/` contains a real run of `scripts/design-audit.mjs` against a live +marketing site — the generated `report.md`, the per-viewport screenshots, and notes on how to +read the output. It's the fastest way to see what the heuristic layer catches before you wire +the stack into your own project. + +## Rules of thumb + +- **Never report UI work "done" without step 4.** If you didn't look at it, it isn't finished. +- **Let `ui-ux-pro-max` set tokens, `frontend-design` set attitude.** Data for correctness, + taste for distinctiveness. +- **Blockers/High gate merging; Medium/Nit don't.** Keep momentum; don't bikeshed nitpicks. +- **Re-screenshot after every fix.** The loop is cheap; regressions are not. diff --git a/stack/examples/juniper-audit/README.md b/stack/examples/juniper-audit/README.md new file mode 100644 index 0000000..664b327 --- /dev/null +++ b/stack/examples/juniper-audit/README.md @@ -0,0 +1,54 @@ +# Worked example — auditing a production marketing site + +This is a **real run** of `scripts/design-audit.mjs` against a live single-page marketing site +(a financial-services firm's homepage), captured to show what the heuristic layer of the stack +produces before you wire it into your own project. + +## How it was produced + +```bash +# serve the site, then audit it over HTTP (not file://, so asset/CORS noise is realistic) +python3 -m http.server 8099 # in the site directory +node scripts/design-audit.mjs --url http://localhost:8099/ --out examples/juniper-audit +``` + +## What's here + +- [`report.md`](./report.md) — ranked findings + console health +- [`report.json`](./report.json) — the same, machine-readable (what CI consumes) +- [`screenshots/`](./screenshots) — full-page capture at all six viewport tiers (360 → 1920 px) + +## Reading the results + +The site is genuinely well-built (design tokens, self-hosted fonts, `prefers-reduced-motion`, +scroll-reveal). The audit still surfaced three actionable items — exactly the kind a human +misses on a quick look: + +| Finding | Severity | Why it matters | +|---------|----------|----------------| +| 2–3 sampled interactive elements show no visible focus indicator | High | Keyboard users can't see where they are — WCAG 2.4.7 | +| 14 tap targets < 44×44 px on mobile | Medium | Small touch targets → mis-taps on phones | +| ~1 text node below AA contrast (approximate) | Medium | Lead to verify by hand; contrast is estimated from the nearest opaque background | + +The single console error is a `404` for one asset — worth chasing, but not a layout defect. + +## Two things this example teaches about the tooling + +1. **Serve over HTTP, not `file://`.** Opening via `file://` produces a wall of font-CORS + errors that are pure artifacts (browsers block cross-origin font fetches from `null` origin). + The numbers above are from a real HTTP serve, so the console is clean. + +2. **Full-page capture + aggressive scroll-reveal don't fully mix.** The script auto-scrolls to + trigger `IntersectionObserver` reveals before shooting, which recovers most content — but a + page that reveals sections only on fine-grained scroll can still show a blank band in a + single full-page screenshot. That's both a tooling limit **and** a real robustness signal + (the same content is invisible to some crawlers, print, and any non-interactive capture). + For those pages, the interactive **`/design-review`** subagent — which scrolls naturally, + waits, and interacts — is the more reliable check. Heuristics catch the mechanical defects; + the subagent judges the experience. + +## The point + +This is the *fast, automatic* half of the stack (also runs in CI). The *full* review — +interaction flows, visual taste, edge cases — is the `design-review` subagent invoked with +`/design-review `. Use both: heuristics to gate merges, the subagent to raise the bar. diff --git a/stack/examples/juniper-audit/report.json b/stack/examples/juniper-audit/report.json new file mode 100644 index 0000000..277fcc3 --- /dev/null +++ b/stack/examples/juniper-audit/report.json @@ -0,0 +1,50 @@ +{ + "target": "http://localhost:8099/", + "consoleErrors": [ + "[mobile-360] Failed to load resource: the server responded with a status of 404 (File not found)" + ], + "findings": [ + { + "severity": "high", + "check": "focus-visible", + "message": "2/25 sampled interactive elements show no visible focus indicator.", + "viewports": [ + "mobile-360", + "mobile-390", + "tablet-768" + ] + }, + { + "severity": "high", + "check": "focus-visible", + "message": "3/25 sampled interactive elements show no visible focus indicator.", + "viewports": [ + "laptop-1024", + "desktop-1440", + "wide-1920" + ] + }, + { + "severity": "medium", + "check": "tap-target", + "message": "14 interactive targets < 44×44px on mobile.", + "viewports": [ + "mobile-360", + "mobile-390" + ] + }, + { + "severity": "medium", + "check": "contrast", + "message": "~1/82 sampled text nodes below WCAG AA contrast (approximate).", + "viewports": [ + "mobile-360", + "mobile-390", + "tablet-768", + "laptop-1024", + "desktop-1440", + "wide-1920" + ] + } + ] +} \ No newline at end of file diff --git a/stack/examples/juniper-audit/report.md b/stack/examples/juniper-audit/report.md new file mode 100644 index 0000000..29a0bdf --- /dev/null +++ b/stack/examples/juniper-audit/report.md @@ -0,0 +1,29 @@ +# Design Audit — http://localhost:8099/ + +Viewports: 360 / 390 / 768 / 1024 / 1440 / 1920 px · Heuristic pass (pair with `/design-review` for taste & flows). + +**Findings:** 2 high · 2 medium · 0 low + +**Console errors (1):** +- `[mobile-360] Failed to load resource: the server responded with a status of 404 (File not found)` + +## HIGH +- **focus-visible** — 2/25 sampled interactive elements show no visible focus indicator. + ↳ seen at: mobile-360, mobile-390, tablet-768 +- **focus-visible** — 3/25 sampled interactive elements show no visible focus indicator. + ↳ seen at: laptop-1024, desktop-1440, wide-1920 + +## MEDIUM +- **tap-target** — 14 interactive targets < 44×44px on mobile. + ↳ seen at: mobile-360, mobile-390 +- **contrast** — ~1/82 sampled text nodes below WCAG AA contrast (approximate). + ↳ seen at: mobile-360, mobile-390, tablet-768, laptop-1024, desktop-1440, wide-1920 + +## Screenshots + +- mobile-360 (360×800): `screenshots/mobile-360.png` +- mobile-390 (390×844): `screenshots/mobile-390.png` +- tablet-768 (768×1024): `screenshots/tablet-768.png` +- laptop-1024 (1024×768): `screenshots/laptop-1024.png` +- desktop-1440 (1440×900): `screenshots/desktop-1440.png` +- wide-1920 (1920×1080): `screenshots/wide-1920.png` diff --git a/stack/examples/juniper-audit/screenshots/desktop-1440.png b/stack/examples/juniper-audit/screenshots/desktop-1440.png new file mode 100644 index 0000000..5123319 Binary files /dev/null and b/stack/examples/juniper-audit/screenshots/desktop-1440.png differ diff --git a/stack/examples/juniper-audit/screenshots/laptop-1024.png b/stack/examples/juniper-audit/screenshots/laptop-1024.png new file mode 100644 index 0000000..a3f93fe Binary files /dev/null and b/stack/examples/juniper-audit/screenshots/laptop-1024.png differ diff --git a/stack/examples/juniper-audit/screenshots/mobile-360.png b/stack/examples/juniper-audit/screenshots/mobile-360.png new file mode 100644 index 0000000..76ce891 Binary files /dev/null and b/stack/examples/juniper-audit/screenshots/mobile-360.png differ diff --git a/stack/examples/juniper-audit/screenshots/mobile-390.png b/stack/examples/juniper-audit/screenshots/mobile-390.png new file mode 100644 index 0000000..840bb1c Binary files /dev/null and b/stack/examples/juniper-audit/screenshots/mobile-390.png differ diff --git a/stack/examples/juniper-audit/screenshots/tablet-768.png b/stack/examples/juniper-audit/screenshots/tablet-768.png new file mode 100644 index 0000000..3363ec2 Binary files /dev/null and b/stack/examples/juniper-audit/screenshots/tablet-768.png differ diff --git a/stack/examples/juniper-audit/screenshots/wide-1920.png b/stack/examples/juniper-audit/screenshots/wide-1920.png new file mode 100644 index 0000000..6edd086 Binary files /dev/null and b/stack/examples/juniper-audit/screenshots/wide-1920.png differ diff --git a/stack/package.json b/stack/package.json new file mode 100644 index 0000000..a09f279 --- /dev/null +++ b/stack/package.json @@ -0,0 +1,19 @@ +{ + "name": "claude-website-design-stack", + "version": "1.0.0", + "private": true, + "description": "A ready-to-use Claude Code stack that makes the agent good at website design: knowledge (ui-ux-pro-max) + taste (frontend-design) + visual feedback (Playwright/Chrome DevTools MCP) + automated design review.", + "type": "module", + "scripts": { + "setup": "bash scripts/setup.sh", + "audit": "node scripts/design-audit.mjs", + "audit:file": "node scripts/design-audit.mjs --file ./index.html" + }, + "devDependencies": { + "playwright": "^1.49.0" + }, + "engines": { + "node": ">=18" + }, + "license": "MIT" +} diff --git a/stack/scripts/design-audit.mjs b/stack/scripts/design-audit.mjs new file mode 100644 index 0000000..45291e4 --- /dev/null +++ b/stack/scripts/design-audit.mjs @@ -0,0 +1,232 @@ +#!/usr/bin/env node +/** + * design-audit.mjs — standalone, MCP-free design audit. + * + * Opens a page in Chromium (Playwright), screenshots it across viewport tiers, and runs + * heuristic checks for the most common web-design defects: horizontal overflow, unsized + * media (CLS risk), missing focus styles, small tap targets, missing accessible names, + * heading structure, viewport/lang meta, console errors, and an approximate text-contrast pass. + * + * Usage: + * node scripts/design-audit.mjs --url http://localhost:3000 [--out audit-output] + * node scripts/design-audit.mjs --file ./index.html + * + * Notes: + * - Heuristic, not a substitute for the /design-review subagent (which also judges taste, + * interaction flows, and edge cases). Use this in CI and for a fast local pass. + * - Contrast is approximate (nearest opaque background); treat as a lead, not a verdict. + * - Set PW_EXECUTABLE_PATH to force a specific Chromium binary if auto-detection fails. + */ +import { chromium } from 'playwright'; +import { pathToFileURL } from 'node:url'; +import { mkdir, writeFile } from 'node:fs/promises'; +import { resolve } from 'node:path'; + +const VIEWPORTS = [ + { name: 'mobile-360', width: 360, height: 800 }, + { name: 'mobile-390', width: 390, height: 844 }, + { name: 'tablet-768', width: 768, height: 1024 }, + { name: 'laptop-1024', width: 1024, height: 768 }, + { name: 'desktop-1440', width: 1440, height: 900 }, + { name: 'wide-1920', width: 1920, height: 1080 }, +]; + +function parseArgs(argv) { + const args = { out: 'audit-output' }; + for (let i = 2; i < argv.length; i++) { + const a = argv[i]; + if (a === '--url') args.url = argv[++i]; + else if (a === '--file') args.file = argv[++i]; + else if (a === '--out') args.out = argv[++i]; + else if (a === '--executable') args.executable = argv[++i]; + } + if (!args.url && !args.file) { + console.error('Provide --url or --file '); + process.exit(1); + } + return args; +} + +// Runs inside the page. Returns structured findings for the current viewport. +function inPageChecks() { + const findings = []; + const push = (severity, check, message, detail) => + findings.push({ severity, check, message, detail }); + + const vw = window.innerWidth; + + // 1. Horizontal overflow + const docW = document.documentElement.scrollWidth; + if (docW > vw + 1) { + const wide = [...document.querySelectorAll('*')] + .filter((el) => el.getBoundingClientRect().right > vw + 1) + .slice(0, 5) + .map((el) => el.tagName.toLowerCase() + (el.className && typeof el.className === 'string' ? '.' + el.className.trim().split(/\s+/)[0] : '')); + push('high', 'horizontal-overflow', `Page is ${docW}px wide at ${vw}px viewport (horizontal scroll).`, wide.join(', ')); + } + + // 2. Unsized media (CLS risk) + missing alt + const imgs = [...document.querySelectorAll('img')]; + const unsized = imgs.filter((im) => !im.getAttribute('width') || !im.getAttribute('height')); + if (unsized.length) push('medium', 'unsized-media', `${unsized.length}/${imgs.length} lack width/height (layout-shift risk).`); + const noAlt = imgs.filter((im) => im.getAttribute('alt') === null); + if (noAlt.length) push('high', 'img-alt', `${noAlt.length} missing alt attribute.`); + + // 3. Tap targets (mobile only) + if (vw <= 480) { + const small = [...document.querySelectorAll('a,button,[role=button],input,select')] + .filter((el) => { const r = el.getBoundingClientRect(); return r.width > 0 && r.height > 0 && (r.width < 44 || r.height < 44); }); + if (small.length) push('medium', 'tap-target', `${small.length} interactive targets < 44×44px on mobile.`); + } + + // 4. Focus visibility (sample of focusables) + const focusables = [...document.querySelectorAll('a[href],button,input,select,textarea,[tabindex]:not([tabindex="-1"])')].slice(0, 25); + let noFocus = 0; + for (const el of focusables) { + el.focus(); + const s = getComputedStyle(el); + const hasOutline = s.outlineStyle !== 'none' && parseFloat(s.outlineWidth) > 0; + const hasShadow = s.boxShadow && s.boxShadow !== 'none'; + if (!hasOutline && !hasShadow) noFocus++; + el.blur(); + } + if (noFocus > 0) push('high', 'focus-visible', `${noFocus}/${focusables.length} sampled interactive elements show no visible focus indicator.`); + + // 5. Accessible names on controls + const namelessBtns = [...document.querySelectorAll('button,a[href]')].filter((el) => { + const text = (el.textContent || '').trim(); + const label = el.getAttribute('aria-label') || el.getAttribute('title'); + const hasImg = el.querySelector('img[alt]:not([alt=""]), svg[aria-label], [aria-hidden="false"]'); + return !text && !label && !hasImg; + }); + if (namelessBtns.length) push('high', 'accessible-name', `${namelessBtns.length} buttons/links have no accessible name.`); + + // 6. Heading structure + const h1 = document.querySelectorAll('h1'); + if (h1.length === 0) push('medium', 'headings', 'No

on the page.'); + else if (h1.length > 1) push('low', 'headings', `${h1.length}

elements (expected 1).`); + + // 7. Document meta + if (!document.querySelector('meta[name=viewport]')) push('high', 'viewport-meta', 'Missing — mobile rendering will break.'); + if (!document.documentElement.getAttribute('lang')) push('medium', 'html-lang', 'Missing lang attribute on .'); + + // 8. Approximate text contrast (sampled) + const toRgb = (c) => { const m = c.match(/rgba?\(([^)]+)\)/); if (!m) return null; const p = m[1].split(',').map((x) => parseFloat(x)); return { r: p[0], g: p[1], b: p[2], a: p[3] ?? 1 }; }; + const lum = ({ r, g, b }) => { const f = (v) => { v /= 255; return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4; }; return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b); }; + const ratio = (a, b) => { const L1 = lum(a), L2 = lum(b); return (Math.max(L1, L2) + 0.05) / (Math.min(L1, L2) + 0.05); }; + const bgOf = (el) => { let n = el; while (n) { const c = toRgb(getComputedStyle(n).backgroundColor); if (c && c.a !== 0) return c; n = n.parentElement; } return { r: 255, g: 255, b: 255, a: 1 }; }; + const textEls = [...document.querySelectorAll('p,span,a,li,h1,h2,h3,h4,button,label,td')] + .filter((el) => (el.textContent || '').trim().length > 1 && el.offsetParent !== null).slice(0, 120); + let lowContrast = 0; + for (const el of textEls) { + const s = getComputedStyle(el); + const fg = toRgb(s.color); if (!fg) continue; + const bg = bgOf(el); + const size = parseFloat(s.fontSize); + const bold = parseInt(s.fontWeight, 10) >= 700; + const large = size >= 24 || (size >= 18.66 && bold); + const need = large ? 3 : 4.5; + if (ratio(fg, bg) < need - 0.05) lowContrast++; + } + if (lowContrast > 0) push('medium', 'contrast', `~${lowContrast}/${textEls.length} sampled text nodes below WCAG AA contrast (approximate).`); + + return findings; +} + +// Scroll through the full page to trigger IntersectionObserver reveals and lazy-loaded media, +// then return to top. Without this, full-page screenshots of scroll-reveal sites capture blank +// sections (content still at opacity:0) — a common false "blank section" reading. +async function autoScroll(page) { + await page.evaluate(async () => { + await new Promise((res) => { + let y = 0; + const step = Math.max(200, Math.floor(window.innerHeight * 0.8)); + const timer = setInterval(() => { + window.scrollBy(0, step); + y += step; + if (y >= document.body.scrollHeight) { clearInterval(timer); res(); } + }, 80); + }); + window.scrollTo(0, 0); + }); + await page.waitForTimeout(300); +} + +async function launchBrowser(executable) { + const exe = executable || process.env.PW_EXECUTABLE_PATH; + try { + return await chromium.launch(exe ? { executablePath: exe } : {}); + } catch (e) { + // Fall back to a common preinstalled path (e.g. managed CI images). + return await chromium.launch({ executablePath: '/opt/pw-browsers/chromium' }); + } +} + +async function main() { + const args = parseArgs(process.argv); + const target = args.url || pathToFileURL(resolve(args.file)).href; + const outDir = resolve(args.out); + await mkdir(`${outDir}/screenshots`, { recursive: true }); + + const browser = await launchBrowser(args.executable); + const consoleErrors = []; + const results = []; + + for (const vp of VIEWPORTS) { + const ctx = await browser.newContext({ viewport: { width: vp.width, height: vp.height }, deviceScaleFactor: 1 }); + const page = await ctx.newPage(); + page.on('console', (m) => { if (m.type() === 'error') consoleErrors.push(`[${vp.name}] ${m.text()}`); }); + page.on('pageerror', (e) => consoleErrors.push(`[${vp.name}] pageerror: ${e.message}`)); + await page.goto(target, { waitUntil: 'networkidle', timeout: 30000 }).catch(() => page.goto(target, { waitUntil: 'load', timeout: 30000 })); + await page.waitForTimeout(400); + await autoScroll(page); + const shot = `screenshots/${vp.name}.png`; + await page.screenshot({ path: `${outDir}/${shot}`, fullPage: true }); + const findings = await page.evaluate(inPageChecks); + results.push({ vp, shot, findings }); + await ctx.close(); + } + await browser.close(); + + // Aggregate findings (dedupe by check+message, tracking which viewports hit each). + const agg = new Map(); + for (const { vp, findings } of results) { + for (const f of findings) { + const key = f.check + '|' + f.message; + if (!agg.has(key)) agg.set(key, { ...f, viewports: [] }); + agg.get(key).viewports.push(vp.name); + } + } + const order = { high: 0, medium: 1, low: 2 }; + const all = [...agg.values()].sort((a, b) => order[a.severity] - order[b.severity]); + + // Markdown report + const lines = []; + lines.push(`# Design Audit — ${target}`, ''); + lines.push(`Viewports: ${VIEWPORTS.map((v) => v.width).join(' / ')} px · Heuristic pass (pair with \`/design-review\` for taste & flows).`, ''); + const counts = { high: 0, medium: 0, low: 0 }; + all.forEach((f) => counts[f.severity]++); + lines.push(`**Findings:** ${counts.high} high · ${counts.medium} medium · ${counts.low} low`, ''); + if (consoleErrors.length) { lines.push(`**Console errors (${consoleErrors.length}):**`); consoleErrors.slice(0, 10).forEach((e) => lines.push(`- \`${e}\``)); lines.push(''); } + for (const sev of ['high', 'medium', 'low']) { + const group = all.filter((f) => f.severity === sev); + if (!group.length) continue; + lines.push(`## ${sev.toUpperCase()}`); + for (const f of group) { + lines.push(`- **${f.check}** — ${f.message}${f.detail ? ` _(${f.detail})_` : ''} \n ↳ seen at: ${f.viewports.join(', ')}`); + } + lines.push(''); + } + lines.push('## Screenshots', ''); + results.forEach(({ vp, shot }) => lines.push(`- ${vp.name} (${vp.width}×${vp.height}): \`${shot}\``)); + lines.push(''); + + await writeFile(`${outDir}/report.md`, lines.join('\n'), 'utf8'); + await writeFile(`${outDir}/report.json`, JSON.stringify({ target, consoleErrors, findings: all }, null, 2), 'utf8'); + + console.log(`\nAudit complete → ${outDir}/report.md`); + console.log(` ${counts.high} high · ${counts.medium} medium · ${counts.low} low · ${consoleErrors.length} console errors`); + process.exit(counts.high > 0 ? 2 : 0); +} + +main().catch((e) => { console.error(e); process.exit(1); }); diff --git a/stack/scripts/setup.sh b/stack/scripts/setup.sh new file mode 100644 index 0000000..3501e24 --- /dev/null +++ b/stack/scripts/setup.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# setup.sh — install the website design stack. +# Safe to re-run. Installs the audit deps + Chromium and the ui-ux-pro-max skill, +# then prints the two in-Claude steps (plugins are installed from inside Claude Code). +set -euo pipefail + +cyan() { printf '\033[36m%s\033[0m\n' "$1"; } +green() { printf '\033[32m%s\033[0m\n' "$1"; } + +cyan "==> Installing audit dependencies (Playwright)…" +npm install + +cyan "==> Installing the Chromium browser for Playwright…" +# Skipped automatically in images that preinstall browsers; harmless to run. +npx playwright install chromium || echo " (Chromium already present or managed by the environment — continuing.)" + +cyan "==> Installing the ui-ux-pro-max skill (knowledge layer)…" +npx --yes ui-ux-pro-max-cli init --ai claude || { + echo " Could not auto-install ui-ux-pro-max-cli. Install it later with:" + echo " npx ui-ux-pro-max-cli init --ai claude" +} + +green "==> CLI setup done." +echo +cyan "Two steps to finish inside Claude Code (plugins install from the app):" +echo " 1. Enable plugins, then run:" +echo " /plugin install frontend-design@anthropics/claude-code" +echo " 2. Start Claude Code in this folder — .mcp.json (Playwright + Chrome DevTools + shadcn)" +echo " and CLAUDE.md load automatically. Approve the MCP servers when prompted." +echo +echo "Optional add-ons: see docs/SETUP.md (Figma Dev Mode MCP, 21st.dev Magic MCP)."