Skip to content
Back to skills

Ship Mockup Before After

ASecurity

Show a planned visible change as a before/after mockup before building it — a real screenshot as BEFORE, a measured HTML/CSS overlay as AFTER, one per plan part — shipped as a self-contained HTML file or artifact that reviewers comment on and hand back as a build spec. After approval, implements and proves each difference on the real screen. Use for "mock this up", "show me before and after", "what will this look like", "I want to see it before you build it", "implement the mockup", "the scre...

  • 2 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 2, 2026
ai-agentsjavascriptpythonrustgojavashellnextjsgit

Works with

  • cli

Security analysis

A100/100

Scanned October 6, 2026

npx -y skills add skowalskidev/dotclaude --skill ship-mockup-before-after --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ship Mockup Before After?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ship Mockup Before After
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/skowalskidev-ship-mockup-before-after/badge)](https://www.skillsdirectory.com/skills/skowalskidev-ship-mockup-before-after)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ship-mockup-before-after
description: Show a planned visible change as a before/after mockup before building it — a real screenshot as BEFORE, a measured HTML/CSS overlay as AFTER, one per plan part — shipped as a self-contained HTML file or artifact that reviewers comment on and hand back as a build spec. After approval, implements and proves each difference on the real screen. Use for "mock this up", "show me before and after", "what will this look like", "I want to see it before you build it", "implement the mockup", "the screen doesn't match the mockup", or any plan with a visible change.
argument-hint: "[optional: ticket id, plan path, or which part to mock]"
---

# Show him the change, don't describe it

**Why this exists:** a plan describes a screen and Simon has to hold it in his head to judge it.
He cannot, so he approves something he has not seen and corrects it after it is built, which is the
expensive end. A preview moves every correction to before the work.

**DO ship the preview as ONE self-contained HTML document, built from real app pixels, whose state is a
data island (below).**
**DON'T hand-write the screen from scratch, and DON'T build a dev route just to view it.** A rebuilt
screen is always nearly right, and nearly right is what wastes the round — the artifact and the
hand-rebuild were both rejected on 2026-08-10, in his words: *"it's not looking like the real app"*,
then *"still not reusing existing components... I want you to use the exact components not remake
them"*. The rule that survives both rejections: **the mockup's fidelity comes from the real app, never
from eyeballing.** A document that starts from a real screenshot and measured real-component styles IS
exact, and it needs nothing running to open or share.

**DO make EVERY deliverable open from `file://` with no server — self-contained, no external requests,
no sibling-file dependencies — whether it is one `#spec` document or the consolidation gallery over many
files (§ below).** The same anti-pattern as the dev route above, one level up: a gallery that needs
`http://localhost` to show its tiles hands a recipient blank tiles.
TEST: with every server off, opening the file renders it fully. A deliverable that needs a running
server is not shareable and is not done.

A change with NO screen — a schema, a prompt harness, a pipeline ordering — has nothing to screenshot;
diagram the before and after instead. The rule is the medium follows the subject.

**When the screen ALREADY EXISTS, the BEFORE is that real screen — always.** A "blind", "exploratory"
or "independent-directions" round is free to reinvent the LAYOUT, but it NEVER licenses a fabricated,
generic, or from-memory before/after. The BEFORE is the real screenshot (or the real component
measured); the AFTER's tokens, chrome and theme trace to the measured real app; only the layout is
open. The no-screen escape above is ONLY for a surface that does not exist yet. Round 1 of the RR
redesign was run blind with zero app grounding and every direction was rejected for not looking like
the product — the exploration belongs in the layout, not in the fidelity. TEST: the BEFORE traces to
the real screen, and every colour/font/radius/theme in the AFTER traces to a measured value, even in a
blind round.

## One mockup file per surface — a new exploration is a new variant, never a new file

**DO keep every exploration for one surface inside the SINGLE existing mockup file for that surface: a
new idea, a section redesign, or "another version of X" is a new switchable variant (or version) added
to that one file.** The file runs N switch axes at once — a PERSONA / data axis (which captured page or
seeded state is shown) and a per-section DESIGN-VARIANT axis (each region's alternative designs). A
section variant is swapped INTO the live captured page in context via a nested `iframe.srcdoc`, and the
`Current` option restores the pristine capture, so a redesign is judged against the real surrounding
page. Adding "versions of another part of the page" is adding another SECTION entry on the design axis,
still in the one file.
**DON'T create a second mockup file for a new idea, a section redesign, or another version** — no
`*-redesign.html`, no `*-variants.html` sibling. A second file splits the exploration so he can no
longer flip between versions of any part of the page in one place, which is the whole reason the single
file exists. The fix for exactly that: fold both spun-off files back into the one gallery, where the
redesigned region is just one more SECTION with its variants on the design axis.
DEFAULT: one mockup file per surface, forever; every later exploration lands in it as a variant/version.
TEST: after any mockup iteration, the surface still has exactly ONE mockup file and the new exploration
is reachable as a variant/version toggle inside it; a second `*-mockup`/`*-redesign`/`*-variants` `.html`
file for the same surface is the violation.

## One stable link — the mockup's path never changes across iterations

**DO give the surface's mockup ONE canonical entry point whose path never changes — a fixed filename, or
a stable alias like `mockup.html` that always resolves to the current build — and write every rebuild IN
PLACE to that same path.** Simon bookmarks it once and hits refresh; the link he opened last round opens
this round's build.
**DON'T rename the file, and DON'T point "the current version" at a new filename across iterations** — no
`-redesign` today, `-variants` tomorrow, `-gallery` the day after. A renamed file breaks the bookmark and
sends him hunting for the new one every round.
WHY: the single-file discipline (§ above) exists so he flips between versions in ONE place; a path that
moves defeats it before he even opens the file.
DEFAULT: one fixed path per surface, forever; a rebuild writes over it, never beside it under a new name.
TEST: the path Simon opens is byte-identical across every iteration (the fix for a mockup he had to
re-find when a `-redesign` file became a `-variants` file became a `-gallery` file); a rebuild that makes
him find a new filename is the violation.

## Variants while EXPLORING; a DECISION collapses to a default

**DO turn every design option you would otherwise ASK about into a VARIANT in the mockup — the user picks
from the mockup's own variant switcher + pick/comment UI, never from a chat question.** Whenever you can
see two or more ways to do a design thing (a shape, a layout, a scroll model, a colour, a placement),
build ALL of them as variants and ship them together in the one mockup. A design suggestion is a variant,
not a question.
**DON'T ask a design-decision question in chat — no AskUserQuestion "A or B?", "which layout?", "which
colour?".** Showing beats asking; that is the whole point of a mockup. Only forks a mockup CANNOT answer
stay chat questions (build-now-vs-mock-first, which repo, a credential, prod approval).
DEFAULT: N design options → N variants in the mockup; zero design-choice questions in chat.
TEST: every option you considered is a variant in the mockup and no chat message asks the user to choose
between design alternatives (the fix for asking a sidebar A/B and a scroll model in chat instead of
rendering them as variants).

**DO offer multiple variants to choose from ONLY while an open question is still open.** The moment Simon
picks one — or when the change was a decided edit from the start, never an exploration — make the chosen
design the DEFAULT the mockup OPENS ON, and demote every superseded design to a labelled `Before — …`
reference.
**DON'T keep a decided change as one equal option among many, and DON'T leave the mockup opening on the
old design after a decision.** Once he picks "severity-tiered list" it is THE view, not option 4 of 10; a
2→4 tab split that was decided up front opens on the 4-tab design, not as a "Current vs New" toggle
defaulting to the 2-tab.
WHY: a settled design shown as one equal option reads as still-open and invites re-litigating what he
already decided.
DEFAULT: exploring → N variants; a decision, or a decided-from-the-start edit → the chosen design is the
open-on default, the old kept only as a before-reference.
TEST: after Simon states a decision, the mockup opens by default on the chosen/decided design with the
old kept only as a labelled before-reference; a decided change presented as one of N equal options, or a
mockup still defaulting to the superseded design, is the violation.

## Direction and target approval

DO use the shared dashboard protocol's Reference setup: supplied images, named platforms or none.
Draft the target from those inputs and the real product context, iterate with Simon, and freeze his
approved design revision as the implementation's reference. Missing screenshots never block drafting.
Pass every supplied or approved artifact by absolute path to builders and have them read it.
TEST: the implementation is judged against the approved target, not a later unapproved proposal.

## Choose the medium — a downloadable HTML FILE, or a Claude artifact URL

The deliverable is one self-contained HTML document; ship it as whichever medium serves THIS mockup.
The Claude-artifact medium is genuinely limited, and the limits pick the medium (all measured 2026-08):

- **A downloadable HTML FILE — the DEFAULT for a collaborative, versioned, multi-screenshot round-trip.**
  It has no size ceiling, opens in any browser, and the recipient's Claude edits the ACTUAL file with
  zero regeneration loss. Write it to a path and hand Simon the file to share.
- **A Claude artifact URL — the frictionless option when the mockup is SMALL and un-gated.** Publish it,
  send the link; non-account viewers get full interactivity, nothing to run.

**DO pick the FILE the moment any of these is true**, because the artifact medium breaks on them:
- **Size:** a full-screen base64 PNG is ~1M+ text tokens; a few inlined screenshots plus version history
  can exceed the context window and make the artifact impossible to rewrite in one turn. A file has no
  such ceiling.
- **`localStorage`/`sessionStorage` are BLOCKED in the artifact sandbox** — state must be in-memory. A
  file can use storage, but keep state in-memory + the embedded spec so it is portable either way.
- **Publishing gotchas:** the one-click "Remix" is gone (a recipient copies the source into their own
  Claude — a fork, not a live shared doc); **Team/Enterprise accounts cannot publish publicly** (org
  only), so if Simon is on a work org, use the FILE; published links do not expire and carry no
  password, so never put anything sensitive in a mockup.

TEST: a mockup with more than one screenshot, more than one version, or a review round-trip ships as a
FILE, not an artifact.

## Make it a DATA ISLAND — the embedded spec is the single source of truth

This is what makes the mockup rebuildable without loss, diffable across versions, and faithful on a
hand-back. It is the Next.js `__NEXT_DATA__` / SingleFile hydration pattern applied to a mockup.

**DO embed ONE `<script type="application/json" id="spec">` block that IS the mockup's entire state** —
`schemaVersion`; a `manifest` (version count + every variant id); design tokens; and `versions[]`, where
each version carries its before/after variants as STRUCTURED component data + measured styles, plus its
author, timestamp, selections, comments and verdicts. A small `render(spec)` builds the whole visible UI
from it.
**DON'T author any label, prop, or state directly into the markup.** A string baked into HTML instead of
`#spec` is a string the next regeneration silently drops — that is the whole failure mode this prevents.
WHY: regeneration then becomes "re-render this JSON" (mechanical, loss-free) instead of "re-describe the
UI" (lossy — verbatim structured data survives an LLM round-trip 91% vs 14% for a summary).
TEST: with the render layer deleted, `#spec` alone still holds every variant, version, selection and
comment; re-running `render(spec)` reproduces the mockup exactly.

## One shell for every mockup: build it through `bin/mockup-build.py`, never hand-roll the chrome

**DO build every mockup file by inlining its `#spec` into `~/.claude/bin/mockup-shell.html` with
`python3 ~/.claude/bin/mockup-build.py <spec.json> <out.html>` (`--extract <mockup.html> <spec.json>` to
edit an existing one), so every mockup opens and behaves the same way.** The shell owns the chrome: the
active variant fills the viewport on open (desktop persona scaled to fit, mobile centred at 390 px), ONE
right rail (300 px, collapsible to a 40 px icon strip) holding Variants (←/→ keys), Persona, Version
stepper, Walk the states, Compare + Diff, Feedback (picks, comments, pins, verdict) and Hand-off, a
full-screen presentation mode (`p`; arrow keys step variants and states; corner HUD), a one-line
dismissible hint bar, and the What's-new flash. The `#spec` supplies content only: variants, captures,
states, tokens, assets, versions, feedback; its `shell` block sets `hintText` and `railCollapsed`, nothing
else.
**DON'T write per-mockup chrome (a grid landing page, an intro modal, a floating panel, a bespoke variant
switcher) and DON'T fork the shell into the mockup file.** Bespoke chrome shifts every round and hides the
mockup (the fix for a grid landing page that made Simon scroll around to find the actual mockup,
2026-09-08).
**DO mark exactly ONE variant `role: "before"` and every earlier round `superseded: true`; never label a
variant "Before — superseded".** The rail then reads Before, then the distinct after-variants of this round,
then a collapsed "Earlier rounds (N)" (the fix for a rail of eight "Before" rows Simon could not tell apart,
2026-09-08). TEST: the rail shows one Before row; superseded rows are hidden until the disclosure opens.
**DO keep the built file at or under 12 MB (the dashboard's asset ceiling) and at 0 external requests:**
store each `data:` URI once in `#spec.assets` and reference it from captures as `@@ASSET:id@@`, and drop
font families the captures never apply.
**DO treat every switch (variant, persona, version, view, state walk, presentation on/off) as a full
teardown that opens the target at its DEFAULT state; a state walk mounts a fresh document at that state
and the rail highlights exactly one active state row.** The shell's `teardownStage()` retires the outgoing mount (posts
`mockup:cleanup` to its iframes, clears shell timers, empties the stage) before the next one mounts;
a capture that holds its own timer, interval or observer releases it on `mockup:cleanup`, and keeps
every overlay (modal, sheet, popover, menu) INSIDE its own iframe document. After ANY edit to
`bin/mockup-shell.html`, run `python3 ~/.claude/bin/mockup-shell.test.py` (Playwright) and ship only on
`ALL PASSED` (the fix for a modal that stayed open on top of the next variant, 2026-09-08).
TEST: `mockup-build.py --extract` on the shipped file yields a spec that rebuilds it byte-identically; at
1440×900 the mockup is fully visible on open with no scroll, and inside a wide-short host iframe (1900×1010)
the whole capture is still visible (fit uses both axes and re-fits on resize); `p` enters presentation mode; a synthetic
two-variant spec renders in the same shell; the file is ≤12 MB with 0 external requests; with an overlay
open, switching variant or toggling presentation shows exactly one iframe at the new target's default
state (`bin/mockup-shell.test.py` passes).

## Capture the BEFORE from the real app

**DO check the target surface for in-flight work before the first capture:** list active worktrees
(`git worktree list`) and open or draft PRs (`gh pr list --state open --json number,headRefName,files`)
that touch the surface's files, read each overlapping plan or diff, and record the design base (a git ref)
in the living plan. When an overlapping change replaces the surface, ask Simon whether to build on it
before drawing.
**DON'T draw against a screen another open PR is replacing.** (the fix for two mockup rounds drawn on a
filter sheet a sibling PR was turning into chips.)
TEST: the plan names the design base and gives a verdict per overlapping PR or worktree before the first
capture.

