CLI
vendor/bin/focus [options]
vendor/bin/focus init [--skip-browsers]
Running vendor/bin/focus with no command generates every screenshot and card in the manifest. composer focus does the same without Composer's process timeout (see Installation).
Options
| Option | Purpose |
|---|---|
--config=PATH, -c |
Manifest path. Defaults to focus.php. |
--only=NAMES |
Only generate the named screenshots or cards. Comma-separated or repeated. |
--theme=THEME |
Only generate one theme: light or dark. |
--cards-only |
Render cards only, without starting Workbench. |
--no-cards |
Capture screenshots only. |
--refresh-templates |
Download GitHub card templates again instead of using the cache. |
--headed |
Show the browser while capturing. |
--base-url=URL |
Use a running application instead of starting Workbench. |
--prune |
Delete orphaned assets after an unfiltered run. |
--force, -f |
Prune without asking for confirmation. |
--list |
Show the planned captures without opening a browser. |
--fresh-login |
Sign in again instead of reusing the previous run's session. |
-v |
Also print each capture as it starts. |
Filtering
vendor/bin/focus --only=editor
vendor/bin/focus --only=editor,brick-picker
vendor/bin/focus --theme=dark
Filters narrow what the manifest defines; they never add captures. An unknown --only name is an error, so a typo does not silently capture nothing.
--list prints each capture's theme, mode, viewport, scale, and output path, and each card's template, theme, size, pixel dimensions, and output path. It is a quick way to check a manifest without starting a browser, and it never touches the network.
Cards
Cards render after the screenshots, in the same browser. A card whose screenshot failed in the run is not rendered, and is reported as Screenshot failed.
vendor/bin/focus --cards-only # render cards from the screenshot files on disk
vendor/bin/focus --only=social # one card, from the screenshot files on disk
vendor/bin/focus --no-cards # screenshots only
When cards use screenshots the run does not capture, Focus names them once before rendering, so you know which files were used as they were. --cards-only does not start Workbench or sign in, so it also works in repositories without one. See Cards.
Headed mode
vendor/bin/focus --headed --only=brick-picker
Opens a visible Chromium window. Use it while writing a screenshot definition to see what the browser sees. Headless is the default.
Warning
Do not commit screenshots from a headed run. Headed and headless Chromium can differ by a pixel, so headed output breaks the byte-for-byte stability of your assets. Run once more without --headed before committing.
Output files
Each capture is written as:
{name}-{theme}.png
so Screenshot::make('editor') produces docs/assets/editor-light.png and docs/assets/editor-dark.png. Cards are written as {name}-{size}-{theme}.png in art/ (see Cards). Names contain no timestamps, hashes, or package prefix, and each run overwrites the previous files. The form {name}-{viewport}-{theme}.png is reserved for a future feature that captures one screenshot at several viewports, so existing names will not change.
Using screenshots in documentation
The awcodes documentation hub serves images from docs/assets/, so the default output path needs no configuration. Reference both theme variants from a page, scoped with the #gh-light-mode-only and #gh-dark-mode-only fragments, and the hub shows the one matching the reader's theme:


From a page in a subdirectory, such as docs/usage/editor.md, the path is ../assets/brick-picker-light.png. The fragment convention comes from GitHub, which has since deprecated it, so a page viewed on GitHub may show both images.
Atomic writes
Each capture is written to a temporary file in the output directory and moved into place only once it succeeds. A failed capture never replaces or deletes the previous file, and the failure report says when an existing file was left over from an earlier run, so it is not mistaken for a fresh one.
Orphaned assets
Renaming or removing a screenshot or card leaves its old files behind. After an unfiltered run, Focus lists files in the screenshot and card output directories that match the naming scheme (*-light.png, *-dark.png) but were not produced by the manifest. With --cards-only or --no-cards, the directory of the skipped side is not checked.
To delete them:
vendor/bin/focus --prune # lists the files and asks for confirmation
vendor/bin/focus --prune --force # deletes without asking
Pruning never touches files that do not match the scheme, so other images in docs/assets are safe. Orphans are not reported when --only or --theme is used, and --prune cannot be combined with them.
Warnings
Warnings do not fail the run. Besides warnings for individual captures and cards, Focus warns when a screenshot's or card's light and dark images are byte-identical. That usually means the page has no dark mode, so the second file doubles the assets for nothing; restrict the screenshot with ->themes([Theme::Light]).
Exit codes
Focus exits with 0 when every capture and card succeeds and 1 when the manifest is invalid, the server or browser cannot start, authentication fails, a card template cannot be found or downloaded, any capture or card fails, or pruning fails. One failed capture or card does not stop the others.
Reading a failure
✗ brick-picker (dark) Selector not found
focus([data-focus="brick-picker"]) matched no elements.
Screenshot brick-picker
Theme dark
Viewport 1440x1000
URL http://127.0.0.1:53211/admin/pages/1/edit
Selector [data-focus="brick-picker"]
Not updated: docs/assets/brick-picker-dark.png is from a previous run.
The first line gives the failure category. See Troubleshooting for what each one means.