Take annotated UI screenshots from YAML recipes, using Playwright.
shotlist opens your running site, drives it to the state you describe, clips a region, draws callouts on it, and writes the image where you want it. Each screenshot is a YAML file. There is no per-screenshot code.
shotlist.dev/docs is the reference — every key, every step verb, every query primitive. This file is the short version.
npm i -D shotlist playwrightPlaywright is an optional peer dependency. shotlist does not install it, because its postinstall downloads browsers.
npx shotlist --initwrites a commented shotlist.config.yaml and a first recipe. Or set the two up by hand:
1. Configure the project once — shotlist.config.yaml in the project root:
site:
url: http://localhost:3000
viewport: { width: 1440, height: 900 }
scale: 2
theme: dark
install:
guide: content/guide/images2. Write a recipe — screenshots/recipes/order-row.yaml:
name: order-row
install: guide
setup:
- click: { role: button, name: Orders }
clip:
css: '.order-row'
contains: Acme Corp
pad: 20
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 }3. Shoot it:
npx shotlist order-row --installThe image is written to screenshots/out/order-row.png, and --install copies it to
content/guide/images/order-row.png. PNG is the default; image.format also takes jpeg
and webp, per project or per recipe.
npx shotlist --init # write a starter config and recipe
npx shotlist # list every recipe
npx shotlist <name> [<name>…] # shoot into paths.out
npx shotlist <name> --install # …and copy to its install destination
npx shotlist --all --install # shoot everything
npx shotlist --all --keep-going # …carrying on past a recipe that fails
npx shotlist --check # compare against committed images
npx shotlist --check --diff # …and write a before/after/changed image
npx shotlist --check --json # …and report it as JSON on stdout
npx shotlist --help # the full list, from the tool--check re-shoots every recipe and compares it against the committed image, exiting
non-zero if any changed — so it can gate a build. Errors name the recipe and the key
inside it that could not be resolved:
recipe "order-row": marks.amount — no element matched {"css":".amount"}
A recipe is data. There is no step that evaluates JavaScript and there will not be one: if a screenshot cannot be described, that is a missing verb or query primitive, and it gets added. See CONTRIBUTING.md.
shotlist also runs in automation, where the config may come from a fork's pull request or
from whoever submitted it. A shot list only ever opens its own site, and never reads or
writes .env, .git, .ssh and their like — in every mode, with no flag to set. For the
rest, --untrusted starts no processes, opens nothing on the runner's own network, and
stays inside the project.
Full detail, and the two things it does not cover, at shotlist.dev/docs/security.
| Page | What it covers |
|---|---|
| Install | Setting up, and a first recipe |
| Configuration | Every key, starting the site, style and fonts |
| Recipes | Every field, and annotating an existing image |
| Steps | The step vocabulary |
| Queries | Sources, filters, traversal, finders |
| Callouts | Labels, numbered discs, masking |
| Macros and data | Sharing setup, driving a shot from a list |
| CLI | Every flag, the API, editor and agent support |
| Checking for staleness | --check, diffs, and a shot that changes |
| What a config can do | What a run is allowed to reach |
Changes are in CHANGELOG.md. How the package is built is in AGENTS.md; setup and releases are in CONTRIBUTING.md.
MIT © Nicola Mustone — see LICENSE.