performance-audit
Playwright Performance Audit
Measures the same three metrics Google actually grades pages on — Largest Contentful Paint, Interaction to Next Paint, Cumulative Layout Shift — using a real browser session, and rates them against Google's own published thresholds rather than inventing a new scale.
Relationship to the other skills
Reuse scenario-mapper's page list the same way visual-snapshot does, rather than rediscovering site structure. For INP specifically (see below), reuse an existing flow-runner scenario's interactions where one exists — a passive page load alone can't produce a meaningful INP reading. Findings here are a natural category for report-generator to fold in, and cadence (how often to run this) is exactly the kind of thing test-plan should schedule rather than running on every deploy — a full audit is more than a quick sanity check needs.
Before you start
Same tool check as the rest of the toolkit, needing whichever tool lets you evaluate JavaScript in the page — same requirement as accessibility-audit. Get the web-vitals library's source available locally the same way and for the same reason as that skill gets axe-core: check node_modules/web-vitals, install it if missing (npm install web-vitals --no-save), and inject its source as a string rather than pointing the browser at a CDN script tag — it works regardless of the target's Content-Security-Policy, for the identical reason described in accessibility-audit.
Core principles (and why)
This measures lab performance, not the Core Web Vitals score Google actually grades. Google's real page-experience grading uses CrUX — field data aggregated from real users' real devices and networks, at the 75th percentile over a rolling 28-day window. A single Playwright-driven run is one synthetic sample under one set of conditions on one machine. It's a genuinely useful early-warning signal and a real regression check, but it is not the number Search Console or actual visitors will produce. State this plainly every time — never present a lab reading with the same confidence as an official field score.
INP needs real interaction — a page load alone won't produce a meaningful reading. LCP fires once, early, from a passive load. INP measures responsiveness to actual clicks, taps, and keypresses, so if you only navigate and wait, there's nothing for it to measure. This is the same reason standard Lighthouse runs fall back to Total Blocking Time as an INP proxy — TBT correlates with responsiveness but isn't the same metric. Drive the page through its real interactions while measuring: reuse a flow-runner scenario for that page if one exists, or at minimum interact with the primary CTA and any obvious form fields, rather than measuring a page that never got touched.
Use the specialized library — don't reconstruct these metrics from raw Performance API entries. Same "delegate to the right tool" reasoning as accessibility-audit's use of axe-core: web-vitals is the standard, browser-vendor-aligned implementation of exactly these metric definitions. Reinventing LCP/CLS/INP calculation from scratch risks subtly wrong numbers that don't match what Google or anyone else's tooling would report for the same page.