core-web-vitals-performance-contracts

Installation
SKILL.md

Core web vitals performance contracts

Core Web Vitals are three field metrics — LCP (loading), CLS (visual stability), INP (responsiveness, "good" at 2.5 s / 0.1 / 200 ms) — plus TTFB as an upstream diagnostic. This is a review lens for attributing a failing vital to a specific element, shift, or task and prescribing the narrowest fix; it is not a metrics tutorial (web.dev owns that).

Checklist (lead with the trap)

  1. Name the real LCP element before you optimize anything. LCP is whatever the largest in-viewport paint is on that load — often an <img>, but it can be a text block, a <video> poster, or a CSS background-image. Read it from a trace / PerformanceObserver / DevTools, not from "the hero looks big." A preload or fetchpriority="high" aimed at the wrong element wastes bandwidth and can demote the true LCP resource.
  2. The LCP resource must be discoverable early and never lazy. It belongs in the initial HTML src/srcset so the preload scanner finds it; late-discovered ones (CSS background-image, script-loaded) need <link rel="preload"> — and preloaded images still default to low priority, so pair with fetchpriority="high". loading="lazy" on the LCP element is always a bug.
  3. Unblock the critical path and TTFB before micro-tuning. Render-blocking CSS/synchronous <script> in <head> and a slow TTFB (aim under 800 ms) can make good LCP unreachable regardless of image work — check them and non-streaming SSR before blaming the asset.
  4. Attribute every layout shift to a source — don't just quote the CLS number. Each shift traces to something concrete — an unreserved media/iframe/embed/ad box, a web-font swap with mismatched fallback metrics (FOUT reflow), a consent banner or toast injected above existing content, or an animation of layout properties. Fix the source, not the aggregate.
  5. Reserve space and animate on the compositor. Give media and late/async content an explicit box (width/height or aspect-ratio), tame font swap with font-display: optional for body text or a metric-matched fallback via size-adjust, and animate transform/opacity — never layout properties.
  6. INP: break up long tasks and shrink handler work. INP (over 200 ms is poor; it replaced FID) has three phases — input delay, processing, presentation. A long task (>50 ms) overlapping the interaction inflates whichever phase it lands on. Do the minimum in click/keydown/pointerdown handlers, defer non-urgent work, and yield with scheduler.yield()/scheduler.postTask() so the browser can paint feedback first — feature-detect each method (globalThis.scheduler?.yield, ?.postTask): Safari does not ship the Scheduler API, so keep a setTimeout(0)/requestIdleCallback chunking fallback. Watch setInterval and third-party timers that steal the main thread.
  7. INP: budget hydration and render bursts. A large hydration pass or one big render/state update is a long task that inflates input delay exactly when users first interact (the main thread is busiest at startup). Attribute it with Long Animation Frames (blockingDuration) rather than guessing which script is at fault.

Quick probes

rg only surfaces candidates — attribution needs a trace: Lighthouse / PageSpeed Insights (lab + CrUX field), the DevTools Performance panel (LCP marker + Layout Shifts track), and the web-vitals library with Long Animation Frames in the field.

Installs
1
GitHub Stars
1
First Seen
3 days ago
core-web-vitals-performance-contracts — voidmatcha/frontend-niche-skills