**DO CAPTURE an existing screen as a 1:1 self-contained HTML first — that capture IS the before-base,
not a screenshot to rebuild from.** A headless real browser (Playwright) authenticates via the project's
own auth path and navigates to the seeded screen; then SingleFile (gildas-lormeau) serialized IN-PAGE —
inject its `single-file-bundle.js` with `addScriptTag`, call `singlefile.getPageData({ blockScripts:true,
… })` — inlines every stylesheet, font and image as a `data:` URI. The output opens from `file://` with
ZERO external requests and is pixel-identical to the live render, because it IS the render, not a
re-description of it.
**DON'T rebuild an existing screen from component code + measured styles.** A rebuild drifts on exactly
the details invisible to the rebuilder: the fix for a mockup that showed a fabricated "$1,088/Pending"
where the real account read "Free access", and dropped a real banner and two table rows — each caught
only by capturing the real page. Reserve the screenshot-raster + measured-styles path below for a surface
that does NOT exist yet, or for building the AFTER overlay on top of a capture.
TEST: for an existing screen, the before-base opens offline with 0 external requests and every string on
it traces to the live render, not to a component read.

**DO screenshot the target screen at a FIXED viewport, in one theme, populated with realistic data.** A
seeded demo is fine and preferred — seed the screen and everything it references with realistic values
(plausible names, lengths, counts) so it looks like the app in real use. Record the route, viewport
size and theme in the spec. That raster is the substrate the AFTER sits on, so every pixel around the
change stays true to the app.
**DON'T screenshot placeholder, lorem or empty-stub content.** The wrapping, truncation and populated
states are the whole reason to look, and only realistic content shows them.

