{"id":"6cb58830-ade5-4177-9c6b-05ff23197ca8","shortId":"vFH97N","kind":"skill","title":"performance-and-web-vitals","tagline":"Audit UI performance with Lighthouse and fix Core Web Vitals — LCP, CLS, INP. Fast UI is good UX. Use when optimising page load, fixing layout shift, reducing input delay, improving Lighthouse scores, or reviewing images, fonts, and render-blocking resources.","description":"# Performance and Web Vitals\n\n## Run a Lighthouse Audit\n\n```bash\n# CLI audit — outputs JSON and HTML report\nnpx lighthouse https://example.com --output html --output-path ./lighthouse-report.html\n\n# Headless, useful in CI\nnpx lighthouse https://example.com --chrome-flags=\"--headless\" --output json --output-path ./report.json\n\n# Audit specific categories only\nnpx lighthouse https://example.com --only-categories=performance,accessibility,seo\n```\n\nOr open Chrome DevTools → Lighthouse tab → Analyse page load.\n\n**Target scores:**\n| Category | Target |\n|---|---|\n| Performance | ≥ 90 |\n| Accessibility | 100 |\n| Best Practices | ≥ 95 |\n| SEO | ≥ 95 |\n\n---\n\n## Core Web Vitals\n\n### LCP — Largest Contentful Paint\n*How fast does the main content appear?*\n\n**Target: ≤ 2.5s**\n\nLCP measures when the largest visible element (hero image, heading, video poster) renders. It is the user's perception of \"did the page load?\"\n\n**Common causes and fixes:**\n\n| Cause | Fix |\n|---|---|\n| Unoptimised hero image | Use WebP/AVIF, correct size, `fetchpriority=\"high\"` |\n| Image not preloaded | `<link rel=\"preload\" as=\"image\" href=\"hero.webp\">` |\n| Render-blocking CSS/JS | Defer non-critical JS, inline critical CSS |\n| Slow server response | CDN, caching headers, edge delivery |\n| Web font blocking render | `font-display: swap` or `optional` |\n\n```html\n<!-- Preload LCP image -->\n<link rel=\"preload\" as=\"image\" href=\"hero.webp\" fetchpriority=\"high\">\n\n<!-- LCP image: no lazy loading -->\n<img src=\"hero.webp\" alt=\"...\" fetchpriority=\"high\" width=\"1200\" height=\"600\">\n```\n\nNever use `loading=\"lazy\"` on the LCP image — it delays the most important render.\n\n---\n\n### CLS — Cumulative Layout Shift\n*Does content jump around while loading?*\n\n**Target: ≤ 0.1**\n\nCLS measures unexpected layout shifts — content moving after it has rendered. Caused by images without dimensions, late-loading ads, fonts swapping, or dynamic content injected above existing content.\n\n**Common causes and fixes:**\n\n| Cause | Fix |\n|---|---|\n| Images without width/height | Always set `width` and `height` on `<img>` |\n| Web font swap | Use `font-display: optional` or preload fonts |\n| Dynamic content above fold | Reserve space with `min-height` on containers |\n| Late-loading ads or embeds | Reserve fixed dimensions for ad slots |\n| Animations that shift layout | Animate `transform` only, never `top/left/width/height` |\n\n```html\n<!-- Always include dimensions -->\n<img src=\"product.jpg\" width=\"400\" height=\"300\" alt=\"...\">\n```\n\n```css\n/* Reserve space for dynamic content */\n.ad-slot { min-height: 250px; }\n\n/* Animate transform, not layout properties */\n.slide-in { transform: translateY(0); transition: transform 300ms; }\n```\n\n---\n\n### INP — Interaction to Next Paint\n*How quickly does the page respond to user input?*\n\n**Target: ≤ 200ms**\n\nINP measures the delay between a user interaction (click, tap, keyboard) and the next visual update. High INP makes the UI feel sluggish or frozen.\n\n**Common causes and fixes:**\n\n| Cause | Fix |\n|---|---|\n| Heavy JS on main thread | Break into smaller tasks, use `requestIdleCallback` |\n| Large event handlers | Debounce/throttle scroll and resize handlers |\n| Synchronous DOM updates | Batch DOM writes with `requestAnimationFrame` |\n| Third-party scripts blocking | Load third-party scripts with `async` or `defer` |\n| React re-renders | Memoize with `useMemo`, `useCallback`, `React.memo` |\n\n---\n\n## Images\n\nImages are the single biggest performance lever on most pages.\n\n```html\n<!-- Modern formats with fallback -->\n<picture>\n  <source srcset=\"image.avif\" type=\"image/avif\">\n  <source srcset=\"image.webp\" type=\"image/webp\">\n  <img src=\"image.jpg\" width=\"800\" height=\"600\" alt=\"...\" loading=\"lazy\">\n</picture>\n\n<!-- Responsive images -->\n<img\n  srcset=\"image-400.webp 400w, image-800.webp 800w, image-1200.webp 1200w\"\n  sizes=\"(max-width: 600px) 100vw, 50vw\"\n  src=\"image-800.webp\"\n  alt=\"...\"\n  width=\"800\"\n  height=\"600\"\n  loading=\"lazy\"\n>\n```\n\n**Rules:**\n- Always set `width` and `height` — prevents CLS\n- Use `loading=\"lazy\"` below the fold, never on LCP image\n- Serve WebP or AVIF — typically 30–50% smaller than JPEG\n- Size images to their display size — do not serve 2000px image for a 400px slot\n- Use a CDN with automatic format conversion where possible\n\n---\n\n## Fonts\n\nWeb fonts block rendering if not handled correctly.\n\n```html\n<!-- Preconnect to font origin -->\n<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n\n<!-- Preload critical font file -->\n<link rel=\"preload\" href=\"/fonts/brand.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>\n```\n\n```css\n@font-face {\n  font-family: 'Brand';\n  src: url('/fonts/brand.woff2') format('woff2');\n  font-display: swap;     /* show fallback immediately, swap when loaded */\n  /* font-display: optional; — never swap, use fallback if not cached */\n}\n```\n\n- `font-display: swap` — good for headings, acceptable CLS\n- `font-display: optional` — zero CLS, font only used if cached (best for body text)\n- Subset fonts to the characters actually used — reduces file size by 60–80%\n\n---\n\n## JavaScript\n\n```html\n<!-- Defer non-critical scripts -->\n<script src=\"analytics.js\" defer></script>\n<script src=\"chat-widget.js\" async></script>\n\n<!-- Module scripts are deferred by default -->\n<script type=\"module\" src=\"app.js\"></script>\n```\n\n- `defer`: executes after HTML parsed, in order — use for most scripts\n- `async`: executes as soon as downloaded, out of order — use for independent scripts (analytics)\n- Never block the main thread with synchronous `<script>` in `<head>`\n\n---\n\n## Lighthouse CI (automated audits)\n\nRun Lighthouse in CI to catch regressions before deployment.\n\n```bash\n# Install\nnpm install -g @lhci/cli\n\n# Run\nlhci autorun --upload.target=temporary-public-storage\n```\n\n```yaml\n# .lighthouserc.json\n{\n  \"ci\": {\n    \"assert\": {\n      \"assertions\": {\n        \"categories:performance\": [\"warn\", { \"minScore\": 0.9 }],\n        \"categories:accessibility\": [\"error\", { \"minScore\": 1.0 }],\n        \"categories:seo\": [\"warn\", { \"minScore\": 0.95 }]\n      }\n    }\n  }\n}\n```\n\n---\n\n## Review Checklist\n\n- [ ] Lighthouse performance score ≥ 90\n- [ ] Lighthouse accessibility score = 100\n- [ ] LCP ≤ 2.5s — LCP image preloaded, no `loading=\"lazy\"` on it\n- [ ] CLS ≤ 0.1 — all images have `width` and `height`, no layout-shifting animations\n- [ ] INP ≤ 200ms — no heavy synchronous JS on main thread\n- [ ] Images served as WebP or AVIF with correct dimensions\n- [ ] `loading=\"lazy\"` on all below-fold images\n- [ ] Web fonts use `font-display: swap` or `optional`\n- [ ] Non-critical JS loaded with `defer` or `async`\n- [ ] Lighthouse CI configured to catch regressions in deployment pipeline","tags":["performance","and","web","vitals","dembrandt","skills","accessibility","agent-skills","claude-code-skills","claude-skills","cursor-skills","design-system"],"capabilities":["skill","source-dembrandt","skill-performance-and-web-vitals","topic-accessibility","topic-agent-skills","topic-claude-code-skills","topic-claude-skills","topic-cursor-skills","topic-design-system","topic-design-tokens","topic-enterprise-ux","topic-gestalt","topic-skills-sh","topic-typography","topic-ui-design"],"categories":["dembrandt-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dembrandt/dembrandt-skills/performance-and-web-vitals","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add dembrandt/dembrandt-skills","source_repo":"https://github.com/dembrandt/dembrandt-skills","install_from":"skills.sh"}},"qualityScore":"0.454","qualityRationale":"deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 9 github stars · SKILL.md body (6,728 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:08:28.005Z","embedding":null,"createdAt":"2026-05-18T13:14:03.195Z","updatedAt":"2026-05-18T19:08:28.005Z","lastSeenAt":"2026-05-18T19:08:28.005Z","tsv":"'/fonts/brand.woff2':537 '/lighthouse-report.html':71 '/report.json':88 '0':352 '0.1':239 '100':118 '2.5':139 '2000px':502 '200ms':371 '250px':341 '30':488 '300ms':355 '400px':506 '50':489 '60':596 '80':597 '90':116 '95':121,123 'accept':568 'access':100,117 'actual':590 'ad':259,310,317,336 'ad-slot':335 'alway':278,466 'analys':108 'analyt':624 'anim':319,323,342 'appear':137 'around':235 'async':441,611 'audit':6,54,57,89 'automat':512 'avif':486 'bash':55 'batch':425 'best':119,581 'biggest':458 'block':45,185,205,434,520,626 'bodi':583 'brand':534 'break':408 'cach':199,560,580 'categori':91,98,113 'caus':166,169,251,270,273,398,401 'cdn':198,510 'charact':589 'chrome':80,104 'chrome-flag':79 'ci':75 'cli':56 'click':380 'cls':17,228,240,472,569,575 'common':165,269,397 'contain':306 'content':129,136,233,245,264,268,296,334 'convers':514 'core':13,124 'correct':176,525 'critic':190,193 'css':194,329,527 'css/js':186 'cumul':229 'debounce/throttle':417 'defer':187,443,600 'delay':34,223,375 'deliveri':202 'devtool':105 'dimens':255,315 'display':209,290,497,542,552,563,572 'dom':423,426 'download':616 'dynam':263,295,333 'edg':201 'element':147 'emb':312 'event':415 'example.com':65,78,95 'execut':601,612 'exist':267 'face':530 'fallback':545,557 'famili':533 'fast':19,132 'feel':393 'fetchprior':178 'file':593 'fix':12,29,168,170,272,274,314,400,402 'flag':81 'fold':298,478 'font':41,204,208,260,285,289,294,517,519,529,532,541,551,562,571,576,586 'font-display':207,288,540,550,561,570 'font-fac':528 'font-famili':531 'format':513,538 'frozen':396 'good':22,565 'handl':524 'handler':416,421 'head':150,567 'header':200 'headless':72,82 'heavi':403 'height':282,304,340,470 'hero':148,172 'high':179,388 'html':61,67,213,328,464,526,599,603 'imag':40,149,173,180,221,253,275,453,454,482,494,503 'immedi':546 'import':226 'improv':35 'independ':622 'inject':265 'inlin':192 'inp':18,356,372,389 'input':33,369 'interact':357,379 'javascript':598 'jpeg':492 'js':191,404 'json':59,84 'jump':234 'keyboard':382 'larg':414 'largest':128,145 'late':257,308 'late-load':256,307 'layout':30,230,243,322,345 'lazi':217,475 'lcp':16,127,141,220,481 'lever':460 'lighthous':10,36,53,64,77,94,106 'load':28,110,164,216,237,258,309,435,474,549 'main':135,406,628 'make':390 'measur':142,241,373 'memoiz':448 'min':303,339 'min-height':302,338 'move':246 'never':214,326,479,554,625 'next':359,385 'non':189 'non-crit':188 'npx':63,76,93 'only-categori':96 'open':103 'optimis':26 'option':212,291,553,573 'order':606,619 'output':58,66,69,83,86 'output-path':68,85 'page':27,109,163,365,463 'paint':130,360 'pars':604 'parti':432,438 'path':70,87 'percept':159 'perform':2,8,47,99,115,459 'performance-and-web-vit':1 'possibl':516 'poster':152 'practic':120 'preload':182,293 'prevent':471 'properti':346 'quick':362 're':446 're-rend':445 'react':444 'react.memo':452 'reduc':32,592 'render':44,153,184,206,227,250,447,521 'render-block':43,183 'report':62 'requestanimationfram':429 'requestidlecallback':413 'reserv':299,313,330 'resiz':420 'resourc':46 'respond':366 'respons':197 'review':39 'rule':465 'run':51 'score':37,112 'script':433,439,610,623 'scroll':418 'seo':101,122 'serv':483,501 'server':196 'set':279,467 'shift':31,231,244,321 'show':544 'singl':457 'size':177,493,498,594 'skill' 'skill-performance-and-web-vitals' 'slide':348 'slide-in':347 'slot':318,337,507 'slow':195 'sluggish':394 'smaller':410,490 'soon':614 'source-dembrandt' 'space':300,331 'specif':90 'src':535 'subset':585 'swap':210,261,286,543,547,555,564 'synchron':422,631 'tab':107 'tap':381 'target':111,114,138,238,370 'task':411 'text':584 'third':431,437 'third-parti':430,436 'thread':407,629 'top/left/width/height':327 'topic-accessibility' 'topic-agent-skills' 'topic-claude-code-skills' 'topic-claude-skills' 'topic-cursor-skills' 'topic-design-system' 'topic-design-tokens' 'topic-enterprise-ux' 'topic-gestalt' 'topic-skills-sh' 'topic-typography' 'topic-ui-design' 'transform':324,343,350,354 'transit':353 'translatey':351 'typic':487 'ui':7,20,392 'unexpect':242 'unoptimis':171 'updat':387,424 'url':536 'use':24,73,174,215,287,412,473,508,556,578,591,607,620 'usecallback':451 'usememo':450 'user':157,368,378 'ux':23 'video':151 'visibl':146 'visual':386 'vital':5,15,50,126 'web':4,14,49,125,203,284,518 'webp':484 'webp/avif':175 'width':280,468 'width/height':277 'without':254,276 'woff2':539 'write':427 'zero':574","prices":[{"id":"02475933-5e50-4f89-85d6-6335daef68bc","listingId":"6cb58830-ade5-4177-9c6b-05ff23197ca8","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"dembrandt","category":"dembrandt-skills","install_from":"skills.sh"},"createdAt":"2026-05-18T13:14:03.195Z"}],"sources":[{"listingId":"6cb58830-ade5-4177-9c6b-05ff23197ca8","source":"github","sourceId":"dembrandt/dembrandt-skills/performance-and-web-vitals","sourceUrl":"https://github.com/dembrandt/dembrandt-skills/tree/main/skills/performance-and-web-vitals","isPrimary":false,"firstSeenAt":"2026-05-18T13:14:03.195Z","lastSeenAt":"2026-05-18T19:08:28.005Z"}],"details":{"listingId":"6cb58830-ade5-4177-9c6b-05ff23197ca8","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"dembrandt","slug":"performance-and-web-vitals","github":{"repo":"dembrandt/dembrandt-skills","stars":9,"topics":["accessibility","agent-skills","claude-code-skills","claude-skills","cursor-skills","design-system","design-tokens","enterprise-ux","gestalt","skills-sh","typography","ui-design","ux","wcag"],"license":"mit","html_url":"https://github.com/dembrandt/dembrandt-skills","pushed_at":"2026-05-14T22:34:06Z","description":"UX and design system skills for AI agents based on 20 years of experience","skill_md_sha":"a7ab5a12e3c171b5234af2ec6191392b3effd115","skill_md_path":"skills/performance-and-web-vitals/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/dembrandt/dembrandt-skills/tree/main/skills/performance-and-web-vitals"},"layout":"multi","source":"github","category":"dembrandt-skills","frontmatter":{"name":"performance-and-web-vitals","description":"Audit UI performance with Lighthouse and fix Core Web Vitals — LCP, CLS, INP. Fast UI is good UX. Use when optimising page load, fixing layout shift, reducing input delay, improving Lighthouse scores, or reviewing images, fonts, and render-blocking resources."},"skills_sh_url":"https://skills.sh/dembrandt/dembrandt-skills/performance-and-web-vitals"},"updatedAt":"2026-05-18T19:08:28.005Z"}}