config/claude/skills/playwright/SKILL.md
Fast-path Playwright CLI recipes for ad-hoc browser testing and screenshots. Use when manually verifying UI changes in a local dev environment - covers installation, the open-snapshot-interact-screenshot loop, stable element targeting, and the gotchas that waste time.
npx skillsauth add dreikanter/dotfiles playwrightInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A focused, opinionated cheat sheet for ad-hoc UI verification - not a full
reference. The verbose canonical version lives in the playwright-cli
skill; check there if a command isn't covered here.
playwright-cli --version || npm install -g @playwright/cli@latest
Versions stick around. No need to reinstall every session.
playwright-cli open https://example.com # opens a fresh in-memory browser
playwright-cli --raw snapshot | head -40 # ARIA tree with [ref=eNN]
playwright-cli fill e17 "[email protected]" # interact via ref
playwright-cli click e27
playwright-cli screenshot "#some-id" \
--filename .playwright-cli/after.png # save to local file
playwright-cli close
Read screenshots back into the conversation with the Read tool on the
file path - they render inline.
--raw is your friendStrips the status/code/snapshot/events sections - only the result remains. Pipe it, JSON-parse it, diff it.
playwright-cli --raw snapshot > before.yml
# ...interact...
playwright-cli --raw snapshot > after.yml
diff before.yml after.yml
# get a select's options as JSON, before guessing labels
playwright-cli --raw eval \
"el => JSON.stringify([...el.options].map(o => ({value: o.value, text: o.text})))" e409
# computed style debugging
playwright-cli --raw eval \
"el => { const cs = getComputedStyle(el); return JSON.stringify({width: cs.width, display: cs.display}); }" e409
# loaded stylesheets - useful when wondering "why doesn't my CSS apply"
playwright-cli --raw eval \
"() => [...document.querySelectorAll('link[rel=stylesheet]')].map(l => l.href).join('\\n')"
In order of preference:
run-code when a re-render will invalidate refs:
playwright-cli run-code "async page => { await page.locator('#some-select').selectOption({label: 'Option A'}); }"
snapshot for one-shot interactions. They're invalidated
by any DOM-replacing event (Turbo Streams, React re-renders, HTMX
swaps, plain AJAX). Re-snapshot after such events.playwright-cli click "getByRole('button', { name: 'Submit' })"# whole page
playwright-cli screenshot --filename .playwright-cli/page.png
# specific element (use a stable selector, not a ref, for repeatability)
playwright-cli screenshot "#some-component" \
--filename .playwright-cli/component-after.png
For before/after comparisons in a PR: git checkout the parent commit of
the visual change (just for the affected view/CSS files via
git checkout SHA -- path/to/file), take the "before" shot, then
git checkout HEAD -- path/to/file to restore. Keep the in-page state
identical (same data, same selections) between shots.
If the DOM was replaced by a Turbo Stream, React state change, HTMX swap,
or any AJAX response, every ref in your earlier snapshot is now wrong.
Either re-snapshot, or target by stable #id via run-code.
reload, goto, and close can hang for 60s with the error
"does not handle the modal state" when a browser-native dialog
(confirm/alert/beforeunload) is open.
playwright-cli dialog-accept # OK on the dialog
playwright-cli dialog-dismiss # Cancel
If unsure whether a dialog is up, just call dialog-accept defensively
before goto/reload.
select retries silently when the label doesn't existplaywright-cli select e409 "Option that does not exist"
# - retrying select option action
# - waiting 500ms ...
It will eventually time out (60s). Always list the actual options first
with --raw eval (see above) before calling select.
playwright-cli requests # list of network requests
playwright-cli request 22 # headers/timing/etc for request #22
playwright-cli response-body 22 # response body
playwright-cli request-body 22 # request body
Pairs well with server logs - confirm both that the right params went out and that the server saw them.
playwright-cli open --persistent # default profile, on-disk
playwright-cli -s=mysession open ... # named session
playwright-cli list # all sessions
playwright-cli close-all
Most ad-hoc work doesn't need this - the default in-memory browser is
fine. Reach for --persistent only when you need to keep auth cookies
between sessions.
In order of likelihood:
playwright-cli reload (after dismissing any
dialog). Fingerprinted asset URLs usually rebust automatically, but
service workers and aggressive cache headers can still bite.[...document.styleSheets]
hrefs (snippet above). Check if your stylesheet is even present.
Conditionally-loaded bundles (theme switches, feature flags,
environment guards) may not link the file you expect.getComputedStyle tells you what actually
applied. Compare to what's in the source CSS.playwright-cli close # close the default browser
playwright-cli close-all # close every browser session
playwright-cli kill-all # if processes are stuck
development
Run a one-shot Claude Code subagent review of the current branch or PR without editing files.
tools
Use the notes CLI to create, list, read, append to, update, annotate, and delete notes in a date-based markdown archive — including daily todos with task rollover and tag operations across the store.
data-ai
Enable "Explain Like I'm a Smart 18 Year Old" mode for the rest of the conversation. Use when the user types /eli18.
data-ai
Enable "Explain Like I'm a Smart 18 Year Old" mode for the rest of the conversation. Use when the user types /eli18.