**DO compress every screenshot HARD before inlining — WebP q70-80, downscaled to display size (long edge
~1280px), never a full-res PNG.** A full-res PNG as base64 can exceed the whole context window on its
own; a downscaled WebP is roughly 40x smaller. Cap inlined screenshots at about 1-3 and keep the total
base64 well under the model's output cap, or the document cannot be rewritten in one turn. Prefer
rendering a state from measured-style DATA over screenshotting it wherever the state is reachable.

**DO fan out the capture across parallel agents and separate seeded accounts by default.** Each
persona/state is an independent seed→capture, so run them at once — one agent per state, on its own
seeded account where two or more exist, falling back to a sequential re-seed on ONE account only when no
second account is available. Parallelise the other independent steps (seeding, verifying, the per-tile
checks) the same way.
TEST: N persona states are captured by N concurrent agents, not a serial loop, whenever N accounts exist.

**DO offer real PRODUCTION merchant profiles as preview personas when the value is seeing REAL data.**
Read a merchant's owner-scoped records from prod with a READ-ONLY key (never write to prod, never
authenticate as a prod user), copy them into a dev account with the owner id remapped, and capture on
DEV — so the page renders the real data under dev auth while prod stays untouched. Copy EVERY collection
the screen reads, not the obvious ones: a page that reconciles money reads more than the funnel does (the
fix for a capture that showed "0 recovered" in the funnel while the commission card read $1,617 — a
rolling measurement snapshot plus its `saves` subcollection were the missing sources; grep the page's
hooks for every read before copying). Preserve the doc IDs any cross-doc reconcile depends on (a snapshot
row keyed to an enrollment id), and write a connected-state stand-in for a live provider connection that
cannot be copied. Capture the TAB that shows the data — a merchant whose primary tab is empty renders its
real numbers on another tab, so click into it before serializing.
TEST: the dev-rendered page reconciles to the same numbers prod shows, and prod received zero writes.

