Core Web Vitals
Pathrule2 Rules • 2 Memories • 2 Skills
The three thresholds have not moved: LCP 2.5 seconds, INP 200 milliseconds, CLS 0.1, all measured at the 75th percentile of real visits rather than in a lab run on your laptop. What changes is which mistakes cost you them, and assistants reliably make the same ones: a hero image that is lazy-loaded, a click handler that blocks the main thread for half a second, and content that jumps because nothing reserved space. This bundle covers the fixes per metric, how to measure the number that actually counts, and the review gate that keeps a fast page fast.
Suggested path map
Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.
Rules
2Give the LCP element priority and remove what blocks it/src/apphighstrictThe largest above-the-fold element loads eagerly with high priority, its origin is preconnected, fonts are preloaded, and nothing render-blocking sits in front of it.
| 1 | LCP is a race between the browser discovering the largest element and everything else you asked it to do first. Most bad LCP is self-inflicted ordering. |
| 2 | |
| 3 | - Identify the LCP element (usually a hero image, sometimes a heading or video poster) and load it eagerly with a high fetch priority. Never `loading="lazy"` above the fold, and never behind a client-side component that must hydrate before the image is requested. |
| 4 | - Serve it at the size it renders, in a modern format, with explicit dimensions, and with a responsive `srcset`. An oversized hero is the single most common LCP regression. |
| 5 | - Preconnect to the origins the critical path needs (image CDN, font host) and preload the fonts used above the fold. Self-host fonts where you can, use `font-display: swap`, and subset them. |
| 6 | - Keep render-blocking work out of the head: inline the critical CSS, defer the rest, and load non-essential scripts with `defer` or after interaction. A third-party tag manager in the head delays every metric. |
| 7 | - Server-render the above-the-fold content. If the first meaningful paint waits for a client fetch, LCP includes that round trip, and no image optimisation will save it. |
Keep the main thread free so interactions stay under 200ms/srchighstrictSplit long tasks, keep handlers cheap, ship less JavaScript, and let the browser paint feedback before doing the work.
| 1 | INP measures the worst realistic interaction: from the input to the next paint. Anything occupying the main thread at that moment is added to the number. |
| 2 | |
| 3 | - Do the minimum inside the handler: update the state that renders feedback, yield to the browser, then do the expensive part. Breaking a long task into chunks (yielding with `scheduler.yield` where available, or a task boundary) turns one blocking second into responsive frames. |
| 4 | - Move genuinely heavy computation off the main thread (a web worker) or to the server. Parsing a large payload, sorting thousands of rows, or running a diff in a click handler is what makes an app feel broken. |
| 5 | - Ship less JavaScript: split by route, load below-the-fold and rarely used components on demand, and audit the bundle for a dependency that costs more than the feature. Hydration cost is proportional to what you sent. |
| 6 | - Keep third-party scripts off the critical path and out of the interaction path. Load them after interaction or in a worker-based sandbox, and measure their main-thread time before accepting them. |
| 7 | - Prefer CSS transitions and transforms over JavaScript animation, and avoid layout thrashing (reading a layout property, writing, then reading again inside a loop). |
Memories
2Layout shift comes from unreserved space/srcEvery element whose size arrives late needs its space reserved up front: images, embeds, fonts, banners, and anything inserted above existing content.
| 1 | CLS is not a mystery metric. It is the sum of content moving after the user could already see it, and each source has a mechanical fix. |
| 2 | |
| 3 | - Set explicit `width` and `height` (or `aspect-ratio`) on every image, video, iframe, and embed so the box exists before the resource arrives. |
| 4 | - Reserve space for anything that loads late: ads, consent banners, promotional bars, dynamically injected components. Render a placeholder of the final size rather than letting the element appear and push the page down. |
| 5 | - Never insert content above existing content after paint. If a notification or banner must appear, either reserve its slot in the initial render or overlay it without affecting layout. |
| 6 | - Match fallback and web font metrics (size-adjust and the local fallback descriptors) so the swap does not reflow text, and preload fonts used above the fold. |
| 7 | - Animate with `transform` and `opacity`, which do not trigger layout. Animating `top`, `height`, or `margin` moves everything after the element. |
| 8 | |
| 9 | See /src/app for the LCP rule and / for how these are measured. |
Measure the field number, not the lab number/rootThe score that counts is real-user data at the 75th percentile per page type; Lighthouse is a diagnostic, and single-page apps need soft navigation awareness.
| 1 | Lab and field metrics answer different questions, and optimising the wrong one is how teams stay red while their scores go green. |
| 2 | |
| 3 | - The thresholds are LCP 2.5s, INP 200ms, CLS 0.1, evaluated at the 75th percentile of real visits. That means a quarter of your users can be slower and you still pass, and it also means your fast laptop tells you almost nothing. |
| 4 | - Collect field data continuously: the `web-vitals` library reporting to your own analytics, plus public field data for context. Segment by page template, device class, and country, because one slow template can drag a whole property. |
| 5 | - Use Lighthouse and traces to diagnose a specific regression, not as the target. A lab score can improve while the field regresses (and vice versa) because the lab has no real network, no real CPU throttling, and no real interactions. |
| 6 | - In a single-page app, a route change is not a document load. Instrument soft navigations so the metrics reflect what the user experienced, and remember that a slow client transition never shows up in a naive document-level measurement. |
| 7 | - Treat TTFB as a diagnostic for LCP rather than a goal in itself: it explains where LCP time went (server, redirect chain, cache miss) even though it is not a Core Web Vital. |
| 8 | |
| 9 | See /src for the INP rule and /src/app for the LCP rule these numbers grade. |
Skills
2web-vitals-triage/rootDiagnostic procedure for a failing metric: identify which one, find the cause, then fix in the order that pays.
| 1 | --- |
| 2 | name: web-vitals-triage |
| 3 | description: Triage procedure for a failing Core Web Vital. Start from field data, identify the metric and page template, find the specific cause, and fix in order of impact. Use when LCP, INP, or CLS is failing or has regressed. |
| 4 | --- |
| 5 | |
| 6 | # Web Vitals triage |
| 7 | |
| 8 | ## 0. Start from the field |
| 9 | 1. Which metric fails, on which page template, on which device class, at the 75th percentile? |
| 10 | 2. Is it a regression (compare to last week) or a long-standing failure? A regression means bisecting recent deploys. |
| 11 | 3. Only then open a lab tool, and reproduce with CPU and network throttling matching the failing segment. |
| 12 | |
| 13 | ## LCP failing |
| 14 | - [ ] Identify the LCP element in a trace. Is it the element you expected? |
| 15 | - [ ] When is it discovered? Late discovery means it is behind JavaScript, a client fetch, or a lazy attribute. |
| 16 | - [ ] Break down the time: TTFB, resource load delay, resource load time, render delay. Fix the largest slice first. |
| 17 | - [ ] Check size and format against rendered dimensions; check preconnect and font preload; check for render-blocking head resources. |
| 18 | |
| 19 | ## INP failing |
| 20 | - [ ] Which interaction is worst, and on which element? Field data should name it. |
| 21 | - [ ] Record a performance profile of that interaction; find the long task and its script. |
| 22 | - [ ] Split the handler: paint feedback first, yield, then do the work; move computation to a worker or the server. |
| 23 | - [ ] Audit third-party scripts and hydration cost on that route. |
| 24 | |
| 25 | ## CLS failing |
| 26 | - [ ] Find the shifting element in a trace, with its shift score and timing. |
| 27 | - [ ] Missing dimensions, late-loading embed, injected banner, or font swap? Each has its own fix. |
| 28 | - [ ] Verify no animation touches layout properties. |
| 29 | |
| 30 | ## Close the loop |
| 31 | - [ ] Ship the fix, then confirm in field data (allow for the reporting window) rather than in the lab. |
| 32 | - [ ] Add a budget or check so the same regression fails CI next time. |
performance-budget-review/rootPre-merge check that a change does not spend the page's performance budget.
| 1 | --- |
| 2 | name: performance-budget-review |
| 3 | description: Pre-merge performance review for frontend changes: bundle and dependency cost, image and font handling, third-party additions, and layout stability. Run on any change that adds JavaScript, images, or third-party scripts. |
| 4 | --- |
| 5 | |
| 6 | # Performance budget review |
| 7 | |
| 8 | ## JavaScript |
| 9 | - [ ] Bundle size delta is known and justified; no dependency added for a one-line utility. |
| 10 | - [ ] New code is route-split; below-the-fold and rarely used components load on demand. |
| 11 | - [ ] No new synchronous work in an interaction path; long tasks are chunked. |
| 12 | - [ ] No new client component doing what the server could render. |
| 13 | |
| 14 | ## Media and fonts |
| 15 | - [ ] Images have explicit dimensions, a modern format, and a correct responsive set. |
| 16 | - [ ] Above-the-fold images are eager and high priority; below-the-fold are lazy. |
| 17 | - [ ] No new font family or weight without measuring the cost; fonts preloaded and subset. |
| 18 | |
| 19 | ## Third parties |
| 20 | - [ ] Every added script has an owner, a purpose, and a measured main-thread cost. |
| 21 | - [ ] It loads after interaction or off the critical path, and failure to load degrades gracefully. |
| 22 | |
| 23 | ## Stability |
| 24 | - [ ] Anything that loads late reserves its space. |
| 25 | - [ ] Nothing is inserted above existing content after paint. |
| 26 | - [ ] Animations use transform and opacity only. |
| 27 | |
| 28 | ## Gate |
| 29 | - [ ] The CI performance budget (bundle size, Lighthouse assertions) still passes. |
| 30 | - [ ] For a risky change, field metrics are checked after release rather than assumed. |
Why this pattern
AI agents lazy-load the hero image, ship blocking scripts and unbounded client bundles, run expensive work inside event handlers, and render content with no reserved space.
Built for Frontend teams whose search ranking and conversion depend on real-user performance.
Keeps your assistant from:
- Lazy-loading or deferring the largest above-the-fold image, delaying LCP
- Blocking the main thread inside a click or input handler so INP exceeds 200ms
- Inserting banners, ads, or late-loading fonts that push content down
- Chasing a Lighthouse score while field data at the 75th percentile stays red
- License
- Apache-2.0
- Version
- 1.0.0
- Updated
- 2026-08-24