agentleFS
Piyush8296Piyush8296/claude-workspace/.claude/skills/core-web-vitals/SKILL.md

core-web-vitals

Skill2 starsChanged 6 months agoMITView on GitHub

Deep-dive into Core Web Vitals — LCP, INP, CLS diagnosis and fix recipes for React/Next.js. Specific patterns for each metric with measurement tools, thresholds, and real-world fixes. Use when pages are slow, Lighthouse scores are low, or search ranking is affected by performance.

Files
SKILL.md

Core Web Vitals

The Three Metrics That Affect Search Ranking

MetricMeasuresGoodNeeds WorkPoor
LCP (Largest Contentful Paint)Loading speed≤ 2.5s≤ 4.0s> 4.0s
INP (Interaction to Next Paint)Responsiveness≤ 200ms≤ 500ms> 500ms
CLS (Cumulative Layout Shift)Visual stability≤ 0.1≤ 0.25> 0.25

LCP — Largest Contentful Paint

What it measures: How fast the biggest visible element loads (hero image, headline, video poster).

Diagnosis

# Run Lighthouse to identify LCP element
npx lighthouse http://localhost:3000 --output=json --quiet | \
  jq '.audits["largest-contentful-paint"]'

# Check which element is the LCP
npx lighthouse http://localhost:3000 --output=json --quiet | \
  jq '.audits["largest-contentful-paint-element"].details'

Fix Recipes

Hero image is the LCP element:

// BEFORE: lazy-loaded hero (kills LCP)
<Image src="/hero.jpg" alt="..." loading="lazy" />

// AFTER: preloaded, priority hero
<Image
  src="/hero.webp"              // Modern format
  alt="Product hero showing the dashboard"
  width={1200}
  height={600}
  priority                       // Preloads in <head>
  fetchPriority="high"           // Browser fetch priority
  sizes="100vw"                  // Full-width
  placeholder="blur"             // Instant placeholder
  blurDataURL={heroBlurDataUrl}  // Base64 blur
/>

Headline text is the LCP element:

/* Preload the heading font */
<link rel="preload" href="/fonts/heading.woff2" as="font" type="font/woff2" crossorigin />

@font-face {
  font-family: 'Heading';
  src: url('/fonts/heading.woff2') format('woff2');
  font-display: swap;    /* Show fallback immediately */
}

Server response is slow:

// Switch from SSR to SSG/ISR for static content
// app/products/[slug]/page.tsx
export const revalidate = 3600;  // ISR: regenerate every hour

// Or generate at build time
export async function generateStaticParams() {
  const products = await getAllProducts();
  return products.map((p) => ({ slug: p.slug }));
}

Render-blocking resources:

// Move non-critical JS to dynamic imports
const HeavyChart = dynamic(() => import('./HeavyChart'), {
  loading: () => <ChartSkeleton />,
  ssr: false,
});

// Defer third-party scripts
<Script src="https://analytics.example.com" strategy="lazyOnload" />

INP — Interaction to Next Paint

What it measures: Delay between user interaction (click, tap, keypress) and the next visual update.

Diagnosis

INP is hard to catch in lab tests — it requires real user interactions.

// Add web-vitals monitoring
import { onINP } from 'web-vitals';

onINP((metric) => {
  console.log('INP:', metric.value, 'ms');
  console.log('Element:', metric.attribution?.interactionTarget);
  console.log('Type:', metric.attribution?.interactionType);
  // Send to analytics
});

Fix Recipes

Heavy computation blocking the main thread:

// BEFORE: synchronous filter on 10K items
function handleSearch(query: string) {
  const results = allItems.filter(item =>
    item.name.toLowerCase().includes(query.toLowerCase())
  );
  setResults(results);  // Blocks paint until done
}

// AFTER: debounce + startTransition
import { startTransition } from 'react';

const debouncedQuery = useDebounce(query, 150);

useEffect(() => {
  startTransition(() => {
    const results = allItems.filter(item =>
      item.name.toLowerCase().includes(debouncedQuery.toLowerCase())
    );
    setResults(results);
  });
}, [debouncedQuery]);

Large re-renders on interaction:

// BEFORE: entire list re-renders on selection
function ItemList({ items, selectedId, onSelect }) {
  return items.map(item => (
    <Item
      key={item.id}
      item={item}
      isSelected={item.id === selectedId}
      onSelect={onSelect}
    />
  ));
}

// AFTER: memoize individual items
const MemoItem = memo(function Item({ item, isSelected, onSelect }) {
  return (
    <div
      role="option"
      aria-selected={isSelected}
      onClick={() => onSelect(item.id)}
    >
      {item.name}
    </div>
  );
});

Long event handlers:

// BEFORE: heavy work in click handler
async function handleSubmit() {
  validateForm();          // 50ms
  transformData();         // 100ms
  await submitToAPI();     // Network
  updateLocalState();      // 30ms
  showSuccessToast();      // 10ms
}  // Total: 190ms+ before any paint

// AFTER: yield to browser between steps
async function handleSubmit() {
  setIsSubmitting(true);   // Paint: show loading

  // Yield to let the loading state paint
  await new Promise(resolve => setTimeout(resolve, 0));

  const data = transformData();
  await submitToAPI(data);
  updateLocalState();
  showSuccessToast();
}

CLS — Cumulative Layout Shift

What it measures: How much the page layout shifts unexpectedly while loading.