**DO re-inject a SMALL interaction layer into a static capture so the components that COLLAPSE or toggle
still work.** SingleFile strips the app's JS, so the capture is frozen — but the rendered DOM keeps its
`data-testid`s and framework classes, so a tiny injected script re-wires the interactions whose content
is present in the captured DOM (a collapsible panel: on the header's `data-testid` click, toggle the
framework collapse body). An interaction whose target content is NOT in the capture — a TAB whose other
view never rendered — needs a per-tab capture (capture each tab state, swap between them), not a re-wire.
TEST: the injected layer toggles the collapsible in the captured page; a tab-switch either swaps between
per-tab captures or is left frozen with a note, never a dead control.

## Measure the real components — never eyeball the AFTER

**DO measure the real rendered components before drawing anything** — `getComputedStyle` and
`getBoundingClientRect` for box size, font, colour, spacing, border-radius, shadow — and size the
AFTER from those exact values, stored in `#spec`. The app itself is the render rig; stand up a scratch
route ONLY when a component the AFTER needs is not reachable on any real screen.
**DON'T set a dimension, colour or font in the overlay by eye.** Eyeballing is the nearly-right failure
that got the rebuild rejected; a measured value cannot be nearly right.
TEST: every dimension and colour in the overlay traces to a value you measured this run.

