with one click
visual-test
Visually verify a component by launching its Storybook story and taking a screenshot with playwright-cli. Use after making visual changes to a component.
Menu
Visually verify a component by launching its Storybook story and taking a screenshot with playwright-cli. Use after making visual changes to a component.
| 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 |
Visually verify $ARGUMENTS by launching Storybook and capturing a screenshot with playwright-cli.
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>
Always boot the per-component stories package (react-<component>-stories) via nx storybook target, which only imports its own component's stories and dependencies.
Find the component's stories package. Each v9 component has a dedicated stories package named react-<component>-stories:
yarn --silent 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:
49360), not the Storybook default 6006. Don't assume.Content-Type.Reliable detection — target the storybook node child (not the yarn wrapper), then probe each listening socket until one returns text/html:
# Wait up to 180s for the storybook child to bind an HTTP port.
# Pattern matches the node child specifically, not `yarn storybook dev` (the wrapper has no sockets).
for i in $(seq 1 180); do
SB_CHILD=$(pgrep -f "node.*\.bin/storybook dev" | head -1)
if [ -n "$SB_CHILD" ]; then
for port in $(lsof -a -p "$SB_CHILD" -i -P -sTCP:LISTEN 2>/dev/null | awk 'NR>1 {print $9}' | sed 's/.*://'); do
CT=$(curl -sI --max-time 2 "http://localhost:$port/" 2>/dev/null | grep -i '^content-type:' | grep -i 'text/html')
if [ -n "$CT" ]; then SB_PORT=$port; break; fi
done
if [ -n "$SB_PORT" ]; then break; fi
fi
sleep 1
done
echo "Storybook child PID=$SB_CHILD on port $SB_PORT"
Then wait for Storybook to finish compiling stories — the HTTP port answers before index.json is populated:
for i in $(seq 1 60); do
N=$(curl -s --max-time 2 "http://localhost:$SB_PORT/index.json" 2>/dev/null \
| python3 -c "import json,sys; print(len(json.load(sys.stdin).get('entries', {})))" 2>/dev/null || echo 0)
if [ "$N" -gt 0 ]; then break; fi
sleep 2
done
If no port turns up, or index.json never populates — do not fall back to the workspace-wide Storybook; read the nx output log and debug the per-component boot. The most common real failure is missing build artifacts for unstable re-export deps (see troubleshooting below).
Open the page with playwright-cli:
npx -y @playwright/cli@0.1.1 open "http://localhost:$SB_PORT"
Navigate to the specific story iframe and capture a screenshot. Use the iframe URL for a clean render without Storybook chrome:
npx -y @playwright/cli@0.1.1 goto "http://localhost:$SB_PORT/iframe.html?id=components-<component>--default&viewMode=story"
npx -y @playwright/cli@0.1.1 screenshot --filename=/tmp/visual-test-$ARGUMENTS.png
View the screenshot using the Read tool to visually inspect the rendered component.
Use snapshot to get the accessibility tree and find interactive element refs:
npx -y @playwright/cli@0.1.1 snapshot
Then interact with elements by ref (e.g., click, hover) before taking more screenshots.
If the component doesn't look right, go back to the code, fix the issue, and repeat from step 4 (Storybook hot-reloads changes).
Clean up when done:
npx -y @playwright/cli@0.1.1 close
# Kill storybook — the nx wrapper may already be gone, so target the child
[ -n "$SB_CHILD" ] && kill "$SB_CHILD" 2>/dev/null
lsof -i :$SB_PORT -t 2>/dev/null | xargs kill 2>/dev/null
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 &
# --port 0 asks Storybook to pick a free port; detect it via the pgrep/lsof pattern above
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.
# 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.
npx -y @playwright/cli@0.1.1 snapshot to get an accessibility tree — useful for verifying ARIA attributes and finding interactive elements.npx -y @playwright/cli@0.1.1 click <ref> to interact with the component (test hover states, open menus, etc.) before taking a screenshot.npx -y @playwright/cli@0.1.1 resize <width> <height> to test responsive behavior.Review a PR for correctness, pattern compliance, testing, accessibility, and safety. Produces a confidence score for merge readiness.
Triage newly-filed GitHub issues on the Fluent UI repo (microsoft/fluentui) following the Shield triage guidelines. Fetches open issues labeled `Needs: Triage :mag:` via the `gh` CLI, classifies each (bug vs feature, product area, partner ask, repro quality, a11y), recommends label changes and area-owner assignment, and then applies the approved changes. Use this skill whenever the user asks to triage issues, run shield triage, go through the triage queue, process needs-triage, or any variation -- even if they don't mention "Fluent UI" or "GitHub" explicitly, since that's the project context here.
Create a beachball change file for the current changes. Determines change type (patch/minor) and generates a description from the diff.
Run lint on affected packages, parse errors, and auto-fix common issues (design tokens, React.FC, SSR safety, import restrictions)
Quick lookup for a Fluent UI package — path, dependencies, owner team, Nx project details, and relevant docs
Find the matching Fluent UI design token for a hardcoded CSS value (color, spacing, font size, border radius, shadow)