Browser testing
Exercise the running app the way a person would. A screenshot of first paint
is not a test. After the flow works, measure density and fit, then fix
what fails. Follow .cursor/rules/ui-design.mdc and
.cursor/skills/frontend/layout.md.
Before you start
- Do not start or restart the dev server. Ask the user for the URL if
it is not already open.
- Prefer the Cursor browser tools (navigate, snapshot, click, type, fill,
screenshot). Do not add Playwright, Cypress, or another runner unless
the user asks.
- If no browser tools are available, say so. Use the closest substitute
(
curl against the URL they gave, or unit tests) and stop.
Procedure
- List open tabs. Navigate to the URL the user is running.
- Snapshot the page. Drive the main flow end to end: click, type, submit,
navigate. Confirm behavior, not only layout.
- Hit every route that shares the state, data, or components you changed.
- Check edge states the change can break (empty, error, loading, auth).
- Run Fit and density (below) at the current browser panel size.
Do not set a desktop width (no 1280×800, no device metrics override).
Then set mobile ~390×844 only, and run the main flow plus Fit and
density again. Do not guess mobile from the panel screenshot. When the
mobile pass is done, clear the device override so the page follows
the panel again.
- Run the design review. Fix CSS, then re-measure and re-screenshot
the panel size and mobile. Stop only when measurements pass and the
review is clean.
Fit and density
Use the browser CDP Runtime.evaluate (returnByValue: true) on a
representative heading, text input, select, button, and the main column
(or [data-shell] if present):
({
vw: innerWidth,
vh: innerHeight,
scrollOverflow:
document.documentElement.scrollWidth >
document.documentElement.clientWidth + 1,
bodyFont: getComputedStyle(document.body).fontSize,
letterSpacing: getComputedStyle(document.body).letterSpacing,
wordSpacing: getComputedStyle(document.body).wordSpacing,
titlePx: title ? parseFloat(getComputedStyle(title).fontSize) : null,
inputH: input ? input.getBoundingClientRect().height : null,
buttonH: button ? button.getBoundingClientRect().height : null,
shell: shell
? {
left: shell.getBoundingClientRect().left,
right: innerWidth - shell.getBoundingClientRect().right,
top: shell.getBoundingClientRect().top,
width: shell.getBoundingClientRect().width
}
: null
})
Fail and fix if any of these are true:
| Check |
Fail when |
| Horizontal fit |
scrollOverflow |
| Column fit |
shell width > 42rem at the panel size, or left/right gutters differ by > 32px |
| Hero spacing |
first heading top > 72px |
| Body type |
body font-size > 16px |
| Title type |
heading font-size > 22px |
| Control height |
input/select/button height > 36px (not textarea) |
| Tracking |
letter-spacing or word-spacing not normal / 0px |
| Words |
labels look tracked-out, colliding, or split mid-word |
| Mobile stack |
a multi-field row still sits in one line at 390px and overflows |
Do not “pass” a 44–48px control to satisfy tap-target folklore. Compact
visual height is 32–36px; use row padding for hit area.
Design review
Take a screenshot (not only a snapshot) of each important state at the
panel size and at mobile.
- Title, form, and list share one left edge. Labels sit on their controls.
Actions in a row share height and vertical center.
- Spacing is even: 8–12px gaps, not 24–32px. No collisions or clipped text.
- Pills: same radius (999px), compact padding, type ~12px. They do not
jump the row height.
- Icons are one set and one optical size. No emoji.
- At the panel size the layout is a compact column, not a stretched
dashboard and not a vertically centered poster. Mobile (~390): no
horizontal scroll, columns stacked, pills wrap.
If something is off, edit the component or CSS, then measure and
screenshot the same states again at the panel size and at mobile.
Viewport
- Desktop / default: leave the page at the current browser panel size.
Do not call
Emulation.setDeviceMetricsOverride (or any other resize)
for 1280, 1440, or a “standard desktop.”
- Mobile only: set ~390×844 (
Emulation.setDeviceMetricsOverride with
mobile: true).
- After the mobile pass,
Emulation.clearDeviceMetricsOverride so the
page matches the panel again.
Checks
- Do not use raw CDP click/type when the dedicated browser tools exist.
- Unlock the browser tab when the session’s browser work is done.
- Keep unit tests in
*.spec.ts (/frontend → testing). This skill is
the live browser pass plus visual review.
1---2name: browser-testing3description: Verify UI in a real browser: exercise the flow, then fail oversized, misaligned, or non-fitting layouts on desktop and mobile. Use when the user asks to browser-test, e2e, smoke-test, design-review, check mobile, fix alignment or density, or when they run /browser-testing.4---56# Browser testing78Exercise the running app the way a person would. A screenshot of first paint9is not a test. After the flow works, **measure** density and fit, then fix10what fails. Follow `.cursor/rules/ui-design.mdc` and11`.cursor/skills/frontend/layout.md`.1213## Before you start14151. Do **not** start or restart the dev server. Ask the user for the URL if16 it is not already open.172. Prefer the Cursor browser tools (navigate, snapshot, click, type, fill,18 screenshot). Do not add Playwright, Cypress, or another runner unless19 the user asks.203. If no browser tools are available, say so. Use the closest substitute21 (`curl` against the URL they gave, or unit tests) and stop.2223## Procedure24251. List open tabs. Navigate to the URL the user is running.262. Snapshot the page. Drive the **main flow** end to end: click, type, submit,27 navigate. Confirm behavior, not only layout.283. Hit every route that shares the state, data, or components you changed.294. Check edge states the change can break (empty, error, loading, auth).305. Run **Fit and density** (below) at the **current browser panel size**.31 Do not set a desktop width (no 1280×800, no device metrics override).32 Then set **mobile ~390×844** only, and run the main flow plus Fit and33 density again. Do not guess mobile from the panel screenshot. When the34 mobile pass is done, **clear the device override** so the page follows35 the panel again.366. Run the **design review**. Fix CSS, then re-measure and re-screenshot37 the panel size and mobile. Stop only when measurements pass and the38 review is clean.3940## Fit and density4142Use the browser CDP `Runtime.evaluate` (`returnByValue: true`) on a43representative heading, text input, select, button, and the main column44(or `[data-shell]` if present):4546```js47({48 vw: innerWidth,49 vh: innerHeight,50 scrollOverflow:51 document.documentElement.scrollWidth >52 document.documentElement.clientWidth + 1,53 bodyFont: getComputedStyle(document.body).fontSize,54 letterSpacing: getComputedStyle(document.body).letterSpacing,55 wordSpacing: getComputedStyle(document.body).wordSpacing,56 titlePx: title ? parseFloat(getComputedStyle(title).fontSize) : null,57 inputH: input ? input.getBoundingClientRect().height : null,58 buttonH: button ? button.getBoundingClientRect().height : null,59 shell: shell60 ? {61 left: shell.getBoundingClientRect().left,62 right: innerWidth - shell.getBoundingClientRect().right,63 top: shell.getBoundingClientRect().top,64 width: shell.getBoundingClientRect().width65 }66 : null67})68```6970**Fail and fix** if any of these are true:7172| Check | Fail when |73| ------------------ | ---------------------------------------------- |74| Horizontal fit | `scrollOverflow` |75| Column fit | shell width > 42rem at the panel size, or left/right gutters differ by > 32px |76| Hero spacing | first heading `top` > 72px |77| Body type | body `font-size` > 16px |78| Title type | heading `font-size` > 22px |79| Control height | input/select/button height > 36px (not textarea) |80| Tracking | `letter-spacing` or `word-spacing` not `normal` / `0px` |81| Words | labels look tracked-out, colliding, or split mid-word |82| Mobile stack | a multi-field row still sits in one line at 390px and overflows |8384Do not “pass” a 44–48px control to satisfy tap-target folklore. Compact85visual height is 32–36px; use row padding for hit area.8687## Design review8889Take a screenshot (not only a snapshot) of each important state at the90panel size and at mobile.9192- Title, form, and list share one left edge. Labels sit on their controls.93 Actions in a row share height and vertical center.94- Spacing is even: 8–12px gaps, not 24–32px. No collisions or clipped text.95- Pills: same radius (999px), compact padding, type ~12px. They do not96 jump the row height.97- Icons are one set and one optical size. No emoji.98- At the panel size the layout is a compact column, not a stretched99 dashboard and not a vertically centered poster. Mobile (~390): no100 horizontal scroll, columns stacked, pills wrap.101102If something is off, edit the component or CSS, then measure and103screenshot the same states again at the panel size and at mobile.104105## Viewport106107- **Desktop / default:** leave the page at the current browser panel size.108 Do not call `Emulation.setDeviceMetricsOverride` (or any other resize)109 for 1280, 1440, or a “standard desktop.”110- **Mobile only:** set ~390×844 (`Emulation.setDeviceMetricsOverride` with111 `mobile: true`).112- After the mobile pass, `Emulation.clearDeviceMetricsOverride` so the113 page matches the panel again.114115## Checks116117- Do not use raw CDP click/type when the dedicated browser tools exist.118- Unlock the browser tab when the session’s browser work is done.119- Keep unit tests in `*.spec.ts` (`/frontend` → testing). This skill is120 the live browser pass plus visual review.