**DO give every inline SVG icon an explicit width/height** — or a clamping rule on its container
(`.x svg{ width:16px; height:16px; flex:0 0 auto }`). An SVG authored with only a `viewBox` and no
dimensions expands to fill its flex parent, so a nav "+" renders as a full-width square. TEST: no
reconstructed icon renders larger than its measured size.

## Render the AFTER: overlay a screen, storyboard a flow

**DO overlay the change on the BEFORE screenshot when it is a single screen** — HTML/CSS positioned and
sized from the measured values in `#spec`, changing ONLY the region the plan touches. Everything outside
the change stays the untouched screenshot, so it is real pixels by construction.
**DON'T re-render the parts that did not change.** Re-drawing them re-introduces the exact nearly-right
rebuild this skill exists to avoid.

**DO make it a WALKABLE storyboard when the change is a flow** — every step the user passes through,
each new state built from the measured component styles, each state the app already renders reused as a
real screenshot. Wire the buttons, fields and steps so he advances through them himself.

## Walk every state — simulated, seeded, no real requests

**DO cover every step of the proposed journey**, enumerated from `references/user-journey-review.md`
(first contact → set-up → trigger → the wait → the result, and the empty, loading and error state of
every surface). He can only tell you which parts need changing if he can reach every state; a first
screen with nothing beyond it sends the fix back to after the build — the exact cost this skill removes.
**DON'T stop at the first state, and DON'T leave a button dead.**
**DO simulate every transition with local state and seeded data** — no network, no persistence, no auth,
no real requests. He is walking the flow, not operating a live app.

## Assemble and navigate through the shared dashboard

DO read `references/workflow-loops.md`. Use its template for the task overview and stable entry
point; put each surface in a left-nav section with Before, named Reference / target and Current.
Keep the plan as the task record and each surface's `#spec` as its design record. Update the plan's
asset paths and source references after each capture, proposal, implementation and judgement.
TEST: the plan view and mockup view use the identical dashboard shell.