Diagnosis

import { onCLS } from 'web-vitals';

onCLS((metric) => {
  console.log('CLS:', metric.value);
  metric.entries.forEach((entry) => {
    console.log('Shifted element:', entry.sources?.[0]?.node);
  });
});

Fix Recipes

Images without dimensions:

// BEFORE: no dimensions = layout shift when image loads
<img src="/photo.jpg" alt="..." />

// AFTER: explicit dimensions prevent shift
<Image src="/photo.jpg" alt="..." width={800} height={600} />

// For responsive images with unknown aspect ratio:
<div style={{ aspectRatio: '16/9', position: 'relative' }}>
  <Image src="/photo.jpg" alt="..." fill style={{ objectFit: 'cover' }} />
</div>

Dynamic content injected above viewport:

// BEFORE: banner appears and pushes everything down
{showBanner && <Banner />}  // CLS when banner loads

// AFTER: reserve space with min-height
<div style={{ minHeight: showBanner ? 'auto' : 0 }}>
  {showBanner && <Banner />}
</div>

// OR: use CSS containment
<div style={{ contentVisibility: 'auto', containIntrinsicSize: '0 60px' }}>
  {showBanner && <Banner />}
</div>

Web fonts causing reflow:

@font-face {
  font-family: 'Body';
  src: url('/fonts/body.woff2') format('woff2');
  font-display: swap;              /* Show fallback immediately */
  size-adjust: 105%;               /* Match fallback metrics */
  ascent-override: 95%;
  descent-override: 22%;
  line-gap-override: 0%;
}

Measurement Tools

ToolLab / FieldBest For
LighthouseLabQuick local audits
Chrome DevTools PerformanceLabTrace individual interactions
PageSpeed InsightsField + LabReal CrUX data + Lighthouse
web-vitals (npm)FieldProduction monitoring
Chrome UX Report (CrUX)Field28-day real user data

Production Monitoring Setup

// lib/web-vitals.ts
import { onLCP, onINP, onCLS } from 'web-vitals';

function sendToAnalytics(metric: { name: string; value: number; id: string }) {
  // Send to your analytics endpoint
  navigator.sendBeacon('/api/vitals', JSON.stringify({
    name: metric.name,
    value: Math.round(metric.value),
    id: metric.id,
    page: window.location.pathname,
    timestamp: Date.now(),
  }));
}

export function initWebVitals() {
  onLCP(sendToAnalytics);
  onINP(sendToAnalytics);
  onCLS(sendToAnalytics);
}

Checklist

  • LCP element identified and optimized (preload, priority, modern format)
  • Hero fonts preloaded with font-display: swap
  • No render-blocking third-party scripts
  • Heavy components code-split with dynamic()
  • Event handlers don't block main thread > 100ms
  • All images have explicit width/height or aspect-ratio
  • No content injected above fold without reserved space
  • Web fonts use size-adjust to match fallback metrics
  • web-vitals monitoring in production
  • Lighthouse score > 90 on all key pages

Integration with Other Skills

  • performance-optimization: Bundle splitting, lazy loading, virtualization
  • seo-fundamentals: Page speed directly affects search ranking
  • css-architecture: Font loading, responsive images
  • react-patterns: Component patterns that affect rendering performance

Download now (ZIP)

  • Other versions32 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 32

    1. midudev/preguntas-entrevista-react★ 7.8ksame name
    2. theopenco/llmgateway★ 1.7ksame name
    3. vmDeshpande/ai-agent-automation★ 177same name
    4. PracticalSwan/agent-skills★ 22same name
    5. Quality-Max/free-qa-skills★ 13same name
    6. RodrigoTomeES/getmcp★ 13same name
    7. 4gentic/obelus★ 8same name
    8. carlosuhart/seo-audit-skills★ 6same name
    9. joaoguirunas/team-os★ 6same name
    10. hsynozdamar-byte/claude-skills-edited★ 4same name

    22 more not listed

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • Used alongsideProjects with it also use web-quality-audit, javascript-seo and 5 more.

    Other files people use alongside this one.

    Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
    • RodrigoTomeESSkill

      web-quality-audit

      Comprehensive web quality audit covering performance, accessibility, SEO, and best practices. Use when asked to "audit my site", "review web quality", "run lighthouse audit", "check page quality", or "optimize my website".

      Also in 11 projects that use this

    • MYehia565Skill

      javascript-seo

      Diagnoses JS rendering, lazy-loading, and indexing gaps for client-rendered pages. Use when content appears in the browser but not in fetched HTML or Search.

      Also in 3 projects that use this

    • kpbraySkill

      best-practices

      Applies Power BI best practices, BPA rules, and enterprise standards. Use for quality validation, naming conventions, and performance optimization.

      Also in 7 projects that use this

    • nilssonrSkill

      seo

      Full SEO workflow for auditing pages and creating SEO-optimized content. Provides checklists with educational explanations aligned with Google Search Central documentation.

      Also in 8 projects that use this

    • xfstudioSkill

      architecture-decision-records

      Write and maintain Architecture Decision Records (ADRs) following best practices for technical decision documentation. Use when documenting significant technical decisions, reviewing past architectural choices, or establishing decision processes.

      Also in 3 projects that use this

    • GoogleSkill

      grill-me

      A relentless interview to sharpen a plan or design.

      Also in 5 projects that use this

  • In this repository18 other files it gives its agents.

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.