UI work reuses components, ships stories and screenshot baselines
State: open/proposed
Summary
Section titled “Summary”- UI work in the determined suite ports an approved mock before it builds anything new, gives every new or changed component Storybook stories and a screenshot baseline, and shows reviewers before/after and mock/built screenshots.
- A finished UI change is checked by parallel validation subagents. A large set of independent units runs as a Workflow.
- The
/ui-workskill (solutions/determined/.claude/skills/ui-work/SKILL.md) is the procedure that meets this standard.
A change that adds or alters a UI component or surface meets all eight rules.
- Duplication review before building. Search for an existing component or primitive before writing a new one. When an approved mock exists for the surface, porting the mock component’s markup and look comes first; reusing an existing component then applies to its behaviour only (e.g. lazy loading, switching state), never to look. Otherwise reuse it, extend it with a prop or slot, or promote a duplicate to a shared package. The PR body records what was searched and what was decided.
- Stories for every new or changed component. Each component has Storybook stories for its empty, loading, error and populated states and its edge cases, in light and dark, and at a narrow width when its layout responds to width. Stories take fixtures, never a live backend.
- A screenshot baseline per story. Each story is captured and compared against a committed baseline. A changed baseline is committed in the same PR as the change that moved it.
- Before/after screenshots for UI-visible changes. The PR commits before and after images on its branch and embeds them in a PR comment. The committed set and its index follow S-0012-human-reviewable-ui-changes.
- Design language. Appearance comes from
--d-*tokens, semantics andD*primitives, per S-0010-design-language-consumption. Light and dark render with parity. Platform-specific look values sit behind one token block. - Parallel validation. Once a change is ready, fresh subagents that did not write it review it in parallel: visual review, accessibility and keyboard, story and state coverage, duplication, and — when a mock exists (rule 8) — mock fidelity. They run within the session’s concurrency limit.
- Workflows for large sets. Work of more than about six independent units, or fan-out plus verification beyond a handful of agents, runs as a Workflow. Smaller work uses single Agent calls.
- Mock fidelity when a mock exists. For a surface with an approved mock, capture mock/built screenshot pairs (same fixture, viewport, theme, density) alongside the before/after set, and run a Mock fidelity validation lens that lists every visible difference with measurements. Every difference is rework unless the spec overrides the mock on purpose, cited by its spec line in the PR. Re-run the lens until it is clean.
The suite composes many experiences from shared packages. A component written twice drifts twice, and the second copy is found only when the two disagree on screen. Stories pin every state a component can show, so a state that renders as nothing is seen before it ships. A baseline turns “does it still look right” into a diff a machine can flag. A reviewer judges a UI change by looking, so the PR carries the pictures.
How to apply
Section titled “How to apply”- Where to search (rule 1).
packages/ts/ui(@determined/ui),packages/ts/app-frame,packages/ts/panes,packages/ts/code-editor,packages/ts/markdown-ui, the shell (solutions/determined/packages/shell), and sibling experiences undersolutions/determined/experiences/.docs/LIBRARIES.mdlists every shared library. Promotion follows S-0001-co-locate-first-promote-when-shared: keep a first use local, promote on the second. - Recording the review (rule 1). One PR section, “Duplication review”: the places searched, the candidates found, and reuse, extend, promote or new for each.
- Story setup (rule 2). An experience Storybook extends the shared
factory in
packages/ts/ui/.storybook/factory.ts, assolutions/determined/experiences/fitness/.storybookandsolutions/determined/experiences/agent-pants/.storybookdo. Stories live instories/, outsideapp/, so they never reach a product bundle. Thethemeglobal (iframe.html?globals=theme:dark) switches light and dark. - Capture (rule 3). The story baseline runner in
packages/ts/ui/.storybook/story-shots/shoots every enrolled story in light and dark, plus a narrow viewport for a story withparameters.screenshot.narrow, and compares each shot with a committed baseline in the package’s.storybook/baselines/. The baselines are Linux PNGs made in the pinned Playwright container, so the runner needs Docker.bun run storybook:checkin an enrolled package compares;bun run storybook:baselinerewrites the baselines after an intended change. Build the Storybook first (bun run storybook:build), or use the moon task (moon run exp-polish:storybook-check), which builds and compares.- CI runs the check for every enrolled Storybook in the
story-shotsjob. - Enrolled:
packages/ts/uiand the Polish experience. An experience enrols by adding the two scripts with its story id prefixes and a step pair in the CI job. parameters.screenshot.skipleaves a story out, with the reason as its value. Use it only for a story that cannot render in Storybook, and say so in the PR.- For images in docs (an exploration README),
capture.tsin the same folder writes PNGs without comparing. - Running app surfaces are still captured by the gated
tests/ui/<experience>/capture-*.e2e.tsdrivers (CAPTURE_SHOTS=1).
- Before/after (rule 4). Capture “before” from the base branch before the first edit. Commit both sets on the branch, then post a PR comment that embeds each pair with a one-line caption.
- Validation (rule 6). Validators are read-only and run in parallel with each other, never beside a writer in the same worktree. Each finding goes back to the implementer as rework or into the PR as a known limit.
- Mock fidelity (rule 8). Save each pair under
docs/screenshots/<set>/as--mock/--built, or compose one image withscripts/compose-screenshot-pair.ts. The lens is a fresh, read-only subagent that compares the pair against the mock: layout, spacing, type, colour, borders, shadows. The PR screenshot comment shows the pairs. - Reviewers. A UI PR with no duplication review, a changed component with no story for one of its states, or a visual change with no before/after comment is incomplete.
Anti-examples
Section titled “Anti-examples”- A new
StatusPill.vuein one experience whenDBadgewith anintentalready renders the same thing. - A component whose only story is the populated state. The empty and error states ship unseen.
- A story that fetches from a running platform server. It breaks offline and changes as the data changes.
- A PR that restyles a panel and shows only the after image.
- Twelve exploration directions run as twelve hand-dispatched agents with no shared verification step.
- A surface built to spec but compared only against its own prior screenshot, never against the mock it was drawn from.
- A change with no visual effect may cite the existing screenshots instead of recapturing them, as S-0012-human-reviewable-ui-changes allows.