DO retain the surface's full data island, versions, variants, persona axes and pinned feedback in
one self-contained asset. Inline it in the target panel; let that surface's editing controls keep
working there. Keep the shared shell quiet and neutral, and the captured product pixels unchanged.
When connecting an existing review, rebuild its canonical entry point in place and preserve its
complete data island inside the embedded asset before replacing any chrome.
TEST: every previous version, selection and comment is reachable after connecting the dashboard.

DO put the approved proposal in Target and a real implementation capture in Current. Before approval,
Current displays verified progress or Not started. A proposal never counts as implementation evidence.
DO keep detailed version comparisons and the review round-trip below inside the surface asset; the
shared shell owns section navigation, progress, presentation and offline export.

## Compare — side by side, and diff what changed between versions

**DO give a 3-up SIDE-BY-SIDE compare mode (cap 3-4 variants)** so he judges options head to head without
holding them in memory, plus an onion-skin opacity slider (or a before/after wipe) for two close variants
where a small positional shift matters.

**DO DIFF two versions at the FIELD level — green added, yellow modified, red removed** — derived from
`#spec`, since the state is structured data. This is the single highest-value comparison feature and the
reason the data island is worth it: it shows exactly what a recipient changed between v1 and v2, which
even design tools built on rasters cannot do on their own content.
TEST: switching to "compare v1 → v2" marks every added/modified/removed field, and the marks come from
`#spec`, not from eyeballing two screenshots.

## Point at what's new

**DO gently highlight the CHANGED region of each variant, so a small new section is not missed.** When a
variant first opens, flash the changed area ONCE — a brief 150-300ms outline or glow — then HOLD a static
marker (a persistent outline on the changed region, the rest dimmed). Give a "What's new" control that
re-flashes on demand, and respect `prefers-reduced-motion` (fall back to the static marker, no motion).
**DON'T pulse it forever or animate the whole screen.** A looping animation dilutes attention and gets
annoying on repeat; one flash then a static marker is what defeats change-blindness without distraction.
TEST: opening a variant flashes only the region the plan changed, once, then a static outline stays; the
"What's new" button re-flashes it, and reduced-motion shows the static marker with no flash.

## Let him — and a recipient — choose, comment, and decide

**DO make the variants selectable (one or more), each with a comment box for what to keep and what to
change, using the response contract from `/sk:work-ask-reply-in-full-before-after-artifact`** — so he
hands back exactly what he selected and commented without typing a variant name.
**DON'T make him read a variant's name and type it back.** The picks and comments ARE the decision.

**DO let him PIN a comment to a specific region** — anchor it to the element's id in `#spec`, not to raw
x/y, so the pin survives a reflow — with a resolve/done state, and an explicit **Approve** or **Request
changes** verdict per variant. Pins, resolve and an approve/request-changes verdict are the feedback
reviewers actually rely on.

**DO keep the picks/comments/pins/verdict panel in the shell rail's Feedback section, collapsed by
default, so it never covers the mockup.**
**DON'T float it over the screen he is judging.**
TEST: he selects variants, comments and pins keep-or-change on each, sets a verdict, opens the panel and
copies, and the block names every pick, rejection, pinned comment and verdict.

## Share it, and run the LOSS-FREE review round-trip

The document goes to someone who is NOT Simon — a stakeholder, a teammate — and comes back with their
picks and change requests. Send the FILE (their Claude edits it directly, loss-free), or the artifact
URL when it is small and un-gated.

**DO make the document GUIDE a first-time recipient.** The shell's one-line dismissible hint bar (never a
modal) says: browse the variants in the rail, select the ones you like, pin comments for changes, and set a
verdict. The guidance is part of the document; the recipient needs no briefing from Simon.

**DO make the copy-paste hand-off block BE the complete build spec — the verbatim `#spec` in an XML
instruction frame**, so nothing degrades on the round-trip:

