{"slug":"webperf-core-web-vitals","title":"webperf-core-web-vitals","summary":"Intelligent Core Web Vitals analysis with automated workflows and decision trees. Measures LCP, CLS, INP with guided debugging that automatically determines follow-up analysis based on results. Includes workflows for LCP deep dive (5 phases), CLS investigation (loading vs interac","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-10-19T21:17:31.913667Z","repo":{"url":"https://github.com/nucliweb/webperf-snippets","stars":1454,"forks":90,"license":"MIT","updatedAt":"2026-10-19T11:21:09Z"},"bodyHtml":"<hr>\n<h2>name: webperf-core-web-vitals\ndescription: Intelligent Core Web Vitals analysis with automated workflows and decision trees. Measures LCP, CLS, INP with guided debugging that automatically determines follow-up analysis based on results. Includes workflows for LCP deep dive (5 phases), CLS investigation (loading vs interaction), INP debugging (latency breakdown + attribution), and cross-skill integration with loading, interaction, and media skills. Use when the user asks about Core Web Vitals, LCP optimization, layout shifts, or interaction responsiveness. Compatible with Chrome DevTools MCP.\ncontext: fork\nlicense: MIT\nmetadata:\nauthor: Joan Leon | @nucliweb\nversion: 2.0.1\nmcp-server: chrome-devtools\ncategory: web-performance\nrepository: <a href=\"https://github.com/nucliweb/webperf-snippets\">https://github.com/nucliweb/webperf-snippets</a></h2>\n<h1>WebPerf: Core Web Vitals</h1>\n<p>JavaScript snippets for measuring web performance in Chrome DevTools.</p>\n<h2>Running a script</h2>\n<ol>\n<li>Run the script with <code>mcp__chrome-devtools__evaluate_script</code> and read the object it returns. The scripts have no console output, so the returned object is the result: <code>script</code>, <code>status</code> and the metric data (<code>value</code>, <code>rating</code>, <code>details</code>, <code>items</code>, <code>issues</code>), as described in <code>references/schema.md</code>.</li>\n<li>When <code>status</code> is <code>\"tracking\"</code>, the script keeps observing the page. After the interactions, call the function named in <code>getDataFn</code> with another <code>mcp__chrome-devtools__evaluate_script</code> call, for example <code>getINP()</code>, and read the object it returns.</li>\n<li>When <code>status</code> is <code>\"error\"</code> or <code>\"unsupported\"</code>, read <code>error</code>: the page may not have loaded yet, or the browser lacks the API.</li>\n</ol>\n<p>Without Chrome DevTools MCP, run the same snippet with the <a href=\"https://webperf-snippets.net/CLI\">webperf-snippets CLI</a>, which loads the page in headless Chromium and prints the same result objects: <code>npx webperf-snippets &lt;url&gt; --snippet &lt;Name&gt; --json</code>, where <code>&lt;Name&gt;</code> is the script name without <code>.js</code>. It needs Playwright and Chromium (<code>npm install --save-dev webperf-snippets playwright</code>, then <code>npx playwright install chromium</code>), and tracking snippets take <code>--interact-script &lt;file&gt;</code> to drive the interactions.</p>\n<h2>Scripts</h2>\n<ul>\n<li><code>scripts/CLS.js</code> — Cumulative layout shift (CLS)</li>\n<li><code>scripts/INP.js</code> — Interaction to next paint (INP)</li>\n<li><code>scripts/LCP-Image-Entropy.js</code> — LCP image entropy</li>\n<li><code>scripts/LCP-Subparts.js</code> — LCP subparts</li>\n<li><code>scripts/LCP-Trail.js</code> — LCP trail</li>\n<li><code>scripts/LCP-Video-Candidate.js</code> — LCP video candidate</li>\n<li><code>scripts/LCP.js</code> — Largest contentful paint (LCP)</li>\n</ul>\n<h2>Common Workflows</h2>\n<h3>Complete Core Web Vitals Audit</h3>\n<p>When the user asks for a comprehensive Core Web Vitals analysis or \"audit CWV\":</p>\n<ol>\n<li><strong>LCP.js</strong> - Measure Largest Contentful Paint</li>\n<li><strong>CLS.js</strong> - Measure Cumulative Layout Shift</li>\n<li><strong>INP.js</strong> - Measure Interaction to Next Paint</li>\n<li><strong>LCP-Subparts.js</strong> - Break down LCP timing phases</li>\n<li><strong>LCP-Trail.js</strong> - Track LCP candidate evolution</li>\n</ol>\n<h3>LCP Deep Dive</h3>\n<p>When LCP is slow or the user asks \"debug LCP\" or \"why is LCP slow\":</p>\n<ol>\n<li><strong>LCP.js</strong> - Establish baseline LCP value</li>\n<li><strong>LCP-Subparts.js</strong> - Break down into TTFB, resource load, render delay</li>\n<li><strong>LCP-Trail.js</strong> - Identify all LCP candidates and changes</li>\n<li><strong>LCP-Image-Entropy.js</strong> - Check if LCP image has visual complexity issues</li>\n<li><strong>LCP-Video-Candidate.js</strong> - Detect if LCP is a video (poster or video element)</li>\n</ol>\n<h3>CLS Investigation</h3>\n<p>When layout shifts are detected or the user asks \"debug CLS\" or \"layout shift issues\":</p>\n<ol>\n<li><strong>CLS.js</strong> - Measure overall CLS score</li>\n<li><strong>Layout-Shift-Loading-and-Interaction.js</strong> <em>(pending — available in webperf-interaction skill)</em></li>\n<li>Cross-reference with <strong>webperf-loading</strong> skill:\n<ul>\n<li>Find-Above-The-Fold-Lazy-Loaded-Images.js (lazy images causing shifts)</li>\n<li>Fonts-Preloaded-Loaded-and-used-above-the-fold.js (font swap causing shifts)</li>\n</ul>\n</li>\n</ol>\n<h3>INP Debugging</h3>\n<p>When interactions feel slow or the user asks \"debug INP\" or \"slow interactions\":</p>\n<ol>\n<li><strong>INP.js</strong> - Measure overall INP value; call <code>getINP()</code> after interactions, <code>getINPDetails()</code> for the 15 slowest interactions</li>\n<li><strong>Interactions.js</strong> <em>(pending — available in webperf-interaction skill)</em></li>\n<li><strong>Input-Latency-Breakdown.js</strong> <em>(pending — available in webperf-interaction skill)</em></li>\n<li><strong>Long-Animation-Frames.js</strong> <em>(pending — available in webperf-interaction skill)</em></li>\n<li><strong>Long-Animation-Frames-Script-Attribution.js</strong> <em>(pending — available in webperf-interaction skill)</em></li>\n</ol>\n<h3>Video as LCP Investigation</h3>\n<p>When LCP is a video element (detected by LCP-Video-Candidate.js):</p>\n<ol>\n<li><strong>LCP-Video-Candidate.js</strong> - Identify video as LCP candidate</li>\n<li><strong>Video-Element-Audit.js</strong> (from Media skill) - Audit video loading strategy</li>\n<li><strong>LCP-Subparts.js</strong> - Analyze video loading phases</li>\n<li>Cross-reference with <strong>webperf-loading</strong> skill:\n<ul>\n<li>Resource-Hints-Validation.js (check for video preload)</li>\n<li>Priority-Hints-Audit.js (check for fetchpriority on video)</li>\n</ul>\n</li>\n</ol>\n<h3>Image as LCP Investigation</h3>\n<p>When LCP is an image (most common case):</p>\n<ol>\n<li><strong>LCP.js</strong> - Measure LCP timing</li>\n<li><strong>LCP-Subparts.js</strong> - Break down timing phases</li>\n<li><strong>LCP-Image-Entropy.js</strong> - Analyze image complexity</li>\n<li>Cross-reference with <strong>webperf-media</strong> skill:\n<ul>\n<li>Image-Element-Audit.js (check format, dimensions, lazy loading)</li>\n</ul>\n</li>\n<li>Cross-reference with <strong>webperf-loading</strong> skill:\n<ul>\n<li>Find-Above-The-Fold-Lazy-Loaded-Images.js (check if incorrectly lazy)</li>\n<li>Priority-Hints-Audit.js (check for fetchpriority=\"high\")</li>\n<li>Resource-Hints-Validation.js (check for preload)</li>\n</ul>\n</li>\n</ol>\n<h2>Decision Tree</h2>\n<p>Use this decision tree to automatically run follow-up snippets based on results:</p>\n<h3>After LCP.js</h3>\n<ul>\n<li><strong>If LCP &gt; 2.5s</strong> → Run <strong>LCP-Subparts.js</strong> to diagnose which phase is slow</li>\n<li><strong>If LCP &gt; 4.0s (poor)</strong> → Run full LCP deep dive workflow (5 snippets)</li>\n<li><strong>If LCP candidate is an image</strong> → Run <strong>LCP-Image-Entropy.js</strong> and <strong>webperf-media:Image-Element-Audit.js</strong></li>\n<li><strong>If LCP candidate is a video</strong> → Run <strong>LCP-Video-Candidate.js</strong> and <strong>webperf-media:Video-Element-Audit.js</strong></li>\n<li><strong>Always run</strong> → <strong>LCP-Trail.js</strong> to understand candidate evolution</li>\n</ul>\n<h3>After LCP-Subparts.js</h3>\n<ul>\n<li><strong>If TTFB phase &gt; 600ms</strong> → Switch to <strong>webperf-loading</strong> skill and run TTFB-Sub-Parts.js</li>\n<li><strong>If Resource Load Time &gt; 1500ms</strong> → Run:\n<ol>\n<li><strong>webperf-loading:Resource-Hints-Validation.js</strong> (check for preload/preconnect)</li>\n<li><strong>webperf-loading:Priority-Hints-Audit.js</strong> (check fetchpriority)</li>\n<li><strong>webperf-loading:Find-render-blocking-resources.js</strong> (competing resources)</li>\n</ol>\n</li>\n<li><strong>If Render Delay &gt; 200ms</strong> → Run:\n<ol>\n<li><strong>webperf-loading:Find-render-blocking-resources.js</strong> (blocking CSS/JS)</li>\n<li><strong>webperf-loading:Script-Loading.js</strong> (parser-blocking scripts)</li>\n<li><strong>webperf-interaction:Long-Animation-Frames.js</strong> (main thread blocking)</li>\n</ol>\n</li>\n</ul>\n<h3>After LCP-Trail.js</h3>\n<ul>\n<li><strong>If many LCP candidate changes (&gt;3)</strong> → This causes visual instability, investigate:\n<ol>\n<li><strong>webperf-loading:Find-Above-The-Fold-Lazy-Loaded-Images.js</strong> (late-loading images)</li>\n<li><strong>webperf-loading:Fonts-Preloaded-Loaded-and-used-above-the-fold.js</strong> (font swaps)</li>\n<li><strong>CLS.js</strong> (layout shifts contributing to LCP changes)</li>\n</ol>\n</li>\n<li><strong>If final LCP candidate appears late</strong> → Run <strong>webperf-loading:Resource-Hints-Validation.js</strong></li>\n<li><strong>If early candidate was replaced</strong> → Understand why initial content was pushed down (likely CLS issue)</li>\n</ul>\n<h3>After LCP-Image-Entropy.js</h3>\n<ul>\n<li><strong>If entropy is very high</strong> → Image is visually complex, recommend:\n<ul>\n<li>Modern formats (WebP, AVIF)</li>\n<li>Appropriate compression</li>\n<li>Potentially a placeholder strategy</li>\n</ul>\n</li>\n<li><strong>If entropy is low</strong> → Image may be over-optimized or placeholder-like</li>\n<li><strong>If large file size detected</strong> → Run <strong>webperf-media:Image-Element-Audit.js</strong> for format/sizing analysis</li>\n</ul>\n<h3>After LCP-Video-Candidate.js</h3>\n<ul>\n<li><strong>If video is LCP</strong> → Run:\n<ol>\n<li><strong>webperf-media:Video-Element-Audit.js</strong> (check poster, preload, formats)</li>\n<li><strong>webperf-loading:Priority-Hints-Audit.js</strong> (check fetchpriority on poster)</li>\n<li><strong>LCP-Subparts.js</strong> (analyze video loading phases)</li>\n</ol>\n</li>\n<li><strong>If poster image is LCP</strong> → Treat as image LCP (run image workflows)</li>\n</ul>\n<h3>After CLS.js</h3>\n<ul>\n<li><strong>If CLS &gt; 0.1</strong> → Run <strong>webperf-interaction:Layout-Shift-Loading-and-Interaction.js</strong> <em>(pending — available in webperf-interaction skill)</em></li>\n<li><strong>If CLS &gt; 0.25 (poor)</strong> → Run comprehensive shift investigation:\n<ol>\n<li><strong>webperf-loading:Find-Above-The-Fold-Lazy-Loaded-Images.js</strong> (images without dimensions)</li>\n<li><strong>webperf-loading:Fonts-Preloaded-Loaded-and-used-above-the-fold.js</strong> (font loading strategy)</li>\n<li><strong>webperf-loading:Critical-CSS-Detection.js</strong> (late-loading styles)</li>\n<li><strong>webperf-media:Image-Element-Audit.js</strong> (missing width/height)</li>\n</ol>\n</li>\n<li><strong>If CLS = 0</strong> → Confirm with multiple page loads (might be timing-dependent)</li>\n</ul>\n<h3>After INP.js</h3>\n<ul>\n<li><strong>If INP &gt; 200ms</strong> → Run <strong>webperf-interaction:Interactions.js</strong> to identify slow interactions</li>\n<li><strong>If INP &gt; 500ms (poor)</strong> → Run full INP debugging workflow:\n<ol>\n<li><strong>webperf-interaction:Interactions.js</strong> (list all interactions)</li>\n<li><strong>webperf-interaction:Input-Latency-Breakdown.js</strong> (phase breakdown)</li>\n<li><strong>webperf-interaction:Long-Animation-Frames.js</strong> (blocking frames)</li>\n<li><strong>webperf-interaction:Long-Animation-Frames-Script-Attribution.js</strong> (culprit scripts)</li>\n</ol>\n</li>\n<li><strong>If specific interaction type is slow (e.g., keyboard)</strong> → Focus analysis on that interaction type</li>\n<li><strong>After INP data is collected</strong> → call <code>getINPDetails()</code> for the 15 slowest interactions, slowest first (useful for identifying patterns across multiple slow interactions)</li>\n</ul>\n<h3>Error Recovery</h3>\n<ul>\n<li><strong>If any script returns <code>status: \"error\"</code></strong> → Check if the page has finished loading:\n<ul>\n<li>If early in load: wait and re-run the script</li>\n<li>If page is an SPA: user may need to navigate to the target route first</li>\n</ul>\n</li>\n<li><strong>If LCP.js / LCP-Subparts.js returns <code>status: \"error\"</code></strong> → Tell the user: \"LCP data is not available yet. Please ensure the page has fully loaded, then run the analysis again.\"</li>\n<li><strong>If INP.js <code>getINP()</code> returns <code>status: \"error\"</code></strong> → The <code>getDataFn: \"getINP\"</code> field signals the agent can retry after user interaction. Prompt the user to click, type, or scroll, then call <code>getINP()</code> again.</li>\n</ul>\n<h3>Cross-Skill Triggers</h3>\n<blockquote>\n<p><strong>Context fork note:</strong> This skill runs with <code>context: fork</code>. Cross-skill triggers below are <strong>recommendations to report back to the parent agent</strong>, not direct calls this subagent can execute. When a cross-skill trigger fires, tell the user which skill and script to run next. Scripts marked <em>(pending)</em> are not yet available — skip them and note the limitation.</p>\n</blockquote>\n<p>These triggers recommend using snippets from other skills:</p>\n<h4>From LCP to Loading Skill</h4>\n<ul>\n<li><p><strong>If LCP &gt; 2.5s and TTFB phase is dominant</strong> → Use <strong>webperf-loading</strong> skill:</p>\n<ul>\n<li>TTFB.js, TTFB-Sub-Parts.js, Service-Worker-Analysis.js</li>\n</ul>\n</li>\n<li><p><strong>If LCP image is lazy-loaded</strong> → Use <strong>webperf-loading</strong> skill:</p>\n<ul>\n<li>Find-Above-The-Fold-Lazy-Loaded-Images.js</li>\n</ul>\n</li>\n<li><p><strong>If LCP has no fetchpriority</strong> → Use <strong>webperf-loading</strong> skill:</p>\n<ul>\n<li>Priority-Hints-Audit.js</li>\n</ul>\n</li>\n</ul>\n<h4>From CLS to Loading Skill</h4>\n<ul>\n<li><p><strong>If CLS caused by fonts</strong> → Use <strong>webperf-loading</strong> skill:</p>\n<ul>\n<li>Fonts-Preloaded-Loaded-and-used-above-the-fold.js</li>\n<li>Resource-Hints-Validation.js (for font preload)</li>\n</ul>\n</li>\n<li><p><strong>If CLS caused by images</strong> → Use <strong>webperf-media</strong> skill:</p>\n<ul>\n<li>Image-Element-Audit.js (check for width/height attributes)</li>\n</ul>\n</li>\n</ul>\n<h4>From INP to Interaction Skill</h4>\n<ul>\n<li><strong>If INP &gt; 200ms</strong> → Use <strong>webperf-interaction</strong> skill for full debugging:\n<ul>\n<li>Interactions.js, Input-Latency-Breakdown.js</li>\n<li>Long-Animation-Frames.js, Long-Animation-Frames-Script-Attribution.js</li>\n<li>LongTask.js (if pre-interaction blocking suspected)</li>\n</ul>\n</li>\n</ul>\n<h4>From LCP/INP to Interaction Skill</h4>\n<ul>\n<li><strong>If render delay or interaction delay is high</strong> → Use <strong>webperf-interaction</strong> skill:\n<ul>\n<li>Long-Animation-Frames.js (main thread blocking)</li>\n<li>LongTask.js (long tasks delaying rendering)</li>\n</ul>\n</li>\n</ul>\n<h3>Multi-Metric Correlation</h3>\n<p>When multiple CWV metrics are poor, prioritize investigation:</p>\n<ul>\n<li><p><strong>If LCP &gt; 2.5s AND CLS &gt; 0.1</strong> → Likely shared root cause:</p>\n<ol>\n<li>Check for late-loading content pushing LCP element</li>\n<li>Run LCP-Trail.js to see LCP candidate changes</li>\n<li>Run Layout-Shift-Loading-and-Interaction.js to correlate timing</li>\n</ol>\n</li>\n<li><p><strong>If LCP &gt; 2.5s AND INP &gt; 200ms</strong> → Main thread congestion:</p>\n<ol>\n<li>Run Long-Animation-Frames.js</li>\n<li>Run webperf-loading:Script-Loading.js</li>\n<li>Run webperf-loading:JS-Execution-Time-Breakdown.js</li>\n</ol>\n</li>\n<li><p><strong>If CLS &gt; 0.1 AND INP &gt; 200ms</strong> → Layout thrashing or interaction-triggered shifts:</p>\n<ol>\n<li>Run Layout-Shift-Loading-and-Interaction.js</li>\n<li>Run Interactions.js</li>\n<li>Check if shifts occur during/after interactions</li>\n</ol>\n</li>\n</ul>\n<h2>References</h2>\n<ul>\n<li><code>references/snippets.md</code> — Descriptions and thresholds for each script</li>\n<li><code>references/schema.md</code> — Return value schema for interpreting script output</li>\n</ul>\n","files":[{"path":"references/schema.md","sizeBytes":49186,"isText":true},{"path":"references/snippets.md","sizeBytes":2861,"isText":true},{"path":"scripts/CLS.js","sizeBytes":1267,"isText":true},{"path":"scripts/INP.js","sizeBytes":5487,"isText":true},{"path":"scripts/LCP-Image-Entropy.js","sizeBytes":4336,"isText":true},{"path":"scripts/LCP.js","sizeBytes":2225,"isText":true},{"path":"scripts/LCP-Subparts.js","sizeBytes":4662,"isText":true},{"path":"scripts/LCP-Trail.js","sizeBytes":3125,"isText":true},{"path":"scripts/LCP-Video-Candidate.js","sizeBytes":4179,"isText":true},{"path":"SKILL.md","sizeBytes":12630,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-10-19T21:18:27.069573Z","sha256":"3A82108E6A7578541812CD3C0DFFCAF77AC2B64F4FC2A8A4AE713FF054B5FDD6","sizeBytes":32459},"review":null,"source":{"repositoryUrl":"https://github.com/nucliweb/webperf-snippets","path":"skills/webperf-core-web-vitals","license":"MIT","commit":"d2f9739cda7363daab6ade96ad2b2198e9b44e8e","subtreeSha":"1882D1238449FF575F70673B9460CFE23EF917F454B01FD3AB693B73E9B1EE66","lastSyncedAt":"2026-10-19T21:17:31.667373Z"},"reviewedAt":"2026-10-19T21:20:20.566419Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/nucliweb/webperf-snippets/tree/main/skills/webperf-core-web-vitals"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install nucliweb-webperf-snippets@llmmart"},{"target":"git","command":"git clone https://github.com/nucliweb/webperf-snippets.git"}]}