Documentation.
Scan the responsive continuum locally, compare a candidate against a baseline, and keep both machine-readable evidence and a visual report.
Quick start
Run the published package directly, or install it as a pinned development dependency in CI.
npx widthwatch https://example.com \ --min-width 320 \ --max-width 1600 \ --max-samples 28 \ --max-captures 8 \ --output widthwatch.html \ --json widthwatch.json
If Chromium is not already available, run npx playwright install chromium once. For deterministic results, generate and compare baselines in the same operating system, browser version, font set, color scheme, and device scale factor.
Project configuration
npm install --save-dev widthwatch npx widthwatch init
This creates a versioned widthwatch.config.ts and a reusable GitHub Actions workflow. The workflow accepts your deployed preview URL, keeps repository permissions read-only, and uploads the portable report as an artifact.
TypeScript API
import {
scanAtReportSchedule,
compareReports,
generateHtmlReport,
type WidthWatchReport,
} from "widthwatch";
const candidate = await scanAtReportSchedule(
"http://localhost:4173",
baseline,
{
pageReady: (page) => page.waitForSelector("[data-app-ready]"),
readinessKey: "app-ready-v1",
},
);
const comparison = compareReports(baseline, candidate, {
maxDiffRatio: 0.002,
includeDiffImages: true,
});
Capture modes
visual is the correctness-first default. Adaptive scans first discover geometry across up to maxSamples widths, then open a fresh page and capture full-page evidence at no more than maxCaptureSamples selected widths. Exact baseline schedules always capture every requested width. Use mode: "layout" or CLI flag --layout-only for a faster single-pass viewport probe. --full-page is already the visual default; with --layout-only it captures the whole document while keeping scroll sweep disabled. reloadPerWidth reruns page initialization at every width; pageReady is a bounded Playwright hook whose context identifies the discovery or capture phase and requires a versioned readinessKey.
Stable output
WidthWatchReport.version is a schema discriminator. Minor releases add optional fields; a breaking schema requires a new version. probes preserves every geometry observation, frames contains screenshot evidence, and canonical issues unifies both sources without dropping discovery-only or capture-only findings. The optional sampling object records both schedules. Comparisons expose severity escalations and de-escalations separately, and fail closed when probe or evidence schedules, viewport dimensions, capture modes, rendering environments, or PNG dimensions are incompatible.
Migration
Baselines created by v0.3.0 or earlier use capture protocol 2 and must be recaptured. v0.3.1 baselines already use capture protocol 3 and remain compatible when the environment and report schedules match.
What it detects
| Finding | Signal | Caveat |
|---|---|---|
| Document overflow | scrollWidth > viewport | Horizontal applications may allowlist this later. |
| Element overflow | Rendered box crosses the viewport edge. | Decorative bleed can be intentional. |
| Clipped text | Text scroll size exceeds a hidden/clip box. | Intentional ellipsis should be ignored by selector. |
| Overlap | Visible leaf geometry intersects materially. | Overlays and badges can be intentional. |
| Layout jump | Adaptive samples isolate a geometry discontinuity. | Breakpoint changes are informational until they regress. |
| Visual diff | Pixel ratio exceeds the configured baseline threshold. | Rendering environment must match. |
Pull-request comparison
npx widthwatch "$CANDIDATE_URL" \ --baseline .widthwatch/home.json \ --json artifacts/home.json \ --output artifacts/home.html \ --fail-on-regression
Commit the typed baseline or download it from a trusted main-branch artifact. Never use pull_request_target to execute untrusted pull-request code with deployment secrets.
Hosted demo
The public surface is intentionally smaller than the local package: one page, 320–1440px, at most 5 geometry probes with compact JPEG captures, one browser at a time, three queued jobs, 200 requests, no media, and a 15-second navigation limit. Job status stays in memory for 30 minutes; deployments with the report runtime role enabled keep completed HTML reports in a private encrypted object store with a 7-day lifecycle. Anyone who has the public report link can open its screenshots and page evidence until expiry, so do not scan sensitive pages. The demo accepts public HTTP(S) pages without credentials or cookies.
Known limits
Canvas content, cross-origin iframe internals, intentional collisions, carousels, sticky transitions, content that changes between samples, and authenticated states need project-specific configuration. WidthWatch reports deterministic evidence; it does not pretend every geometric intersection is a product bug.