```text
<task>
Rebuild a versioned UI mockup from the spec below. The <spec> JSON is the SINGLE SOURCE OF TRUTH.
Render it exactly. Do NOT paraphrase, summarise, invent, reorder, or drop any field.
Produce ONE self-contained HTML file. No external calls; inline or pin every dependency.
</task>
<spec> …the FULL #spec JSON, verbatim: manifest, designTokens, versions[] with before/after, selections,
comments, verdicts… </spec>
<build>
1. Embed <spec> unchanged as <script type="application/json" id="spec">.
2. render(spec): build the grid, focus view, filmstrip, HUD, compare/diff and comment UI PURELY from #spec.
3. Nothing user-visible may exist outside #spec; style only from designTokens.
</build>
<invariants>
- Output the ENTIRE file. No placeholders, no "// unchanged", no elisions.
- Every string in #spec must appear in the rendered output.
- versions rendered == manifest.versionCount; all manifest.variantIds present.
- When handing back, RE-EMBED the updated #spec verbatim — never a prose description.
</invariants>
<selfcheck>
Before returning: parse #spec from your own output, assert the three invariants, report each PASS/FAIL.
</selfcheck>
```

**DO instruct the recipient's Claude, inside that block, to:**
1. Apply the picks + comments as a NEW version — EDIT the file's `#spec` directly if they have the file,
   or REGENERATE from the block above if they only have the copied text. Change only the relevant `#spec`
   fields and re-render; reserve a full rebuild for when the render layer changes.
2. Increment the VERSION HISTORY in `#spec`, attributed: v1 is the author, v2 is this recipient, and so
   on — each version records who made it, when, and what changed.
3. Tell the recipient to LOOK at the updated mockup and confirm it reflects their asks.
4. Tell the recipient to PASS IT BACK to the original author (the updated file, or the fresh block).

**DO keep EVERY version inside `#spec` — the mockup carries its own history.** The HUD version selector
switches between versions, each attributed to its author, and the field-level diff (above) shows what
each version changed. The author receives ONE document and steps back through v1 (what they proposed),
v2 (what the recipient chose and asked for), and onward — variants and all — never a pile of files.
**DON'T flatten an old version away when a new one is made.** The trail — who changed what, and why — is
exactly what the author needs to resolve it.
TEST: the returned document opens on the latest version, the HUD lists every version with its author,
switching to an earlier version shows that author's variants + picks + comments intact, and the diff
marks what changed between any two.

## Version every iteration — grouped, latest-active, and QUIET

Versions are not only for the external round-trip above — every LOCAL iteration checkpoint is a version,
so you can watch your own progression as you edit. The hyperspeed fan-out → consolidation is the
canonical case: each fanned-out pass saves its variant stamped with the CURRENT version, and the
consolidation groups all same-version variants into ONE version group. So each consolidation checkpoint
is one version (round 3 = v3, round 4 = v4, …), each holding that round's variants; the next round
APPENDS the next version rather than overwriting, and older versions stay reachable to show the
progression.

**The newest version is always active; older versions are for looking back only** — open on the latest
version's variants. **Keep the version control COMPACT and quiet — it is NOT a primary interface
element:** the shell rail's small version stepper, visually quieter than the variant list, that never
obscures the design and is reached only on demand. TEST: the version control is the rail's small stepper,
the newest version is active on open, and switching to an older version shows that version's grouped
variants intact.

## Cite where each part came from

**DO put a sources line in the spec**, naming the validated plan part, ticket or decision each visible
difference implements, by link.
**DON'T show a change with no source.** Anything unsourced is scope you invented, and this is the
cheapest moment to notice.

**DO refute, in the spec's own notes, any part that changed after validation** — what the plan said,
what it is now, and what the evidence was. He validated the plan; a silent divergence spends that trust.

TEST: every visible difference between before and after traces to a named source, or is explicitly
marked as a refutation with its reason.

## Check it against the real app, and fold the fix forward

**DO diff every overlaid and new-state component against a real render before showing him** — screenshot
the same component in the app at the same viewport and seed, and compare size, font, colour, spacing and
radius. A difference means the overlay measured the wrong value or missed one; fix it against the
measurement, never by nudging pixels.
**DON'T show a mockup you have not checked against the real app.** Nearly-right is the whole failure, and
the real render is the ground truth that catches it.

**DO open the finished mockup where its JavaScript actually RUNS, and look at it, before handing it
over** — a real browser, or copy it into the project / serve it over http. A mockup file written to a
scratch dir OUTSIDE the project renders as a STATIC snapshot in the preview pane: the `<script>` never
executes, so the JS-built grid, overlay and controls are invisible there and a blown-up icon or overflow
ships unseen. TEST: you have viewed the rendered mockup with JS run and confirmed no icon is oversized
and nothing overflows, before sending. (Inside a dashboard/gauntlet loop the embed IS that JS-running
render surface, so the check happens there — do NOT run `open <file>` per rebuild to pop the user a
browser tab; regenerate the gauntlet and play one sound instead, per `references/workflow-loops.md` §
preview/mockup.)

