Full-page screenshots across breakpoints
Capture a whole page - not just the visible viewport - at several widths in one run, and get a report that doubles as a responsive-layout check.
Install
make install # into ~/.claude/skills, available in every project
make check # confirm it is installed and a browser will start
make install-project puts it in ./.claude/skills instead, so it travels
with a repository. make link symlinks rather than copies, so git pull
updates the installed skill. make uninstall removes it.
Quick start
make capture URL=https://example.com
# or
node scripts/capture.mjs --url https://example.com --out ./screenshots
Widths default to 375,768,992,1200,1440. Override with --widths.
If a capture will not start, node scripts/capture.mjs --check lists every
browser it can find and reports which of them actually launch.
Why not just take a screenshot
Three things make this harder than it looks, and the script handles all three.
A viewport screenshot is not a page screenshot. Most tooling captures what is on screen. Stitching slices by scrolling breaks on sticky headers, which reappear in every slice. Use the browser's own full-page capture instead.
Images decode late. A full-page capture fires before lazy or still-decoding
images finish, and you get half-loaded photography in an otherwise perfect
screenshot. The script waits for every <img> to settle first.
The page may need setting up. Behind basic auth, a cookie banner, a state
toggle. --auth covers credentials; --prepare runs arbitrary JS in the page
before the shot.
The report is the point
Every run prints one row per capture:
width requested captured height status
375 375 375 4205 ok
768 768 773 3865 OVERFLOWS BY 5px
A full-page capture expands to fit horizontal overflow. So when the captured width exceeds the requested width, the page overflows sideways at that breakpoint - which is a layout bug you would otherwise have to hunt for. The screenshots come with a free responsive audit attached.
Options
| flag | default | meaning |
|---|---|---|
--url |
required | page to capture |
--out |
./screenshots |
output directory, created if absent |
--widths |
375,768,992,1200,1440 |
comma separated CSS widths |
--scale |
2 |
device pixel ratio; 1 for smaller files |
--auth |
none | user:password for HTTP basic auth |
--prepare |
none | path to a JS module run in the page before each capture |
--name |
page |
filename prefix |
--chrome |
auto-detected | path to a Chrome binary |
Preparing the page
--prepare takes a module exporting a default async function. It runs inside
the page for every capture, and receives the width so it can vary by
breakpoint:
// prepare.mjs
export default async function (width) {
localStorage.setItem('cookie-consent', 'accepted');
if (width >= 992) {
document.querySelector('[data-view="grid"]')?.click();
}
}
Capturing two variants of the same page means two runs with different
--prepare and --name.
Requirements
Node 18+ and a Chrome or Chromium binary. The script finds one automatically
from the Puppeteer cache, a Playwright cache or the usual system locations; use
--chrome to point at a specific build. Only puppeteer-core is installed - no
browser download.
See references/troubleshooting.md when a browser will not start.