Visual review
Unit specs render a component on its own, in jsdom: they never see a page that stopped scrolling, a washed-out dark theme or a button overflowing on mobile. Each of the four front-end apps therefore has a gallery: it serves the production build, drives it with Playwright and captures every page.
cd front/application # or landing, docs, playground
npm run build && npm run gallery
The captures and the contact sheets — every page in a grid, one per theme — land in
tools/.gallery/. Set CHROMIUM_PATH if Playwright cannot find a browser.
When to run it
After any change to a layout, a theme token, the shell or @blouppy/ui: those touch every
page at once.
The application's gallery
The app is signed in, so its gallery answers for the back end itself:
tools/gallery-fixtures.mjs serves the responses and Playwright fulfils the requests —
nothing is started. It captures two lists, declared in tools/gallery-scenes.mjs:
PAGES— every route the router has. Keep it exhaustive: a route missing from here is a page nobody looks at.SCENES— the states a URL cannot reach: an open dialog, a toast, a scrolled page. Each names the invariant it holds down.
Both go through both themes at three widths. Fixtures are shaped to make layout fail
visibly — long names, many rows — rather than to be realistic, and every page is
expected to render full: a capture showing an empty state is a missing fixture.
GALLERY_TRACE=1 names the endpoint that fell through.
Narrow a run while iterating:
npm run gallery -- --page=workspaces --theme=dark --width=mobile --no-sheet
Contrast
The playground also measures it: npm run contrast in front/playground/ checks the
design tokens against the contrast floors DESIGN.md sets. The gallery shows what a
change looks like; contrast says whether it is legible.