| name | web-performance |
| description | Web performance optimization and Core Web Vitals. Trigger: When measuring, auditing, or improving page load speed, interactivity, or visual stability. |
| license | Apache 2.0 |
| metadata | {"version":"1.0","type":"domain"} |
Web Performance
Optimize loading speed, interactivity, and visual stability using Core Web Vitals and resource budgets.
When to Use
- Improving Lighthouse performance scores
- Diagnosing slow LCP, high INP, or layout shifts (CLS)
- Reducing JavaScript or CSS bundle size
- Optimizing images, fonts, or third-party scripts
- Auditing page load performance across frameworks
Don't use for:
- React component re-render optimization (use
react)
- Next.js image/font components (use
next)
- Accessibility audits (use
a11y)
- SEO meta tags or structured data (use
web-seo)
Critical Patterns
โ
REQUIRED: Core Web Vitals Thresholds
LCP (Largest Contentful Paint) Good: โค 2.5s Poor: > 4.0s
INP (Interaction to Next Paint) Good: โค 200ms Poor: > 500ms
CLS (Cumulative Layout Shift) Good: โค 0.1 Poor: > 0.25
Measure with: Chrome DevTools Performance tab, Lighthouse, Web Vitals extension, or web-vitals npm package.
โ
REQUIRED: Resource Budgets
JavaScript (compressed): < 300 KB
CSS (compressed): < 100 KB
Total page weight: < 1.5 MB
Enforce with bundler size limits (Vite build.rollupOptions, Webpack performance.maxAssetSize).
โ
REQUIRED: Image Optimization
<img src="hero.webp" width="800" height="600"
alt="Hero image" loading="lazy" fetchpriority="high" />
<img src="photo.jpg" alt="Photo" />
Rules: Always specify width/height to prevent CLS. Use loading="lazy" for below-fold images. Use fetchpriority="high" on the LCP image only.
โ
REQUIRED: Font Loading โ Eliminate CLS
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preload" href="/fonts/inter.woff2" as="font"
type="font/woff2" crossorigin />
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap;
}
โ
REQUIRED: Script Loading
<script src="analytics.js" async></script>
<script src="app.js" defer></script>
<script src="app.js"></script>
โ NEVER: Render-Blocking Resources
<link rel="stylesheet" href="non-critical.css" />
<style></style>
<link rel="stylesheet" href="styles.css" media="print"
onload="this.media='all'" />
Decision Tree
LCP > 2.5s?
โ Is LCP element an image? โ Optimize image size, add fetchpriority="high", use WebP/AVIF
โ Is LCP element text? โ Check font loading (font-display: swap + preload)
โ Slow server response? โ Add SSR/SSG or caching layer
โ See references/core-web-vitals.md for full LCP diagnosis
INP > 200ms?
โ Long JS tasks on interaction? โ Split tasks with scheduler.yield() or setTimeout
โ Heavy input handler? โ Debounce/throttle, move work to Web Worker
โ See references/core-web-vitals.md for full INP diagnosis
CLS > 0.1?
โ Images without dimensions? โ Add explicit width/height attributes
โ Font swap causing jump? โ Preload font + font-display: swap or optional
โ Dynamic content inserted above fold? โ Reserve space with min-height
โ See references/core-web-vitals.md for full CLS diagnosis
Bundle too large (JS > 300KB)?
โ Identify with source-map-explorer or bundler analyzer
โ Lazy-load routes and heavy components (dynamic import)
โ Tree-shake unused exports, audit dependencies
โ See references/resource-optimization.md
Images unoptimized?
โ Convert to WebP/AVIF, add responsive srcset, enable lazy loading
โ See references/image-video.md
Font causing layout shift?
โ Add font-display: swap, preload critical fonts, use size-adjust
โ See references/font-loading.md
Framework-specific?
โ React: re-render optimization โ react skill
โ Next.js: next/image, next/font, PPR โ next skill
โ Astro: output mode, islands โ astro skill
Example
Diagnosing and fixing poor LCP on an e-commerce product page.
<img src="product.jpg" class="hero" alt="Running shoes" />
<img
src="product.webp"
width="800" height="600"
alt="Running shoes"
fetchpriority="high"
decoding="async"
/>
<img src="thumbnail.webp" width="200" height="150"
alt="Side view" loading="lazy" />
<link rel="preload" href="/fonts/inter-regular.woff2"
as="font" type="font/woff2" crossorigin />
import { onLCP, onINP, onCLS } from 'web-vitals';
onLCP(({ value }) => console.log('LCP:', value));
onINP(({ value }) => console.log('INP:', value));
onCLS(({ value }) => console.log('CLS:', value));
Result: LCP drops from 4.2s โ 1.8s after image format change + fetchpriority hint.
Edge Cases
Third-party scripts: Ad scripts, chat widgets, and analytics are common INP offenders. Load with async, defer initialization, or use a Facade pattern (lazy-load on first interaction).
Variable fonts: A single variable font file replaces multiple weight files, reducing requests. Use font-display: optional if layout shift on first load is unacceptable.
Responsive images: Use srcset + sizes for art direction. Without sizes, browser downloads the wrong size.
<img srcset="img-400.webp 400w, img-800.webp 800w"
sizes="(max-width: 600px) 400px, 800px"
src="img-800.webp" alt="..." />
INP vs FID: FID (First Input Delay) was replaced by INP in March 2024. INP measures all interactions throughout page lifetime, not just the first.
Resources
See references/README.md for navigation.