web-perf
Web Performance Skill
Tooling Notes
This skill relies on Chrome DevTools MCP. If the MCP server is missing, stop and ask the user to configure it.
First: Verify MCP Tools Available
Run a quick check before starting. If navigate_page or performance_start_trace is unavailable, stop and ask the user to add the MCP config.
"chrome-devtools": {
"type": "local",
"command": ["npx", "-y", "chrome-devtools-mcp@latest"]
}
Key Guidelines
- Be assertive and verify claims with network, DOM, or codebase evidence.
- Verify before recommending any removals or refactors.
- Quantify impact using estimated savings; skip 0ms impact changes.
- Skip non-issues: document low impact without recommending changes.
- Be specific: name exact assets and sizes when suggesting optimisation.
- Prioritise ruthlessly: excellent metrics should be called out as such.
Workflow
Copy this checklist and use it to track progress through the audit:
Audit Progress
- [ ] Phase 1: Performance trace (navigate + record)
- [ ] Phase 2: Core Web Vitals analysis (includes CLS culprits)
- [ ] Phase 3: Network analysis
- [ ] Phase 4: Accessibility snapshot
- [ ] Phase 5: Codebase analysis (skip if third-party site)
Phase 1: Performance Trace
The problem is unknown page performance; capture a cold-load trace so you can diagnose it.
-
Navigate to the target URL:
navigate_page(url: "<target-url>") -
Start a performance trace with reload:
performance_start_trace(autoStop: true, reload: true) -
Wait for trace completion and capture the results.
Troubleshooting:
- If the trace is empty, confirm the page loaded via
navigate_page. - If insight names do not match, inspect the trace response to list available insights.
Phase 2: Core Web Vitals Analysis
Use performance_analyze_insight to extract key metrics. If an insight name fails, use the insightSetId from the trace response to discover available insights.
Common insight names:
| Metric | Insight Name | What to Look For |
|---|---|---|
| LCP | LCPBreakdown |
TTFB, resource load, render delay |
| CLS | CLSCulprits |
Images without dimensions, injected content, font swaps |
| Render Blocking | RenderBlocking |
CSS/JS blocking first paint |
| Document Latency | DocumentLatency |
Server response time issues |
| Network Dependencies | NetworkRequestsDepGraph |
Chains delaying critical resources |
Example:
performance_analyze_insight(insightSetId: "<id-from-trace>", insightName: "LCPBreakdown")
Key thresholds (good/needs-improvement/poor):
- TTFB: < 800ms / < 1.8s / > 1.8s
- FCP: < 1.8s / < 3s / > 3s
- LCP: < 2.5s / < 4s / > 4s
- INP: < 200ms / < 500ms / > 500ms
- TBT: < 200ms / < 600ms / > 600ms
- CLS: < 0.1 / < 0.25 / > 0.25
- Speed Index: < 3.4s / < 5.8s / > 5.8s
Phase 3: Network Analysis
List requests to identify optimisation opportunities:
list_network_requests(resourceTypes: ["Script", "Stylesheet", "Document", "Font", "Image"])
Look for:
- Render-blocking resources: JS/CSS in
<head>withoutasync/defer/media. - Network chains: late-discovered resources that delay critical assets.
- Missing preloads: fonts, hero images, key scripts without preload.
- Caching issues: weak
Cache-Control, missingETag/Last-Modified. - Large payloads: oversized or uncompressed JS/CSS.
- Unused preconnects: confirm zero requests to the origin before removal.
For details:
get_network_request(reqid: <id>)
Phase 4: Accessibility Snapshot
Take an accessibility tree snapshot:
take_snapshot(verbose: true)
Flag high-level gaps:
- Missing or duplicate ARIA IDs.
- Poor contrast ratios (WCAG AA: 4.5:1 normal text, 3:1 large text).
- Focus traps or missing focus indicators.
- Interactive elements without accessible names.
Phase 5: Codebase Analysis
Skip if auditing a third-party site without codebase access.
Detect the stack by searching for config files:
| Tool | Config Files |
|---|---|
| Webpack | webpack.config.js, webpack.*.js |
| Vite | vite.config.js, vite.config.ts |
| Rollup | rollup.config.js, rollup.config.mjs |
| esbuild | esbuild.config.js, build scripts with esbuild |
| Parcel | .parcelrc, package.json (parcel field) |
| Next.js | next.config.js, next.config.mjs |
| Nuxt | nuxt.config.js, nuxt.config.ts |
| SvelteKit | svelte.config.js |
| Astro | astro.config.mjs |
Also check package.json for framework dependencies and build scripts.
Tree-shaking and dead code:
- Webpack: confirm
mode: "production",sideEffects, andusedExports. - Vite/Rollup: tree-shaking is default; check
treeshakeoptions. - Watch for barrel files and large libraries imported wholesale.
Unused JS/CSS:
- Check for CSS-in-JS versus static extraction.
- Look for PurgeCSS/UnCSS, or Tailwind
contentconfig. - Identify dynamic imports versus eager loading.
Polyfills:
- Check
@babel/preset-envtargets anduseBuiltIns. - Look for oversized
core-jsimports. - Verify
browserslistis not overly broad.
Compression and minification:
- Check for
terser,esbuild, orswcminification. - Verify gzip/brotli compression in server config.
- Confirm source maps are external or disabled in production.
Output Format
Present findings in this order:
- Core Web Vitals Summary: table with metric, value, rating.
- Top Issues: prioritised list with estimated impact (high/medium/low).
- Recommendations: specific, actionable fixes with snippets.
- Codebase Findings: framework/bundler and optimisation notes (omit if no codebase access).