From stardust
Reconciles a built web page against its source prototype using pixel/layout and structural content+typography diffs to catch regressions.
How this skill is triggered — by the user, by Claude, or both
Slash command
/stardust:diffThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Two probes that compare a **source** prototype against a **built** page. They catch
Two probes that compare a source prototype against a built page. They catch disjoint failure classes — run BOTH; either alone gives a false "looks fine".
Both are framework-agnostic Playwright probes that compare two rendered URLs by
computed style + DOM (not pixels). All stack-specific language lives in a
profile (--profile eds|generic); the comparison logic is generic.
stardust:deploy skill's Step 10) — use --profile eds.--profile generic.Not for: a single static file with no JS decoration (use the build/harness URL so components are decorated — a raw .plain.html has no roles to classify).
| Probe | Script | Sees | Blind to |
|---|---|---|---|
| Pixel / layout | skills/diff/scripts/visual-diff.mjs | stretched images, dropped max-width wraps, blank renders, surface/ground colour flips, image-count gaps | "right text, wrong slot"; a dropped CTA (full pixels, plausible colours → no flag) |
| Structural content + type | skills/diff/scripts/content-diff.mjs | MISSING / ROLE-SWAPPED headings·eyebrows·CTAs, invented/dropped body copy, rendered-FACE font forks (width probe) | geometry / layout regressions |
content-diff extracts an ordered, role-classified inventory (heading / eyebrow /
cta+href / body) from each <main>, classifying by computed style + tag so the
prototype's DOM and the built DOM compare symmetrically, then diffs them.
# Prereq 0: playwright importable from the project root — probe
# node -e "import('playwright').then(()=>process.exit(0))"
# and re-install (npm i -D playwright --no-save --legacy-peer-deps) on failure:
# a --no-save install from extract is PRUNED by any later real npm i
# (extract SKILL.md § Setup). Run the copied scripts from the project, not the plugin.
# Copy the WHOLE skills/diff/scripts/ dir: content-diff imports diff-profiles.mjs AND
# content-inventory.mjs, and the deploy gates (#93/#94) import ../../diff/scripts/*.
# Prereq: a RENDERABLE source. Static → serve from its own dir (python3 -m http.server).
# The build URL must be the DECORATED page (live/preview or a local harness), not raw markup.
PROTO="http://localhost:8791/<prototype>.html"
BUILD="https://<branch>--<repo>--<owner>.aem.page/<path>" # or http://localhost:3000/<harness>
# 1. PIXEL/layout
node skills/diff/scripts/visual-diff.mjs "$PROTO" "$BUILD" --profile eds --sections ".hero"
# 2. STRUCTURAL content + type
node skills/diff/scripts/content-diff.mjs "$PROTO" "$BUILD" --profile eds # --json dumps both inventories
Flags (both tools): --profile eds|generic (default eds), --width <px> (default 1280),
--main <selector> (content root; content-diff defaults from the profile, visual-diff to main),
plus the live-target set (shared engine: scripts/live-session.mjs — every context sends the
real-Chrome UA and the standard Chrome request headers; the UA alone still 403s on
Akamai-class bot management):
--ua <string> — user agent override (default: real-Chrome desktop UA).
--wait-until <state> — goto wait override. Default rule (one shared
defaultWaitUntil in scripts/live-session.mjs), decided per URL side, three tiers:
networkidle (local prototypes / harnesses, unchanged);.aem.page, .aem.live, .hlx.page,
.hlx.live → networkidle (they decorate asynchronously and reliably reach
networkidle; measuring at domcontentloaded reads the pre-decoration DOM — flaky
false reds / FONT FORK on deploy Step 10);domcontentloaded (live sites with analytics beacons
never reach networkidle).--wait-until overrides all three tiers.
--dismiss [sel,...] — dismiss overlays on both sides: cookie consent (clicked, not
removed) AND timed marketing/newsletter modals, plus optional extra site-specific
selectors; the mouse is parked afterwards.
--headed — escalation for bot-managed sites: headed stealth real Chrome.
--locale <tag> — pin Accept-Language + context locale (geo-redirecting sites capture a
different locale per run otherwise).
visual-diff also: --out <dir>, --sections a,b (per-section screenshots).
A bot-management challenge/blocked interstitial on either navigation fails LOUD with
exit 3 — it is never measured as the source. Escalate with --headed; if that is still
blocked, the site needs crawl.mjs-class capture and the check cannot run headless.
A plain (non-challenge) HTTP error on either side — e.g. a 404 build side, normal on aem.page before preview propagation — is NOT fatal: the probe logs a loud warning, measures the error page, and the flags (BLANK RENDER / content asymmetry) carry the signal with exit 0. That is the probes' advisory contract: 0 = ran (flags advisory), 1 = probe error, 3 = bot challenge.
document.fonts.check). source X→sys means the prototype named font X but never loaded it and fell back to system — the build self-hosting the intended fallback is then CORRECT, not a bug. All forked lines are grouped into one advisory.Pass bar: visual red flags none/justified AND content-diff 0 structural 🔴 (🟡/🟠 confirmed intended). Re-run BOTH after each fix.
skills/diff/scripts/diff-profiles.mjs holds them. A profile supplies the source/target labels,
per-flag remediation hints, the font-delta threshold, the default content-root
selector, and the eyebrow classifier thresholds. The engines carry no stack
strings.
eds (default) — Edge Delivery / DA remediation language + stardust:deploy finding numbers.generic — neutral source/build language for any stack.Add a profile by copying generic in diff-profiles.mjs and editing hints.
The structural probe's classifier + differ live in skills/diff/scripts/content-inventory.mjs,
SHARED with two stardust:deploy gates so every fidelity layer measures with the same instrument:
section-schema.mjs (the pre-code ENCODE/DECODE contract, deploy #93) and block-roundtrip.mjs
(the in-loop per-block gate, deploy #94 — the same inventory diff, run per block at authoring time
against a local decorate() harness, no DA needed, exit-code gated). Run the in-loop gate while
converting; run THIS skill's two probes as the final post-deploy proof. A defect first found here
that the in-loop gate passed = the delivery pipeline reshaped the content in transport — fix the
block's flattened-shape fallback, not the authoring.
Call both scripts in a validation phase and gate on the output. The
stardust:deploy conversion workflow's Validate phase runs both after building
a local harness; mirror that:
visual-diff … --profile eds → fix STRETCHED/FLUSH-LEFT/SURFACE-GROUND/GAP flags (unless justified).content-diff … --profile eds → fix every 🔴; confirm 🟡/🟠.Naming note: this skill ships in the
stardustplugin and is invoked asstardust:diff. It pairs withstardust:deploy, whose Step 10 runs both probes as its Validate gate.
npx claudepluginhub p/adobe-stardust-plugins-stardustPerforms visual regression testing by capturing and comparing Playwright screenshots before/after changes across desktop, tablet, and mobile viewports to detect layout shifts, color changes, and UI bugs.
Detects visual and UI regressions via screenshot comparison and pixel-diff analysis using Playwright or Puppeteer. Captures cross-browser/viewport screenshots, categorizes layout shifts and color changes, generates diff reports for CI/CD PR checks.
Visual-QA loop for replica sites: screenshots, responsiveness gate (390px), visual-parity per section, accessibility checks, and automated fix escalation. Invoked by orchestrators.