| name | browser-microscope |
| description | Real-browser DOM and layout microscope. Runs arbitrary JS in a live page, hit-tests why a click won't land (elementFromPoint — what element is covering it), dumps scroll and box geometry (scrollWidth, offsetLeft, overflow, scroll-padding), reads CSS custom-property design tokens via getComputedStyle, and sweeps viewport widths to find container-query / breakpoint thresholds. Use when a button does not respond to clicks, an element is mysteriously overlapped, covered, clipped or off-screen, scroll / snap geometry looks wrong, computed styles or tokens need reading live, z-index / stacking is suspect, or a layout bug only appears at certain widths. Drives the local Playwright npm package (no global CLI required) and supports HTTP basic-auth and self-signed staging certs. The layout-forensics complement to the journey-walking browser and browser-qa skills. |
| argument-hint | [url] [what to inspect] |
| allowed-tools | ["Bash","Read"] |
| metadata | {"author":"oleg","version":"1.0"} |
Purpose
Dissect a single frozen frame of a live web page to explain layout and
interaction bugs that jsdom and screenshots cannot — a click that does not
register, an element covered by an invisible overlay, wrong scroll/snap math,
a token that resolves to the wrong value, or a defect that only appears at
certain viewport widths. It drives the locally-installed Playwright package
through one bundled script and returns JSON, so findings are exact numbers, not
guesses. This is the introspection primitive; browser / browser-qa walk user
journeys, this one inspects the DOM truth at a moment in time.
Prerequisites
- The
playwright (or playwright-core) npm package must be resolvable from the
target project — usually a devDep, so run commands from the project directory.
Pass --playwright <path> to point elsewhere. The script exits with code 3 and
a remediation hint if it cannot find it — it never hard-stops the way a global
playwright-cli dependency would.
Variables
PROBE: ./scripts/probe.mjs # Bundled introspection engine (relative to skill root)
VIEWPORT: 1200x900 # Default viewport WxH; cross a breakpoint by changing width
WAIT_MS: 1000 # Settle delay after load before probing
Workflow
-
Run from the target project
cd into the repo so the script resolves its playwright devDep, then call
the bundled engine. Read node <PROBE> --help once if you need the full flag list.
- IF: the page is behind staging basic-auth → add
--http-auth user:pass
(username may be blank: --http-auth ":pass") and --ignore-https-errors.
- Example:
cd ~/Documents/app && node <PROBE> eval --url https://stg.example.com --http-auth ":s3cret" --ignore-https-errors --js "document.title"
-
When a click does not register → hit-test
- The most common "button is broken" cause: another element covers it.
hit-test does a real elementFromPoint at the element's center and reports
whether the click lands on the target or what is on top.
- IF:
lands: false → the covering element (tag/class/outerHTML) is eating the
click — inspect its z-index, position, or whether it is a stray overlay.
- IF:
visible: false (zero-size) → the control is display:none / collapsed,
not covered — check container queries and parent layout instead.
- Example:
node <PROBE> hit-test --url <url> --selector 'button[aria-label="Previous"]'
-
When geometry looks wrong → box
- Dumps
getBoundingClientRect + scrollLeft/scrollWidth/clientWidth/maxScrollLeft
- computed margins/padding/overflow/scroll-padding/transform for one element.
- IF:
scroll.overflowsX is true but maxScrollLeft is tiny → the rail barely
overflows; index-based "next/prev" math can clamp short of a slide (a real
class of carousel bug).
- Example:
node <PROBE> box --url <url> --selector '#rail .viewport'
-
Find the REAL scroll container → find-scroller
- A
querySelector('[class*=viewport]') can match a non-scrolling wrapper.
find-scroller returns elements whose COMPUTED overflowX is /,
with their scroll metrics — use this to grab the actual scroller, then it.
Gotchas
- Programmatic
.click() lies. element.click() and Playwright's locator.click()
on a forced node bypass hit-testing, so a covered button can look like it "works"
in script while a real user's pointer is blocked. Trust hit-test
(elementFromPoint), not a synthetic click, to decide whether a click truly lands.
- The local Playwright package is CommonJS.
import { chromium } fails under ESM;
the bundled script already handles this (default import then destructure) — keep that
pattern if you hand-roll a one-off.
- Class-substring selectors are treacherous with hashed CS-module names
(
_viewport_ab12 vs _viewportWrapper_cd34 both match [class*=viewport], and
querySelector returns the first in document order — often the wrong one). Prefer
find-scroller to grab the element by COMPUTED overflowX.
References
Worked diagnoses and advanced recipes
- IF: you want a full worked example (the Carousel "left button dead ≥692px" case),
more
eval snippets (focus order, stacking, container-query state), or screenshot
capture for visual diffing
- THEN: Read
./reference/recipes.md
- EXAMPLES:
- "show me how this found the carousel bug"
- "how do I check focus order / z-index stacking with this"
- "capture a screenshot at a specific width"
Works well with
Optional collaborators — browser-microscope runs standalone and these degrade gracefully if absent.
browser — the journey-walking complement; use browser to navigate, browser-microscope to dissect why a click won't land or a layout breaks.
browser-review / browser-qa — reach for the microscope to diagnose the layout failures these surface.
ios-simulator-microscope — the real-device counterpart; escalate there when a bug only reproduces in real iOS Safari (toolbar collapse, dynamic-viewport units, touch dynamics) that desktop Playwright can't model.