Mockup Gallery
Local design review for HTML mockups. Yay/nay ratings, page assignment, wireframe-first toggle, design-system and per-route handoff artifacts.
Install
claude plugin marketplace add tyroneross/RossLabs-AI-Toolkit
claude plugin install mockup-gallery@rosslabs-ai-toolkit

The Problem
Mockup review is a folder of HTML files and a chat thread. Which version did the designer rate yay? Which component inside that mockup is the one to ship? Which route does it map to? Three weeks later nobody remembers and the build ships against the wrong reference.
What I Built
npx mockup-gallery opens a local UI at http://localhost:8787 that browses every HTML mockup in your project, captures ratings and notes, assigns winners to specific pages or screens, and emits two implementation-handoff artifacts when you commit to a build. Auto-detects Next.js routes, SwiftUI views, Python endpoints, and static pages.
Architecture
Mockup Gallery is a zero-dependency Node.js server, not a framework app — package.json lists no dependencies and no devDependencies. There is no Express, no bundler, no database. The frontend (gallery/gallery.html, ~2,500 lines) is a single vanilla-JS file served in an iframe. All review state — ratings, selections, handoff specs, the project’s design tokens — is plain JSON and Markdown on disk under .mockup-gallery/, so it’s diffable and committable rather than living in a store only the tool can read.
The one piece of judgment in the system — filling in a DESIGN.md token or a handoff spec’s Data Elements section — is deliberately not made by the gallery. handoff.mjs and design-system.mjs both state the same design principle in their source comments: “the host agent is the LLM.” The gallery scaffolds structure (route detection, JSON schemas, Markdown templates with frontmatter); the already-running coding agent (Claude Code or Codex, wired in via plugin hooks) reads the source mockup’s data-component markup and supplies the actual content.
flowchart LR
A["npx mockup-gallery /<br/>plugin launch"] --> B["Node http server<br/>zero deps, stable port hash"]
B --> C["detectProjectRoutes()<br/>Next.js / Astro / SwiftUI /<br/>Python / SPA / static / manifest"]
B --> D["gallery.html<br/>vanilla JS, iframe preview"]
D -->|"rate yay/nay + notes"| F[("selections.json")]
D -->|"POST /selected"| G[("selected.json")]
G --> H["handoff.mjs"]
H --> I[("handoff/<slug>.md")]
D -->|"POST /design-system/scaffold"| J["design-system.mjs"]
J --> K[("DESIGN.md<br/>project root")]
I --> L["Host coding agent<br/>Claude Code / Codex"]
K --> L
L -->|"fills tokens +<br/>handoff content"| M["Component build"]
N["Plugin hooks<br/>SessionStart / UserPromptSubmit /<br/>PostToolUse"] -.surfaces pending state.-> L
Models — which, where, why:
| Stage | Model / tier | Where it runs | Why there |
|---|---|---|---|
| Gallery process itself (server, route detection, variant pairing, JSON/Markdown I/O) | None | server/gallery-server.mjs, src/lib/*.mjs | Fully deterministic — every response is filesystem reads/writes and regex/string matching. Zero LLM calls, zero AI SDK, zero API key to manage. |
Filling DESIGN.md tokens and handoff-spec content (Components, Data Elements, Connectors/APIs, Open Questions) | The host coding agent’s own model (whatever backs Claude Code or Codex in that session) | Outside the gallery process — triggered by the plugin’s hooks and the /mockup-gallery implement/handoff commands | Explicit tradeoff, not an oversight: the gallery only needs to scaffold structure, so it never bundles a model or pays inference cost — judgment work is delegated to the coding agent that’s already running the session. |
Tools & infra — which, why:
- Node.js
http(built-in) — the whole server is the standard-library module; no framework dependency to install, patch, or audit. - Vanilla JS frontend (
gallery/gallery.html) — single file, no bundler or framework; talks to the HTTP API directly. - Flat JSON + Markdown files (
.mockup-gallery/*.json,handoff/*.md, rootDESIGN.md) — no database; state is plain text so it’s git-diffable and portable, trading away concurrent-writer safety (the tool assumes one local reviewer at a time). - Claude Code / Codex plugin surface (
hooks/*.mjs,commands/,skills/mockup-review) — SessionStart, UserPromptSubmit, and PostToolUse(Write|Edit) hooks plus the/mockup-galleryslash command wire the gallery into the host agent’s existing session instead of requiring a separate chat window. - Multi-framework route detector (
detectProjectRoutesingallery-server.mjs) — filesystem/regex scan across six project shapes (below), so the same binary auto-places mockups without per-project config.
How it works
npx mockup-gallery(or a plugin-driven launch) starts the Nodehttpserver, hashing the project’s absolute path into a stable port in the 8787-8886 range so multiple projects’ galleries can run at once without colliding.detectProjectRoutes()walks the target repo once at startup and returns the first match, in order: Next.js App Router (app/page.tsx), Astro (gated on anastro.config.*file, thensrc/pages/pages), Next.js Pages Router, SwiftUI (*View.swift, platform-tagged ios/macos/watchos/shared), Python route decorators (@app.route/@router.get/etc.), a manual.mockup-gallery/routes.jsonmanifest, Wouter/React Router SPA (<Route path=...>JSX), or static HTML inpublic//dist/build/static.- The server scans the mockups directory (
mockups/,docs/mockups/,.claude/mockups/, or--dir), pairs dark/light filename variants, and serves the list togallery.htmloverGET /mockups; filenames fuzzy-match against the detected routes for auto-placement. - The reviewer opens a mockup in an iframe (
GET /mockup/:file), rates it and itsdata-component-tagged pieces yay/nay with notes; the frontend posts toPOST /saveon every change, which writes straight to.mockup-gallery/selections.json(orsessions/<slug>/selections.jsonin sessions mode) — auto-saved, no explicit save step. - Assigning a mockup to a page (
POST /selected) writesselected.jsonand callshandoff.mjs, which emits.mockup-gallery/handoff/<route-slug>.md— frontmatter (route,source,status,filled) plus body sections (Components, Data Elements, Connectors/APIs, Visualizations, States, Open Questions). Existing handoff files are preserved on re-emit unlessregenerateHandoffs: trueis passed. POST /design-system/scaffoldwrites a starterDESIGN.mdat the project root (Google’s design-system format: YAML token frontmatter + prose sections) if none exists — 409 if one is already there withoutforce: true.- Neither artifact’s content is generated by the gallery. The plugin’s
SessionStart,UserPromptSubmit, andPostToolUse(Write|Edit)hooks (hooks/*.mjs) surface pending ratings, shared feedback, and unfilled handoff state to the host coding agent, which reads the mockup’sdata-componentmarkup and fills inDESIGN.mdtokens and the handoff sections. /mockup-gallery implement(orhandoff) re-reads the filled handoff spec to drive the actual component build;implemented.jsonthen tracks which components have shipped, so later review sessions don’t re-litigate settled UI.
Sidebar Layout
SELECTED Top — mockups assigned to pages/screens
ACTIVE Middle — all current mockups (yay/nay just marked, not moved)
SAVED Below active — bookmarked for later reference
ARCHIVE Bottom — reviewed and set aside
Each project gets a stable port derived from its path, so multiple galleries run at once without collisions. Implementation tracking flags which components have already shipped, so review sessions do not re-debate settled UI. Auto-save to file means the rating database is a plain text artifact you can diff, commit, or hand to another reviewer.
Wireframe-First Toggle
A sidebar toggle (default ON) drives the host coding agent to begin new review batches with a lo-fi wireframe before hi-fi HTML variants. Flip OFF when you want hi-fi directly. The preference persists per-project at .mockup-gallery/state.json#preferences.wireframeFirst. This catches the “the agent jumped to visuals before we agreed on structure” problem before it happens.
Two Handoff Artifacts
When a mockup gets selected for a build, the gallery emits two artifacts the coding agent then reads:
- Project-root
DESIGN.mdin Google’s design-system format — the visual identity (colors, typography, spacing, elevation, shape) shared by mockup-gallery, ad-hoc design tools, and any AI coding agent working the repo. Scaffold viaPOST /design-system/scaffold. - Per-route
.mockup-gallery/handoff/<slug>.md— emitted on/selected. Carries the interaction, data, state, and connector contracts for that specific route. The agent reads it before writing the component.
Project Detection
| Project Type | Detection | Route Source |
|---|---|---|
| Next.js App Router | app/ dir with page.tsx | File-system routes |
| Astro | astro.config.* present, then src/pages/pages | .astro/.md/.mdx file routes |
| Next.js Pages | pages/ dir | File-system routes |
| SwiftUI | *View.swift files | View names + platform tags (ios/macos/watchos/shared) |
| Python (Flask/FastAPI) | @app.route/@router.get decorators | Decorated endpoints |
| Manual manifest | .mockup-gallery/routes.json | Whatever the project author lists |
| Wouter / React Router SPA | <Route path=...> JSX in App.tsx/router.tsx | Parsed route props |
| Static | HTML files in public/, dist/, build/, static/ | Filenames |
Detection runs once at server startup, in the order above (first match wins), so a project matching two shapes (e.g. an Astro site with a stray pages/ dir) resolves deterministically. Filenames fuzzy-match to routes for pre-selection. Component tagging works inside each mockup for finer-grained tracking.
Claude Code Plugin
Installed as a Claude Code plugin, the gallery adds:
- SessionStart hook — shows design-review status (ratings, pending items, implementation progress)
/mockup-gallery— single entry point for launch, status, feedback, sessions, selected-build handoff, implementation guidance, and sync checks. Before coding selected UI, the agent uses/mockup-gallery implementor/mockup-gallery handoffto map changed UI elements to static/dynamic data, actions, connectors, visualizations, and unresolved questions.
Data Storage
All data lives in .mockup-gallery/ inside the project root: state.json (schema version, current session, preferences), selections.json (ratings + notes), selected.json (build assignments), implemented.json (tracking), accepted-designs.json (approved patterns), handoff/ (per-route specs), and sessions/<slug>/ for multi-session work. The project-root DESIGN.md lives alongside package.json — it is the visual-identity source of truth, not a per-tool artifact. Add .mockup-gallery/ to .gitignore to keep review state out of version control.
Tech stack
Node.js’s built-in http module runs the whole server — package.json declares zero dependencies and zero devDependencies, so there’s no Express, no bundler, no framework in the request path. The frontend is one vanilla-JS file (gallery/gallery.html) serving mockups in an iframe. Storage is flat JSON (selections.json, selected.json, implemented.json, accepted-designs.json, state.json) plus Markdown (handoff/*.md, root DESIGN.md) — no database. Route detection is a from-scratch filesystem/regex scanner covering Next.js App Router, Next.js Pages Router, Astro, SwiftUI, Python (Flask/FastAPI-style decorators), Wouter/React Router SPAs, static HTML, and a manual JSON manifest fallback. Distribution and host-agent integration run through the Claude Code and Codex plugin surfaces (hooks/*.mjs, commands/, skills/mockup-review) — no separate model or inference dependency; the host agent’s own model does the judgment work the gallery scaffolds for.