check-visual-parity
Load this skill when a UI change must not change how it looks, or only where intended (a migration, CSS refactor, or library upgrade): Playwright baselines, then explain every diff. Trigger on "make s
SKILL.mdplugins/standalone/ceh-web-frontend/skills/check-visual-parity/SKILL.md
GitHubCheck visual parity
Prove that two versions of a UI render the same, by screenshot diff, everywhere outside the areas meant to change. The task is done when every captured screen either matches the old version or has a diff with a written reason, and no diff is left unexplained.
Procedure
- List what to capture. Each route the change touches, at two viewports (360 and 1280 wide), in each theme the app has, in the states that render differently: empty, loaded, error, and the focus and hover states of the components the change touched. Write the list down: it is the coverage the result claims, and a screen not on it is not proven.
- Make rendering deterministic. Serve fixed data (MSW handlers or a seeded database), freeze
the clock with
page.clock.setFixedTime(...), wait fordocument.fonts.ready, and mask what still varies (avatars, relative times, ads) with themaskoption. Keep Playwright's defaultanimations: "disabled". A screen that differs between two runs of the same version cannot prove anything about two versions. - Write one spec over the list, one
expect(page).toHaveScreenshot("<route>-<viewport>-<state>.png")per entry, under the project's existing Playwright setup.ceh-web-frontend:write-vitest-playwright-testsholds the runner conventions. - Capture the baseline from the old version. Check it out in a worktree
(
git worktree add ../parity-base <old-commit>), copy the spec in, start that version, and runbunx playwright test <spec> --update-snapshots. Run it twice: if the second run is not identical, go back to step 2. - Compare the new version. Copy the baseline snapshots into the working tree, start the new
version, and run
bunx playwright test <spec>. Playwright writes the expected, actual, and diff image of each failure undertest-results/. - Explain every diff. Open each diff image. An intended change gets one line in the report
naming the change that caused it, then its snapshot alone is updated with
--update-snapshots --grep "<test name>". An unintended one is a bug, fixed in the new version and compared again. - Remove the worktree with
git worktree remove ../parity-base.
Rules
- Tolerance is set per screenshot, with a reason. Start at Playwright's default of zero
differing pixels. Raise
maxDiffPixelson one screenshot only, for a cause you can name, such as sub-pixel font anti-aliasing. A global threshold hides real regressions in every other screen. - Both versions run on the same machine or image, back to back. A baseline from a laptop against a run in CI compares two font renderers, not two versions.
- Commit the baseline snapshots with the spec, so the reviewer sees exactly what the new version was held to.
Output
**Compared:** <old commit> → <new commit>, on <machine or image>
| Screen | Result |
| ---------------------------------- | ----------------------------------------- |
| <route> <viewport> <theme> <state> | match / intended: <reason> / fixed: <bug> |
**Not captured:** <screens left off the list, and why>
Stop conditions
- The old version will not start → report it. A baseline taken from anything else (a design file, a screenshot from memory) proves nothing about the code.
- Two runs of the same version still differ after step 2 → report which regions vary and stop, since every comparison after that is noise.