aw codes icon logo
Docs Hub

Search across every project.

Focus Switch project

Generate consistent documentation screenshots from Laravel Workbench applications using Playwright.

0.x Current

On this page

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:

![The brick picker](assets/brick-picker-light.png#gh-light-mode-only)
![The brick picker](assets/brick-picker-dark.png#gh-dark-mode-only)

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.