mirror of
https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git
synced 2026-07-22 14:07:56 +00:00
Add website design stack starter kit under stack/ (#424)
A ready-to-use Claude Code environment for website design, bundling: - .mcp.json: Playwright + Chrome DevTools + shadcn MCP servers - CLAUDE.md: the design loop (plan -> commit to a look -> build -> screenshot -> review) - .claude/agents/design-review.md: 7-phase design-review subagent (WCAG AA, responsive) - .claude/commands: /design-review, /design-plan - scripts/design-audit.mjs: standalone multi-viewport screenshot + heuristic audit - .github/workflows/design-review.yml: CI design gate (template) - docs/: STACK, SETUP, WORKFLOW - examples/juniper-audit/: a real audit run (report + 6 viewport screenshots) Pairs with the ui-ux-pro-max skill (knowledge layer) and the official frontend-design plugin (taste layer). Lives on its own branch as a self-contained starter; copy stack/ out to seed a standalone repo. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f8ac5e1266
commit
b484e8338c
96
stack/.claude/agents/design-review.md
Normal file
96
stack/.claude/agents/design-review.md
Normal file
@ -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 — <page/URL>
|
||||
**Verdict:** <Ship / Ship with fixes / Needs work> · Viewports checked: 375/768/1024/1440/1920
|
||||
|
||||
### Blockers (breaks usability or fails AA — must fix)
|
||||
- [What you observed] → [why it fails] → [the fix] · evidence: <screenshot/console>
|
||||
|
||||
### 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.
|
||||
26
stack/.claude/commands/design-plan.md
Normal file
26
stack/.claude/commands/design-plan.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
description: Generate a concrete design system (tokens, style, type, UX) before building UI
|
||||
argument-hint: <product/industry + keywords>, 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 <ui-ux-pro-max-skill-path>/scripts/search.py "$ARGUMENTS" --design-system -p "Project"
|
||||
```
|
||||
(Resolve `<ui-ux-pro-max-skill-path>` from the installed skill — see CLAUDE.md.)
|
||||
|
||||
2. Pull anything the brief needs specifically, e.g.:
|
||||
- `--domain color "<industry> <mood>"` for the palette / semantic tokens
|
||||
- `--domain typography "<mood>"` for font pairing + imports
|
||||
- `--domain web-vitals "<page type>"` for the performance budget
|
||||
- `--domain ux "<pattern>"` 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.
|
||||
15
stack/.claude/commands/design-review.md
Normal file
15
stack/.claude/commands/design-review.md
Normal file
@ -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.
|
||||
15
stack/.claude/settings.json
Normal file
15
stack/.claude/settings.json
Normal file
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
47
stack/.github/workflows/design-review.yml
vendored
Normal file
47
stack/.github/workflows/design-review.yml
vendored
Normal file
@ -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
|
||||
27
stack/.gitignore
vendored
Normal file
27
stack/.gitignore
vendored
Normal file
@ -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/
|
||||
17
stack/.mcp.json
Normal file
17
stack/.mcp.json
Normal file
@ -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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
73
stack/CLAUDE.md
Normal file
73
stack/CLAUDE.md
Normal file
@ -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 <skill>/scripts/search.py "<product> <industry> <keywords>" --design-system -p "Project"
|
||||
python3 <skill>/scripts/search.py "<query>" --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
|
||||
|
||||
- `<skill>` 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.
|
||||
96
stack/README.md
Normal file
96
stack/README.md
Normal file
@ -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.
|
||||
82
stack/docs/SETUP.md
Normal file
82
stack/docs/SETUP.md
Normal file
@ -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: <https://help.figma.com/hc/en-us/articles/39888612464151-Claude-Code-and-Figma-Set-up-the-MCP-server>
|
||||
|
||||
### 21st.dev Magic MCP
|
||||
Generates React components from a prompt. Get an API key at <https://21st.dev>, then add:
|
||||
|
||||
```json
|
||||
"magic": { "command": "npx", "args": ["-y", "@21st-dev/magic@latest"], "env": { "API_KEY": "<your-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.
|
||||
67
stack/docs/STACK.md
Normal file
67
stack/docs/STACK.md
Normal file
@ -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: <https://github.com/nextlevelbuilder/ui-ux-pro-max-skill> · License: MIT
|
||||
- Install: `npx ui-ux-pro-max-cli init --ai claude`
|
||||
- Use: `python3 <skill>/scripts/search.py "<brief>" --design-system` and `--domain <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: <https://github.com/anthropics/claude-code/tree/main/plugins/frontend-design>
|
||||
- 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: <https://ui.shadcn.com/docs/mcp> · 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: <https://github.com/microsoft/playwright-mcp> — `npx @playwright/mcp@latest`
|
||||
- Chrome DevTools MCP: <https://github.com/ChromeDevTools/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 <url>`. 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.
|
||||
<https://help.figma.com/hc/en-us/articles/39888612464151-Claude-Code-and-Figma-Set-up-the-MCP-server>
|
||||
- **21st.dev Magic MCP** — generate React components from a prompt ("v0 in your editor"). Needs
|
||||
an API key. <https://github.com/21st-dev/magic-mcp>
|
||||
|
||||
Both are opt-in (see `docs/SETUP.md`) so the base stack runs with zero secrets.
|
||||
84
stack/docs/WORKFLOW.md
Normal file
84
stack/docs/WORKFLOW.md
Normal file
@ -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.
|
||||
54
stack/examples/juniper-audit/README.md
Normal file
54
stack/examples/juniper-audit/README.md
Normal file
@ -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 <url>`. Use both: heuristics to gate merges, the subagent to raise the bar.
|
||||
50
stack/examples/juniper-audit/report.json
Normal file
50
stack/examples/juniper-audit/report.json
Normal file
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
29
stack/examples/juniper-audit/report.md
Normal file
29
stack/examples/juniper-audit/report.md
Normal file
@ -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`
|
||||
BIN
stack/examples/juniper-audit/screenshots/desktop-1440.png
Normal file
BIN
stack/examples/juniper-audit/screenshots/desktop-1440.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 504 KiB |
BIN
stack/examples/juniper-audit/screenshots/laptop-1024.png
Normal file
BIN
stack/examples/juniper-audit/screenshots/laptop-1024.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 304 KiB |
BIN
stack/examples/juniper-audit/screenshots/mobile-360.png
Normal file
BIN
stack/examples/juniper-audit/screenshots/mobile-360.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 186 KiB |
BIN
stack/examples/juniper-audit/screenshots/mobile-390.png
Normal file
BIN
stack/examples/juniper-audit/screenshots/mobile-390.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 192 KiB |
BIN
stack/examples/juniper-audit/screenshots/tablet-768.png
Normal file
BIN
stack/examples/juniper-audit/screenshots/tablet-768.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 276 KiB |
BIN
stack/examples/juniper-audit/screenshots/wide-1920.png
Normal file
BIN
stack/examples/juniper-audit/screenshots/wide-1920.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 575 KiB |
19
stack/package.json
Normal file
19
stack/package.json
Normal file
@ -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"
|
||||
}
|
||||
232
stack/scripts/design-audit.mjs
Normal file
232
stack/scripts/design-audit.mjs
Normal file
@ -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 <http…> or --file <path>');
|
||||
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} <img> lack width/height (layout-shift risk).`);
|
||||
const noAlt = imgs.filter((im) => im.getAttribute('alt') === null);
|
||||
if (noAlt.length) push('high', 'img-alt', `${noAlt.length} <img> 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 <h1> on the page.');
|
||||
else if (h1.length > 1) push('low', 'headings', `${h1.length} <h1> elements (expected 1).`);
|
||||
|
||||
// 7. Document meta
|
||||
if (!document.querySelector('meta[name=viewport]')) push('high', 'viewport-meta', 'Missing <meta name="viewport"> — mobile rendering will break.');
|
||||
if (!document.documentElement.getAttribute('lang')) push('medium', 'html-lang', 'Missing lang attribute on <html>.');
|
||||
|
||||
// 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); });
|
||||
31
stack/scripts/setup.sh
Normal file
31
stack/scripts/setup.sh
Normal file
@ -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)."
|
||||
Loading…
x
Reference in New Issue
Block a user