| name | visual-test |
| description | Visually verify a component by launching its Storybook story and taking a screenshot with playwright-cli. Use after making visual changes to a component. |
| disable-model-invocation | true |
| argument-hint | <ComponentName> [story-name] |
| allowed-tools | Bash Read Grep Glob |
Visual Test a Component
Visually verify $ARGUMENTS by launching Storybook and capturing a screenshot with playwright-cli.
Prerequisites
Run playwright-cli via npx so nothing is installed globally on the user's box. The first invocation downloads @playwright/cli@0.1.1 into the npx cache; subsequent calls are cached. Every command below uses this form:
npx -y @playwright/cli@0.1.1 <command>
Critical: use the per-component Storybook only
Always boot the per-component stories package (react-<component>-stories) via nx storybook target, which only imports its own component's stories and dependencies.
Steps
-
Find the component's stories package. Each v9 component has a dedicated stories package named react-<component>-stories:
yarn nx show project react-<lowercase-component-name>-stories --json
If nx returns nothing with output of Could not find project react-<component>-stories, the component doesn't have its own stories package — check for a preview package (react-<component>-preview-stories) or ask before proceeding.
-
Start the component's Storybook dev server. Use the storybook target on the stories project directly — it's the most portable, since library aliases like react-<component>:start were only added in April 2026 and may not exist in older workspace snapshots:
yarn nx run react-<component>-stories:storybook &
-
Find the storybook port. Three quirks to know:
- Storybook picks a random high port on first boot (e.g.
49360), not the Storybook default 6006. Don't assume.
- The nx wrapper process often exits 0 after delegating to storybook, leaving the actual server running as a child. So the nx PID isn't the storybook PID.
- The storybook child opens two listening sockets: one for HTTP content, one for the webpack HMR event-stream. They are not ordered — either one can be numerically lower. Picking by port number is unreliable; pick by
Content-Type.
Reliable detection — target the storybook node child (not the yarn wrapper), then probe each listening socket until one returns text/html:
for i in $(seq 1 180); do
SB_CHILD=$(pgrep -f "node.*\.bin/storybook dev" | head -1)
if [ -n "$SB_CHILD" ]; then
for port $(lsof -a -p -i -P -sTCP:LISTEN 2>/dev/null | awk | sed );
CT=$(curl -sI --max-time 2 2>/dev/null | grep -i | grep -i )
[ -n ]; SB_PORT=; ;
[ -n ]; ;
1
Troubleshooting
yarn nx run react-<component>-stories:storybook says the target doesn't exist.
The workspace graph may be stale (recent reparent). Run yarn nx reset then retry. If stroybook aliases still don't exist, use the direct yarn invocation:
cd packages/react-components/react-<component>/stories && yarn storybook dev --port 0 &
Story ID Pattern
Story IDs follow the pattern <category>-<component>--<story>:
# Default story for Button
components-button--default
# Appearance variant
components-button--appearance
# Default story for Menu
components-menu--default
To discover exact story IDs, open the Storybook sidebar and use snapshot to find navigation links,
or check the story file's export default { title: '...' } metadata.
Iframe URL Format
# Local storybook (replace $SB_PORT with the actual port)
http://localhost:$SB_PORT/iframe.html?id=components-button--default&viewMode=story
# Dark theme
http://localhost:$SB_PORT/iframe.html?id=components-button--default&viewMode=story&globals=theme:webDarkTheme
The /iframe.html URL gives a clean render without Storybook chrome — always prefer this for screenshots.
Tips
- Use
npx -y @playwright/cli@0.1.1 snapshot to get an accessibility tree — useful for verifying ARIA attributes and finding interactive elements.
- Use
npx -y @playwright/cli@0.1.1 click <ref> to interact with the component (test hover states, open menus, etc.) before taking a screenshot.
- Use
npx -y @playwright/cli@0.1.1 resize <width> <height> to test responsive behavior.
- For multiple story variants, take a screenshot of each: Default, Appearance, Size, Disabled, etc.