$npx -y skills add jgamaraalv/ts-dev-kit --skill core-web-vitalsCore Web Vitals reference for measuring, diagnosing, and improving LCP, INP, and CLS. Use when: (1) auditing page performance against Google's thresholds, (2) implementing the web-vitals JS library for field monitoring, (3) diagnosing slow LCP, high INP, or layout shifts, (4) cho
| 1 | # Core Web Vitals |
| 2 | |
| 3 | The three stable Core Web Vitals, each measured at the **75th percentile** of |
| 4 | real page loads (segmented by mobile and desktop): |
| 5 | |
| 6 | <quick_reference> |
| 7 | |
| 8 | | Metric | Measures | Good | Needs Improvement | Poor | |
| 9 | |--------|----------|------|-------------------|------| |
| 10 | | **LCP** — Largest Contentful Paint | Loading | ≤ 2.5 s | 2.5–4.0 s | > 4.0 s | |
| 11 | | **INP** — Interaction to Next Paint | Interactivity | ≤ 200 ms | 200–500 ms | > 500 ms | |
| 12 | | **CLS** — Cumulative Layout Shift | Visual stability | ≤ 0.1 | 0.1–0.25 | > 0.25 | |
| 13 | |
| 14 | A page **passes** Core Web Vitals only if all three metrics meet "Good" at the 75th percentile. |
| 15 | |
| 16 | ## Supporting metrics (non-Core but diagnostic) |
| 17 | |
| 18 | - **FCP** (First Contentful Paint) — diagnoses render-blocking resources upstream of LCP |
| 19 | - **TTFB** (Time to First Byte) — server response time; directly affects LCP |
| 20 | - **TBT** (Total Blocking Time) — lab proxy for INP; identifies long tasks |
| 21 | |
| 22 | ## Tools matrix |
| 23 | |
| 24 | | Tool | Type | LCP | INP | CLS | Notes | |
| 25 | |------|------|-----|-----|-----|-------| |
| 26 | | Chrome User Experience Report (CrUX) | Field | ✓ | ✓ | ✓ | 28-day rolling window of real users | |
| 27 | | PageSpeed Insights | Field + Lab | ✓ | ✓ | ✓ | Field = CrUX data; Lab = Lighthouse | |
| 28 | | Search Console CWV report | Field | ✓ | ✓ | ✓ | Groups URLs by template | |
| 29 | | Chrome DevTools Performance panel | Field + Lab | ✓ | ✓ | ✓ | Local profiling, interaction tracing | |
| 30 | | Lighthouse | Lab | ✓ | TBT* | ✓ | CI integration; INP → use TBT as proxy | |
| 31 | |
| 32 | *Lighthouse uses **Total Blocking Time (TBT)** as a lab proxy for INP. TBT |
| 33 | correlates with INP but does not replace field measurement. |
| 34 | |
| 35 | ## Metric lifecycle |
| 36 | |
| 37 | Metrics progress through: **Experimental → Pending → Stable**. |
| 38 | All three current Core Web Vitals (LCP, CLS, INP) are **Stable**. |
| 39 | INP replaced FID (First Input Delay) in March 2024. |
| 40 | Changes to stable metrics follow an annual cadence with advance notice. |
| 41 | |
| 42 | </quick_reference> |
| 43 | |
| 44 | <examples> |
| 45 | |
| 46 | ## Quick setup: measure all three in the field |
| 47 | |
| 48 | ```bash |
| 49 | npm install web-vitals |
| 50 | ``` |
| 51 | |
| 52 | ```js |
| 53 | import { onCLS, onINP, onLCP } from 'web-vitals'; |
| 54 | |
| 55 | function sendToAnalytics(metric) { |
| 56 | const body = JSON.stringify(metric); |
| 57 | (navigator.sendBeacon && navigator.sendBeacon('/analytics', body)) || |
| 58 | fetch('/analytics', { body, method: 'POST', keepalive: true }); |
| 59 | } |
| 60 | |
| 61 | onCLS(sendToAnalytics); |
| 62 | onINP(sendToAnalytics); |
| 63 | onLCP(sendToAnalytics); |
| 64 | ``` |
| 65 | |
| 66 | Each callback receives `{ name, value, rating, delta, id, navigationType }`. |
| 67 | `rating` is `"good"`, `"needs-improvement"`, or `"poor"`. |
| 68 | |
| 69 | > The `web-vitals` library handles bfcache restores, prerendered pages, iframe |
| 70 | > aggregation, and other edge cases that raw PerformanceObserver does not. |
| 71 | |
| 72 | </examples> |
| 73 | |
| 74 | <visual_report> |
| 75 | |
| 76 | ## Generate a visual report |
| 77 | |
| 78 | When the user provides metric values or a Lighthouse JSON file, generate an interactive HTML report and open it in the browser: |
| 79 | |
| 80 | To locate the script, find `scripts/visualize.py` relative to this skill's directory. The path depends on how ts-dev-kit is installed: |
| 81 | - **Project scope**: `skills/core-web-vitals/scripts/visualize.py` or `.claude/skills/core-web-vitals/scripts/visualize.py` |
| 82 | - **Personal scope**: `~/.claude/skills/core-web-vitals/scripts/visualize.py` |
| 83 | - **Plugin scope**: resolve via `node_modules/@jgamaraalv/ts-dev-kit/skills/core-web-vitals/scripts/visualize.py` |
| 84 | |
| 85 | Use `find` or `ls` to discover the actual path, then run: |
| 86 | |
| 87 | ```bash |
| 88 | # From manual values (replace SCRIPT_PATH with the discovered path) |
| 89 | python3 SCRIPT_PATH/visualize.py \ |
| 90 | --lcp 2.1 --inp 180 --cls 0.05 \ |
| 91 | --url https://example.com |
| 92 | |
| 93 | # From a Lighthouse JSON output |
| 94 | python3 SCRIPT_PATH/visualize.py \ |
| 95 | --lighthouse lighthouse-report.json |
| 96 | |
| 97 | # Custom output path, no auto-open |
| 98 | python3 SCRIPT_PATH/visualize.py \ |
| 99 | --lcp 3.8 --inp 420 --cls 0.12 \ |
| 100 | --output cwv-report.html --no-open |
| 101 | ``` |
| 102 | |
| 103 | The script (`scripts/visualize.py`) requires only Python 3 stdlib — no packages to install. |
| 104 | It outputs a self-contained HTML file with color-coded metric cards, a visual progress bar showing where each value falls on the Good/Needs Improvement/Poor scale, and an overall PASS/FAIL/NEEDS IMPROVEMENT verdict. |
| 105 | |
| 106 | </visual_report> |
| 107 | |
| 108 | <references> |
| 109 | |
| 110 | ## When to read reference files |
| 111 | |
| 112 | | Reference | Read when… | |
| 113 | |-----------|-----------| |
| 114 | | [references/lcp.md](references/lcp.md) | LCP > 2.5 s, diagnosing slow image/text load, preload/CDN questions | |
| 115 | | [references/inp.md](references/inp.md) | INP > 200 ms, s |