1---
2name: verify-frontend-change
3description: Verify a UI change end-to-end before declaring it done — drive the change in a headless Playwright browser (never the user's Chrome), screenshot the result. ONE verification pass per task, at the end — never after each intermediate edit. Scale it to the change: trivial tweaks (copy, spacing, a color) get one desktop look; the full loop (console, mobile, before/after) is for structural/layout or cross-page changes. Triggers on "verify the UI", "did that work", "check it in the browser", or before reporting a UI change complete.
4---
5
6# Verifying frontend changes
7
8Never report a UI change as complete on a successful edit alone. A green typecheck
9and a clean build do not prove a button clicks, a layout holds, or a page stops
10scrolling sideways on a phone. Verify it the way a human reviewer would: open it,
11touch it, look at it. Only then is it done.
12
13This is the stop condition for frontend work. If any step below fails, fix the
14issue and rerun from step 1 — do not hand back partially verified work.
15
16**Run this ONCE, at the end of the task — never after each intermediate edit.**
17And scale it to the change:
18
19- **Trivial tweak** (copy, spacing, a color, one component's styling): one
20 desktop screenshot of the result. Skip the mobile resize, the console read,
21 and the before/after pair unless something looks off.
22- **Structural / layout / cross-page change** (grid changes, shared components,
23 global CSS, new fixed/absolute elements, anything touching widths or
24 overflow): the full loop below.
25
26## Before you verify: fix the root cause, not the symptom
27
28If the change is a styling / spacing / layout fix, diagnose the real cause before
29editing CSS. Read the component tree, find the element actually responsible, and
30change that one thing. Do not guess-and-check by layering CSS. If a fix does not
31work on the first attempt, step back and re-analyze instead of piling on more
32rules. (This mirrors the standing UI rules — reason the change through, then
33verify once at the end.)
34
35For anything nontrivial, load the `meirlabs-ui-design` skill first (the
36design-system entry point — it carries the house rules this verification is
37checking against) and `emil-design-engineering` for the polish craft bar.
38
39## The loop
40
411. **Serve the page.** Start the dev server if it is not already running. For the
42 meirlabs monorepo app this is `cd apps/web && pnpm dev` (port 3000 — check
43 `lsof -i :3000` first; reuse a running server rather than starting a second).
44 Open the edited page.
45
462. **Drive the change directly.** Use the Playwright MCP server
47 (`mcp__playwright__browser_*`) — an isolated headless Chromium, NOT the user's
48 real Chrome. Never use `mcp__claude-in-chrome__*` for verification unless the
49 task genuinely requires the user's logged-in session, and say why first.
50 Navigate with `browser_navigate`, then do the thing a user would:
51 - New control (button, input, toggle, link): interact with it (`browser_click`,
52 `browser_type`, …) and confirm the expected state change actually happens —
53 not just that it renders. Use `browser_snapshot` to find elements.
54 - Screenshot **before and after** the interaction (`browser_take_screenshot`)
55 so the change is visible, not asserted.
56
573. **Check the console.** Read `browser_console_messages`. Zero **new** errors or
58 warnings introduced by the change. A pre-existing warning is not a pass — note
59 it, but a new one is a fail.
60
614. **Check mobile, specifically horizontal scroll** — only when the change
62 plausibly affects responsive layout (widths, overflow, position, fixed
63 elements, shared layout). Resize to a phone width (`browser_resize` to
64 ~390px) and confirm the page does not scroll sideways — this is the single
65 most common regression from a CSS/layout edit here. Confirm the change still
66 works and tap targets are reachable at that width.
67
685. **Confirm no layout shift and no hidden-element hacks.** Nothing you added uses
69 `display:none` to hide an element (it shifts layout on toggle) — hiding must use
70 `visibility:hidden` or `opacity:0`. Watch for content jumping as the page
71 settles or as the control changes state.
72
73## Verification (stop conditions)
74
75Treat these as the bar. Every one must pass before calling a UI change done.
76(For a trivial tweak per the scaling rule above, the bar is 1 + 6 only —
77renders correctly on desktop, typecheck clean.)
78
791. **Renders and behaves.** The edited page loads and the specific change is
80 visible in an after-screenshot; any new control produces its expected state
81 change when interacted with. Before/after screenshots captured.
82
832. **Console clean.** `browser_console_messages` shows zero new errors or
84 warnings attributable to the change.
85
863. **No horizontal scroll on mobile.** At ~390px width the document does not
87 scroll horizontally. Quick check via `browser_evaluate`:
88 ```
89 document.documentElement.scrollWidth <= window.innerWidth
90 ```
91 evaluates `true`.
92
934. **No layout shift on state change / load.** Toggling the control or reloading
94 does not visibly jump the layout. No `display:none` used to hide anything.
95
965. **Consistent with siblings.** The changed element matches how the same kind of
97 element (header, card, button, spacing, type) is styled on other pages — UI
98 consistency is a hard requirement, not a nice-to-have. Spot-check one sibling
99 page if the change touches a shared pattern.
100
1016. **Typecheck still clean** (for TS/JSX edits). In `apps/web`: `pnpm typecheck`
102 (`tsc --noEmit`) exits `0`.
103
104If a step fails: fix it, then rerun from step 1. Report the change as done only
105when all six hold, and say which you verified — not "should work now".
106