Recipes
One YAML file per screenshot, in paths.recipes. The filename is the recipe's name unless name: says otherwise.
| Field | Default | What it does |
|---|---|---|
name | filename | Output filename, without the extension |
source | app | app drives the site; file annotates an image you have |
file | — | The image to annotate, with source: file |
install | — | Which named destination to copy to |
url | site.url | Page to open for this recipe |
viewport | site's | Viewport for this recipe |
scale | site's | Device pixel ratio for this recipe |
theme | site's | Colour scheme for this recipe |
style | — | Style overrides for this recipe |
format | project's | png, jpeg or webp for this shot |
quality | project's | For the two that are lossy |
setup | [] | Steps that drive the site into the state to capture |
clip | viewport | The region to capture: viewport, full, or a query |
marks | {} | Named regions, resolved after setup runs |
mask | [] | Regions painted over before the callouts are drawn |
callouts | [] | What to draw on those marks |
numbered | — | Marks to number 1…n with a disc, in the order given |
retries | 0 | How many times to shoot this again if it fails, up to 5 |
check | project's | This recipe's --check limits and ignore, or false |
What gets captured
clip: viewport is the browser window at site.viewport. clip: full is the whole document, however far it scrolls. Anything else is a query, and the shot is the box of the element it finds — with pad or grow if it needs room around it.
A clip is held to the width of the viewport, because nothing widens a page the way scrolling lengthens it. Its height is not held to anything: a region reaching past the fold is captured by scrolling to it, so a long form or a full table comes out whole rather than cut at the bottom of the window.
Marks
A mark is a name you choose for a region inside the shot. Callouts point at marks by name, and so do numbered and check.ignore; one mark can carry as many callouts as you like.
clip: { css: '.order-row', contains: Acme Corp }
marks:
amount: { within: clip, text: $42.00 }
status: { within: clip, text: Open }
callouts:
- { mark: amount, text: What they owe }
- { mark: status, text: Where it stands } Marks resolve after setup has run, in the order they are written. That order is usable: within: clip narrows a query to the region being captured, and within: <mark> narrows it to one already resolved above.
A mark that matches nothing stops the run, naming the recipe and the mark — recipe "order-row": marks.amount — no element matched. That is the assertion a recipe makes: the shot is of what it says it is of, or there is no shot.
Annotating an existing image
To annotate an image that is already on disk, set source: file and give file its path. Callouts, style and install work the same way. It is the way to shoot a screen no script can reach — anything behind a sign-in you cannot automate, or a browser extension's UI. The file may be a PNG, a JPEG or a WebP; what it is read as comes from its bytes rather than its name.
There is no page to query, so marks are literal rectangles measured off the image: [x, y, width, height] in image pixels.
name: billing-settings
source: file
file: captures/billing-settings.png
install: guide
marks:
plan: { rect: [866, 874, 150, 50] }
callouts:
- { mark: plan, text: The current plan, place: top } When a shot fails
A run stops at the first recipe that fails, and says which recipe and which key in it could not be resolved. --keep-going shoots the rest instead, prints each failure as it happens, and names them all at the end.
✓ modal → screenshots/out/modal.png
✗ recipe "order-row": marks.amount — no element matched {"css":".amount"}
1 of 2 failed: order-row Either way the exit code is non-zero, so CI still fails. --keep-going works the same way with --check, where a recipe that could not be shot is reported FAILED alongside the ones that diffed.
retries shoots a recipe again when it fails, up to five times. It is for the failures that are gone a second later — an element that had not rendered, a request that had not landed — which a capture has because it drives a real application.
name: order-row
retries: 2 # three attempts in all Each attempt is a fresh browser context, and a failure is reported while the run is still going rather than after it. A source: file recipe never retries whatever this says — it has no page to be flaky about, so a second attempt could only reach the same answer.