**DO fix the CLASS and fold it forward.** When a divergence traces to how this skill works — a
measurement it skips, a token it never reads, a state it forgets — propose the durable fix to this skill
through `/sk:claude-config-update` so the next mockup is exact from the first render. That is the
`self-healing-config` loop.

**DO iterate on the mockup until he EXPLICITLY confirms implementation — mockup-only, no platform
changes, in a loop.** Every change he asks for lands in the MOCKUP (re-capture, re-inject, edit the
spec), never in the app, and the loop continues until he says to implement in his own words ("implement",
"build it", "go build", "the mockup is approved"). A green mockup he has not signed off is not approval;
do not touch app source before the word. Then the second half of this skill starts.

## After he approves: implement it, prove it matches

**A mockup is only worth what ships.** On 2026-08-11 an approved mockup was called implemented three
separate times with three different details still missing, and each was found by Simon opening the
real screen — *"how come you're still missing mockup details"*. The comparison had been done from
memory every time, so whatever was forgotten was invisible to the person forgetting it. That is the
failure this half exists to make impossible.

### Build the inventory BEFORE writing any code

**DO re-read the approved `#spec` top to bottom and write every visible difference as a numbered
row.** Each row names three things: what changes, the file that must change, and **the CALL SITE that
must pass it**. Rows come out of the spec, never out of your memory of the round. Walk every
storyboard step, not just the entry screen — a state reached only by clicking through is the one most
likely to be missed.

**DO treat the third column as the real work.** A component that grows a prop no caller passes is the
DEFAULT outcome, not an edge case: the component changes, every test passes, the screen does not move.
Every row whose middle column is a component needs a row-mate that is the caller. The `columns` prop
no grid was asked for, the width constant nothing read, the formatter wired nowhere — one round, three
instances, all of them shaped identically.

**DO give every row a mechanical check** — a DOM query, a count, a computed style, a `data-testid` —
that returns a value you can read, in one line. Not "looks right".

**DON'T let a row exist only in the mockup's markup.** A control the mockup renders that no plan,
ticket or slice ever specified is still a row, and it is the one most likely to be dropped: the
Regenerate button was in the approved mockup, in nobody's spec, and absent from the screen through
three rounds of being told the work was done. Sweep the mockup for controls before sweeping the plan.

**When the work is fanned out, ownership follows the row, not the component.** One slice owns a
change AND its call site, or the wiring is stated as the reconciler's job. A slice that changes a
component and not its caller is individually correct and collectively useless, and every slice passing
its own gate is exactly what the failure looks like from inside.

### Then prove it, against the real screen

**DO open the real screen and run the checklist row by row**, reading the actual value each check
returns. Both screens, side by side, same session, same data.
**DON'T report a match you did not measure.** "Implemented" means every row returned its expected
value on the real screen, not that every file was edited and the suite is green. A green suite is what
the failure looked like all three times.

TEST: every row has a value next to it. A row with no value is an unimplemented row.

**DO tear down any scratch render rig you stood up to measure** — a temporary route or harness — in
the change that lands the work. The mockup document itself is not in the repo, so there is no dev route
to clean up.

## Report

**DO hand back the dashboard link (`#section=<id>`), where the mockup is already embedded as that
section's `target` asset with `status: "review"`, plus one numbered Before / Proposed image pair per
surface awaiting approval, inline in the reply.** Render the pairs from the current target revision at the
same viewport, theme and data, each with a one-line note of the change. Then the open questions as a
numbered list, each with the option you would pick. (the fix for an approval that waited until Simon asked
for before/after screenshots.)
**DON'T send the mockup as a file card (SendUserFile), an artifact URL, a bare file path, or `open` it.**
A file card per round reads as a new file each time and leaves the dashboard empty; the path belongs
only in the dashboard asset caption. (the fix for three rounds of a landing mockup handed back as
attachments while every dashboard panel said "No capture yet", 2026-09-26.)
TEST: every surface awaiting approval has a numbered pair in the reply, the pairs come from the
dashboard's target revision, no file card is attached, and `workflow-dashboard.py export` passes with the
mockup referenced.
**DON'T ask him about anything the preview already answers.**

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.