Prove a change on the site's pixels with the screenshot harness - a baseline of the commit it starts from, a capture after, a compare read as a report - so a refactor shows it moved nothing and a design change shows it moved only what it meant to. Use before any merge that touches src/, styles or images, when asked to prove, verify, check parity or "make sure nothing else changed", and after a `SIZE`, `CHANGED` or `reflow` line needs reading.
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
SSkills DirectorySkills Directory
Is that skill safe to install?
Are you the author of Design Proof?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/hkamkar-design-proof)
---
name: design-proof
description: Prove a change on the site's pixels with the screenshot harness - a baseline of the commit it starts from, a capture after, a compare read as a report - so a refactor shows it moved nothing and a design change shows it moved only what it meant to. Use before any merge that touches src/, styles or images, when asked to prove, verify, check parity or "make sure nothing else changed", and after a `SIZE`, `CHANGED` or `reflow` line needs reading.
argument-hint: [pages or sections the change touches]
---
# Design proof
The harness is `agentic-cms visual-parity` (`docs/visual-parity.md` of the
kit — the modes, the file names, the waits, the traps — and
`docs/commands.md` for the flags). A proof is two captures and one compare;
the compare's report says what moved, where, and whether the rest of the
page survived.
A site says once what it is — the widths it is designed at, its schemes,
the pages a bare proof covers, the ref it proves against — in its profile,
`src/config/harness.ts` (`docs/visual-parity.md` § The site's profile), so
`pnpm kit visual-parity proof` with no flag is the site's short proof. A
site without one gets the built-in eight widths in light: write the profile
before the first proof.
## Read first
`docs/visual-parity.md` § Reading a compare; the change's diff (`git diff
develop --stat`) to know which pages and sections it can touch, and
whether it touches anything that animates (`grep -rl "agentic-cms/ix"
src/components`) or a hover, focus, checked or open state, which decides
whether `--motion` or `--states` is part of the proof.
## Steps
1. **The baseline**, from the exact commit the change starts from, never
from a checkout that moved on:
```bash
pnpm kit visual-parity capture before --ref develop # or the branch's base commit; its demo routes are left out
```
(A served build standing in for a commit — `--url` — has its `git log
-1` printed first.) With animation changed, also `--motion`; with
hover, focus, checked or open states touched, also `--states`. The
widths and the scheme are the site's profile (`src/config/harness.ts`),
so no flag names them; a site with two schemes captures the second
with `--scheme` as well, or lets `proof` run both. Only a first capture
of a large site needs the background (every later one copies the
unchanged pages); a capture is finished when
`.parity/visual/<label>/capture.json` exists.
2. **The change**, built exactly as it will be committed (`pnpm build`;
nothing edited after it), then:
```bash
pnpm kit visual-parity capture after
pnpm kit visual-parity compare before after --json
```
Or steps 1 and 2 in one, once the change is built:
`pnpm kit visual-parity proof [--motion] [--states] [--ref <base>]` —
each pass's baseline, after capture and compare in each scheme the
profile names, one summary, exit 1 on any difference; its captures are
`proof-before` / `proof-after` (`-motion`, `-states`, and `-dark` for
the dark scheme of a site with two), read like any compare in step 3.
Keep the proof as short as the change allows:
- the site's widths and schemes, which the profile says once
(`widths`, `schemes` in `src/config/harness.ts`) and every capture
takes without a flag — `--widths w,w` or `--scheme` only for a run
that needs another;
- the pages the change touches (`--pages /a,/b`, the same on both
captures and the compare; `proof --pages` passes it to every step,
and the profile's `proof.pages` is what a bare `proof` covers),
and one page per template (`--sample 1`) only when the change is
site-wide: a token, the chrome, a shared component;
- `--states` only when hover, focus, checked or open states changed,
`--motion` only when animation did;
- each scheme the profile names, never one the site does not have.
A page is left out because the change cannot reach it, never to save
time: a page whose build files did not change is copied from
`.parity/shot-cache/` rather than taken again, and several browsers
work at once (`--jobs`), so a wider capture stays cheap when the change
needs it.
3. **Read the report** (`report.json`, or the lines):
- `ok` everywhere: a refactor proved. Say so with the counts.
- `CHANGED` on the pages the change touches, `rows` inside the changed
section: expected; look at the diff image once to confirm the rows are
the section's.
- `SIZE … shift`: the section grew or shrank and everything below moved
intact — the crops (`<name>.before.png`, `.after.png`) show the band;
expected for a design change that changes a height.
- `SIZE … reflow`, or `CHANGED` rows on every text line below one
section: first read the `cause:` line under it — a section whose
height changed by a fraction of a pixel moves everything below by that
fraction and repaints it; fix or accept that height. When it says no
section moved and the rows run through every text line below that
section, something changed how the compositor paints the whole page —
usually a new stacking context (`position: relative`, a `z-index`, a
`transform`) on a section whose animated elements overflow it.
`pnpm kit probe / --select "<the section>"` prints the stacking chain;
keep the utility to the breakpoint that needs it.
- `CHANGED` on a mid-flight motion frame (`--s03-500`, 20–30 %): timing
jitter is possible; re-run the after capture once with `--fresh` (else
an unchanged page is copied from `.parity/shot-cache`, the same frame
again), and identical on the re-run is accepted. A settled or static frame never jitters (an inline
SMIL loop is held at its `data-rest` there, and at each frame's own
time mid-flight; a loop that still differs lacks `data-rest` or ships
as an `<img>`, which no capture can hold).
- A settled motion frame that keeps differing where a sequence runs long:
the section declares `data-settle="<ms>"` (`STANDARD.md` §7).
- `FLAKY`: one side took that shot with an image still loading, even
after a reload; its `cause:` names the image. It proves nothing either
way: retake the page with `--pages <route> --fresh` on that side.
- `MISSING`: its `cause:` says why — a page appeared or disappeared, a
width one side did not take, a state one side skipped for want of its
target — or the compare lacks the `--pages` the capture had (with it,
only the named pages are judged, on both sides).
4. **Say what the proof says**, in numbers: how many files, which
differed, the verdicts, and why each difference is the change. A design
change is its own commit and says so.
## Traps
- Build the exact tree you mean to prove before capturing. The capture
photographs a copy of that build; after its `snapshot:` line the tree is
free to build and edit.
- Commit each proven state before the next change.
- `.parity/` is gitignored and grows fast: `pnpm kit visual-parity clean`
(`--dry-run` first) keeps the newest captures and removes the rest with
their compares.
- Other origins' scripts and beacons are held back on a build the harness
serves (analytics, a chat widget); their images and fonts still load. A
site whose look includes a third-party script's drawing (a chat bubble, a
consent banner from another origin) captures with `--third-party allow`,
on both sides.
- Every browser starts as a first visit. What the site shows until a choice
is stored (its own consent banner, fixed over the page) covers what it
sits on in every shot: seed the choice in `src/config/harness.ts`
(`export const harness = { storage: { <key>: <record> } }`, `{now}` where
the record has a time), which every capture, both sides of a proof, `shot`
and `probe` read; recapture the baselines once, since `compare` refuses an
unseeded one. `--no-storage` shows the banner itself, to prove it.
- A baseline built for the wrong sha proves nothing: the sha is in
`meta.json` and on the compare's first line.
## Verify
The compare's report, read as step 4 says, on the exact tree the branch
commits.
End the procedure with `pnpm lint`, 0 failures, after the last edit: it
holds the engines in every file (copy, head, forms, tags, e-mail) and the
library's placement, and a failure is fixed at its source, never by
loosening a rule. <!-- required -->
## Stop for the user
A `reflow` or a difference outside the change's pages that the report does
not explain; the merge.