hyperhive/claude-plugins/plugins/base/skills/headless-screenshot/SKILL.md
iris 086f3f43d6 claude-plugins: add headless-screenshot skill to base
Captures a lesson from this session: "no browser in this container"
usually means no browser pre-installed, not unreachable. nixpkgs#chromium
is a `nix shell` away and one headless invocation renders a page +
writes a PNG, no puppeteer/playwright needed for a static screenshot.

Every agent hits "I have no browser" sooner or later when reviewing a
frontend change; this has been rediscovered independently across
several sessions rather than remembered. Belongs in base (every agent
needs it, not role-specific) alongside the sibling skills already
there.
2026-08-12 18:31:55 +02:00

3.9 KiB

name description
headless-screenshot Take a real screenshot of an HTML/CSS page from inside a container with no browser installed - `nix shell nixpkgs#chromium --command chromium --headless=new --disable-gpu --no-sandbox --screenshot=<out.png> --window-size=<w>,<h> "file://<path-or-url>"` renders the page and writes a PNG in one command, no puppeteer/playwright or pre-installed browser needed (only needed for scripted interaction like clicking, not a static render). Use this whenever you're about to claim a CSS/layout/frontend change looks right, whenever a PR reviewer or the operator asks to see a screenshot of a web UI change, or before asking a peer GUI-testing agent to screenshot something you could just render yourself - reasoning about a visual change from source or compiled CSS alone is not the same as actually seeing it rendered, and a real browser is one shell command away even when `which chromium` comes back empty.

Headless Screenshot

"No browser in this container" usually means no browser is pre-installed, not that one is unreachable. nixpkgs#chromium is a nix shell away, and a single headless invocation renders a page and writes a PNG without a running server, a display, or any scripted interaction library.

The command

nix shell nixpkgs#chromium --command chromium \
  --headless=new --disable-gpu --no-sandbox \
  --screenshot=/path/to/out.png \
  --window-size=1200,900 \
  "file:///path/to/page.html"
  • --no-sandbox is required inside most agent containers (no user namespaces for Chromium's sandbox); --disable-gpu sidesteps GPU init failures in a headless environment. Both are safe defaults here
    • this isn't a browser you're exposing to untrusted input.
  • The target can be a local file:// path or a real http(s):// URL (a dev server, a served dist/, a deployed page) - same command either way.
  • --window-size sets the viewport; match it to what you actually want to see (a narrow width to check a mobile layout, a wide one for a dashboard row).
  • Swap --screenshot= for --dump-dom= to get the rendered DOM as text instead of an image - useful for confirming markup/attributes landed correctly without needing to eyeball pixels.

Stray dbus/UPower errors on stderr are harmless container noise (no session bus, no power daemon) - ignore them as long as the PNG file actually gets written.

When source-reading isn't enough

Grepping compiled CSS or tracing class names tells you the rules exist and target the right selectors - it doesn't tell you what the result actually looks like (wrong tier picked, two rules fighting, a value that's technically correct but visually off). Before telling someone a layout/CSS change "looks right," or before reaching for a peer agent's GUI-testing capability, try rendering it yourself first:

  • Testing a real page: serve the built dist/ (or point at wherever it's already deployed) and screenshot that - it's the actual production bundle, not a reconstruction.
  • Testing an isolated CSS change (e.g. a shared stylesheet used by several pages): a small self-contained HTML file with the real CSS inlined and a few representative markup samples is faster to build than standing up a server, and just as honest about what the CSS actually renders.
  • Attach it: post the PNG as a PR/issue comment (or file attachment) alongside a one-line note on what it's rendered from - a reviewer with no browser either benefits from the same shortcut.

What this doesn't replace

This is a static render, not a browser automation tool. Verifying an interaction (a click, a hover state, a form submission) needs puppeteer/playwright or similar - reach for those only when the question is genuinely about behavior, not appearance. A missing emoji/glyph rendering as a box in the screenshot is usually a font availability artifact of the headless environment, not a real bug - don't mistake one for the other when reporting results.