[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FCXPWS2H74G71X14CFRQ03":3,"skill-same:01M6FCXPWS2H74G71X14CFRQ03":74},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":37,"linkOnly":38,"markdownContent":39,"markdownBody":40,"frontMatter":41,"refName":45,"commitSha":46,"skillDirectory":47,"forksCount":48,"repositoryHtmlUrl":49,"repositoryHomepageUrl":6,"ownerHtmlUrl":50,"ownerBlogUrl":51,"ownerDescription":52,"updatedAt":53,"install":54,"resources":67},"01M6FCXPWS2H74G71X14CFRQ03","core-web-vitals",null,"Optimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when asked to \"improve Core Web Vitals\", \"fix LCP\", \"reduce CLS\", \"optimize INP\", \"page experience optimization\", or \"fix layout shifts\".","Dicklesworthstone\u002Fpi_agent_rust","Dicklesworthstone","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F35050222?v=4","01M6CG26AR942CSVMN1M5E3DSE","Jeff Emanuel","01M6CG26EPP71PSHW1VDS71121",1991,"MIT","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fdevelopment\u002Fcore-web-vitals\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36],"core","web","vitals","core web vitals","web vitals","when","reduce","for","ranking","layout","optimize","use","fix","quality","page","improve","Optimization","search","2026-11-10 10:12:30",false,"---\nname: core-web-vitals\ndescription: Optimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when asked to \"improve Core Web Vitals\", \"fix LCP\", \"reduce CLS\", \"optimize INP\", \"page experience optimization\", or \"fix layout shifts\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"1.0\"\n---\n\n# Core Web Vitals optimization\n\nTargeted optimization for the three Core Web Vitals metrics that affect Google Search ranking and user experience.\n\n## The three metrics\n\n| Metric | Measures | Good | Needs work | Poor |\n|--------|----------|------|------------|------|\n| **LCP** | Loading | ≤ 2.5s | 2.5s – 4s | > 4s |\n| **INP** | Interactivity | ≤ 200ms | 200ms – 500ms | > 500ms |\n| **CLS** | Visual Stability | ≤ 0.1 | 0.1 – 0.25 | > 0.25 |\n\nGoogle measures at the **75th percentile** — 75% of page visits must meet \"Good\" thresholds.\n\n---\n\n## LCP: Largest Contentful Paint\n\nLCP measures when the largest visible content element renders. Usually this is:\n- Hero image or video\n- Large text block\n- Background image\n- `\u003Csvg>` element\n\n### Common LCP issues\n\n**1. Slow server response (TTFB > 800ms)**\n```\nFix: CDN, caching, optimized backend, edge rendering\n```\n\n**2. Render-blocking resources**\n```html\n\u003C!-- ❌ Blocks rendering -->\n\u003Clink rel=\"stylesheet\" href=\"\u002Fall-styles.css\">\n\n\u003C!-- ✅ Critical CSS inlined, rest deferred -->\n\u003Cstyle>\u002F* Critical above-fold CSS *\u002F\u003C\u002Fstyle>\n\u003Clink rel=\"preload\" href=\"\u002Fstyles.css\" as=\"style\" \n      onload=\"this.onload=null;this.rel='stylesheet'\">\n```\n\n**3. Slow resource load times**\n```html\n\u003C!-- ❌ No hints, discovered late -->\n\u003Cimg src=\"\u002Fhero.jpg\" alt=\"Hero\">\n\n\u003C!-- ✅ Preloaded with high priority -->\n\u003Clink rel=\"preload\" href=\"\u002Fhero.webp\" as=\"image\" fetchpriority=\"high\">\n\u003Cimg src=\"\u002Fhero.webp\" alt=\"Hero\" fetchpriority=\"high\">\n```\n\n**4. Client-side rendering delays**\n```javascript\n\u002F\u002F ❌ Content loads after JavaScript\nuseEffect(() => {\n  fetch('\u002Fapi\u002Fhero-text').then(r => r.json()).then(setHeroText);\n}, []);\n\n\u002F\u002F ✅ Server-side or static rendering\n\u002F\u002F Use SSR, SSG, or streaming to send HTML with content\nexport async function getServerSideProps() {\n  const heroText = await fetchHeroText();\n  return { props: { heroText } };\n}\n```\n\n### LCP optimization checklist\n\n```markdown\n- [ ] TTFB \u003C 800ms (use CDN, edge caching)\n- [ ] LCP image preloaded with fetchpriority=\"high\"\n- [ ] LCP image optimized (WebP\u002FAVIF, correct size)\n- [ ] Critical CSS inlined (\u003C 14KB)\n- [ ] No render-blocking JavaScript in \u003Chead>\n- [ ] Fonts don't block text rendering (font-display: swap)\n- [ ] LCP element in initial HTML (not JS-rendered)\n```\n\n### LCP element identification\n```javascript\n\u002F\u002F Find your LCP element\nnew PerformanceObserver((list) => {\n  const entries = list.getEntries();\n  const lastEntry = entries[entries.length - 1];\n  console.log('LCP element:', lastEntry.element);\n  console.log('LCP time:', lastEntry.startTime);\n}).observe({ type: 'largest-contentful-paint', buffered: true });\n```\n\n---\n\n## INP: Interaction to Next Paint\n\nINP measures responsiveness across ALL interactions (clicks, taps, key presses) during a page visit. It reports the worst interaction (at 98th percentile for high-traffic pages).\n\n### INP breakdown\n\nTotal INP = **Input Delay** + **Processing Time** + **Presentation Delay**\n\n| Phase | Target | Optimization |\n|-------|--------|--------------|\n| Input Delay | \u003C 50ms | Reduce main thread blocking |\n| Processing | \u003C 100ms | Optimize event handlers |\n| Presentation | \u003C 50ms | Minimize rendering work |\n\n### Common INP issues\n\n**1. Long tasks blocking main thread**\n```javascript\n\u002F\u002F ❌ Long synchronous task\nfunction processLargeArray(items) {\n  items.forEach(item => expensiveOperation(item));\n}\n\n\u002F\u002F ✅ Break into chunks with yielding\nasync function processLargeArray(items) {\n  const CHUNK_SIZE = 100;\n  for (let i = 0; i \u003C items.length; i += CHUNK_SIZE) {\n    const chunk = items.slice(i, i + CHUNK_SIZE);\n    chunk.forEach(item => expensiveOperation(item));\n    \n    \u002F\u002F Yield to main thread\n    await new Promise(r => setTimeout(r, 0));\n    \u002F\u002F Or use scheduler.yield() when available\n  }\n}\n```\n\n**2. Heavy event handlers**\n```javascript\n\u002F\u002F ❌ All work in handler\nbutton.addEventListener('click', () => {\n  \u002F\u002F Heavy computation\n  const result = calculateComplexThing();\n  \u002F\u002F DOM updates\n  updateUI(result);\n  \u002F\u002F Analytics\n  trackEvent('click');\n});\n\n\u002F\u002F ✅ Prioritize visual feedback\nbutton.addEventListener('click', () => {\n  \u002F\u002F Immediate visual feedback\n  button.classList.add('loading');\n  \n  \u002F\u002F Defer non-critical work\n  requestAnimationFrame(() => {\n    const result = calculateComplexThing();\n    updateUI(result);\n  });\n  \n  \u002F\u002F Use requestIdleCallback for analytics\n  requestIdleCallback(() => trackEvent('click'));\n});\n```\n\n**3. Third-party scripts**\n```javascript\n\u002F\u002F ❌ Eagerly loaded, blocks interactions\n\u003Cscript src=\"https:\u002F\u002Fheavy-widget.com\u002Fwidget.js\">\u003C\u002Fscript>\n\n\u002F\u002F ✅ Lazy loaded on interaction or visibility\nconst loadWidget = () => {\n  import('https:\u002F\u002Fheavy-widget.com\u002Fwidget.js')\n    .then(widget => widget.init());\n};\nbutton.addEventListener('click', loadWidget, { once: true });\n```\n\n**4. Excessive re-renders (React\u002FVue)**\n```javascript\n\u002F\u002F ❌ Re-renders entire tree\nfunction App() {\n  const [count, setCount] = useState(0);\n  return (\n    \u003Cdiv>\n      \u003CCounter count={count} \u002F>\n      \u003CExpensiveComponent \u002F> {\u002F* Re-renders on every count change *\u002F}\n    \u003C\u002Fdiv>\n  );\n}\n\n\u002F\u002F ✅ Memoized expensive components\nconst MemoizedExpensive = React.memo(ExpensiveComponent);\n\nfunction App() {\n  const [count, setCount] = useState(0);\n  return (\n    \u003Cdiv>\n      \u003CCounter count={count} \u002F>\n      \u003CMemoizedExpensive \u002F>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n### INP optimization checklist\n\n```markdown\n- [ ] No tasks > 50ms on main thread\n- [ ] Event handlers complete quickly (\u003C 100ms)\n- [ ] Visual feedback provided immediately\n- [ ] Heavy work deferred with requestIdleCallback\n- [ ] Third-party scripts don't block interactions\n- [ ] Debounced input handlers where appropriate\n- [ ] Web Workers for CPU-intensive operations\n```\n\n### INP debugging\n```javascript\n\u002F\u002F Identify slow interactions\nnew PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (entry.duration > 200) {\n      console.warn('Slow interaction:', {\n        type: entry.name,\n        duration: entry.duration,\n        processingStart: entry.processingStart,\n        processingEnd: entry.processingEnd,\n        target: entry.target\n      });\n    }\n  }\n}).observe({ type: 'event', buffered: true, durationThreshold: 16 });\n```\n\n---\n\n## CLS: Cumulative Layout Shift\n\nCLS measures unexpected layout shifts. A shift occurs when a visible element changes position between frames without user interaction.\n\n**CLS Formula:** `impact fraction × distance fraction`\n\n### Common CLS causes\n\n**1. Images without dimensions**\n```html\n\u003C!-- ❌ Causes layout shift when loaded -->\n\u003Cimg src=\"photo.jpg\" alt=\"Photo\">\n\n\u003C!-- ✅ Space reserved -->\n\u003Cimg src=\"photo.jpg\" alt=\"Photo\" width=\"800\" height=\"600\">\n\n\u003C!-- ✅ Or use aspect-ratio -->\n\u003Cimg src=\"photo.jpg\" alt=\"Photo\" style=\"aspect-ratio: 4\u002F3; width: 100%;\">\n```\n\n**2. Ads, embeds, and iframes**\n```html\n\u003C!-- ❌ Unknown size until loaded -->\n\u003Ciframe src=\"https:\u002F\u002Fad-network.com\u002Fad\">\u003C\u002Fiframe>\n\n\u003C!-- ✅ Reserve space with min-height -->\n\u003Cdiv style=\"min-height: 250px;\">\n  \u003Ciframe src=\"https:\u002F\u002Fad-network.com\u002Fad\" height=\"250\">\u003C\u002Fiframe>\n\u003C\u002Fdiv>\n\n\u003C!-- ✅ Or use aspect-ratio container -->\n\u003Cdiv style=\"aspect-ratio: 16\u002F9;\">\n  \u003Ciframe src=\"https:\u002F\u002Fyoutube.com\u002Fembed\u002F...\" \n          style=\"width: 100%; height: 100%;\">\u003C\u002Fiframe>\n\u003C\u002Fdiv>\n```\n\n**3. Dynamically injected content**\n```javascript\n\u002F\u002F ❌ Inserts content above viewport\nnotifications.prepend(newNotification);\n\n\u002F\u002F ✅ Insert below viewport or use transform\nconst insertBelow = viewport.bottom \u003C newNotification.top;\nif (insertBelow) {\n  notifications.prepend(newNotification);\n} else {\n  \u002F\u002F Animate in without shifting\n  newNotification.style.transform = 'translateY(-100%)';\n  notifications.prepend(newNotification);\n  requestAnimationFrame(() => {\n    newNotification.style.transform = '';\n  });\n}\n```\n\n**4. Web fonts causing FOUT**\n```css\n\u002F* ❌ Font swap shifts text *\u002F\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n}\n\n\u002F* ✅ Optional font (no shift if slow) *\u002F\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n  font-display: optional;\n}\n\n\u002F* ✅ Or match fallback metrics *\u002F\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n  font-display: swap;\n  size-adjust: 105%; \u002F* Match fallback size *\u002F\n  ascent-override: 95%;\n  descent-override: 20%;\n}\n```\n\n**5. Animations triggering layout**\n```css\n\u002F* ❌ Animates layout properties *\u002F\n.animate {\n  transition: height 0.3s, width 0.3s;\n}\n\n\u002F* ✅ Use transform instead *\u002F\n.animate {\n  transition: transform 0.3s;\n}\n.animate.expanded {\n  transform: scale(1.2);\n}\n```\n\n### CLS optimization checklist\n\n```markdown\n- [ ] All images have width\u002Fheight or aspect-ratio\n- [ ] All videos\u002Fembeds have reserved space\n- [ ] Ads have min-height containers\n- [ ] Fonts use font-display: optional or matched metrics\n- [ ] Dynamic content inserted below viewport\n- [ ] Animations use transform\u002Fopacity only\n- [ ] No content injected above existing content\n```\n\n### CLS debugging\n```javascript\n\u002F\u002F Track layout shifts\nnew PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (!entry.hadRecentInput) {\n      console.log('Layout shift:', entry.value);\n      entry.sources?.forEach(source => {\n        console.log('  Shifted element:', source.node);\n        console.log('  Previous rect:', source.previousRect);\n        console.log('  Current rect:', source.currentRect);\n      });\n    }\n  }\n}).observe({ type: 'layout-shift', buffered: true });\n```\n\n---\n\n## Measurement tools\n\n### Lab testing\n- **Chrome DevTools** → Performance panel, Lighthouse\n- **WebPageTest** → Detailed waterfall, filmstrip\n- **Lighthouse CLI** → `npx lighthouse \u003Curl>`\n\n### Field data (real users)\n- **Chrome User Experience Report (CrUX)** → BigQuery or API\n- **Search Console** → Core Web Vitals report\n- **web-vitals library** → Send to your analytics\n\n```javascript\nimport {onLCP, onINP, onCLS} from 'web-vitals';\n\nfunction sendToAnalytics({name, value, rating}) {\n  gtag('event', name, {\n    event_category: 'Web Vitals',\n    value: Math.round(name === 'CLS' ? value * 1000 : value),\n    event_label: rating\n  });\n}\n\nonLCP(sendToAnalytics);\nonINP(sendToAnalytics);\nonCLS(sendToAnalytics);\n```\n\n---\n\n## Framework quick fixes\n\n### Next.js\n```jsx\n\u002F\u002F LCP: Use next\u002Fimage with priority\nimport Image from 'next\u002Fimage';\n\u003CImage src=\"\u002Fhero.jpg\" priority fill alt=\"Hero\" \u002F>\n\n\u002F\u002F INP: Use dynamic imports\nconst HeavyComponent = dynamic(() => import('.\u002FHeavy'), { ssr: false });\n\n\u002F\u002F CLS: Image component handles dimensions automatically\n```\n\n### React\n```jsx\n\u002F\u002F LCP: Preload in head\n\u003Clink rel=\"preload\" href=\"\u002Fhero.jpg\" as=\"image\" fetchpriority=\"high\" \u002F>\n\n\u002F\u002F INP: Memoize and useTransition\nconst [isPending, startTransition] = useTransition();\nstartTransition(() => setExpensiveState(newValue));\n\n\u002F\u002F CLS: Always specify dimensions in img tags\n```\n\n### Vue\u002FNuxt\n```vue\n\u003C!-- LCP: Use nuxt\u002Fimage with preload -->\n\u003CNuxtImg src=\"\u002Fhero.jpg\" preload loading=\"eager\" \u002F>\n\n\u003C!-- INP: Use async components -->\n\u003Ccomponent :is=\"() => import('.\u002FHeavy.vue')\" \u002F>\n\n\u003C!-- CLS: Use aspect-ratio CSS -->\n\u003Cimg :style=\"{ aspectRatio: '16\u002F9' }\" \u002F>\n```\n\n## References\n\n- [web.dev LCP](https:\u002F\u002Fweb.dev\u002Farticles\u002Flcp)\n- [web.dev INP](https:\u002F\u002Fweb.dev\u002Farticles\u002Finp)\n- [web.dev CLS](https:\u002F\u002Fweb.dev\u002Farticles\u002Fcls)\n- [Performance skill](..\u002Fperformance\u002FSKILL.md)\n","# Core Web Vitals optimization\n\nTargeted optimization for the three Core Web Vitals metrics that affect Google Search ranking and user experience.\n\n## The three metrics\n\n| Metric | Measures | Good | Needs work | Poor |\n|--------|----------|------|------------|------|\n| **LCP** | Loading | ≤ 2.5s | 2.5s – 4s | > 4s |\n| **INP** | Interactivity | ≤ 200ms | 200ms – 500ms | > 500ms |\n| **CLS** | Visual Stability | ≤ 0.1 | 0.1 – 0.25 | > 0.25 |\n\nGoogle measures at the **75th percentile** — 75% of page visits must meet \"Good\" thresholds.\n\n---\n\n## LCP: Largest Contentful Paint\n\nLCP measures when the largest visible content element renders. Usually this is:\n- Hero image or video\n- Large text block\n- Background image\n- `\u003Csvg>` element\n\n### Common LCP issues\n\n**1. Slow server response (TTFB > 800ms)**\n```\nFix: CDN, caching, optimized backend, edge rendering\n```\n\n**2. Render-blocking resources**\n```html\n\u003C!-- ❌ Blocks rendering -->\n\u003Clink rel=\"stylesheet\" href=\"\u002Fall-styles.css\">\n\n\u003C!-- ✅ Critical CSS inlined, rest deferred -->\n\u003Cstyle>\u002F* Critical above-fold CSS *\u002F\u003C\u002Fstyle>\n\u003Clink rel=\"preload\" href=\"\u002Fstyles.css\" as=\"style\" \n      onload=\"this.onload=null;this.rel='stylesheet'\">\n```\n\n**3. Slow resource load times**\n```html\n\u003C!-- ❌ No hints, discovered late -->\n\u003Cimg src=\"\u002Fhero.jpg\" alt=\"Hero\">\n\n\u003C!-- ✅ Preloaded with high priority -->\n\u003Clink rel=\"preload\" href=\"\u002Fhero.webp\" as=\"image\" fetchpriority=\"high\">\n\u003Cimg src=\"\u002Fhero.webp\" alt=\"Hero\" fetchpriority=\"high\">\n```\n\n**4. Client-side rendering delays**\n```javascript\n\u002F\u002F ❌ Content loads after JavaScript\nuseEffect(() => {\n  fetch('\u002Fapi\u002Fhero-text').then(r => r.json()).then(setHeroText);\n}, []);\n\n\u002F\u002F ✅ Server-side or static rendering\n\u002F\u002F Use SSR, SSG, or streaming to send HTML with content\nexport async function getServerSideProps() {\n  const heroText = await fetchHeroText();\n  return { props: { heroText } };\n}\n```\n\n### LCP optimization checklist\n\n```markdown\n- [ ] TTFB \u003C 800ms (use CDN, edge caching)\n- [ ] LCP image preloaded with fetchpriority=\"high\"\n- [ ] LCP image optimized (WebP\u002FAVIF, correct size)\n- [ ] Critical CSS inlined (\u003C 14KB)\n- [ ] No render-blocking JavaScript in \u003Chead>\n- [ ] Fonts don't block text rendering (font-display: swap)\n- [ ] LCP element in initial HTML (not JS-rendered)\n```\n\n### LCP element identification\n```javascript\n\u002F\u002F Find your LCP element\nnew PerformanceObserver((list) => {\n  const entries = list.getEntries();\n  const lastEntry = entries[entries.length - 1];\n  console.log('LCP element:', lastEntry.element);\n  console.log('LCP time:', lastEntry.startTime);\n}).observe({ type: 'largest-contentful-paint', buffered: true });\n```\n\n---\n\n## INP: Interaction to Next Paint\n\nINP measures responsiveness across ALL interactions (clicks, taps, key presses) during a page visit. It reports the worst interaction (at 98th percentile for high-traffic pages).\n\n### INP breakdown\n\nTotal INP = **Input Delay** + **Processing Time** + **Presentation Delay**\n\n| Phase | Target | Optimization |\n|-------|--------|--------------|\n| Input Delay | \u003C 50ms | Reduce main thread blocking |\n| Processing | \u003C 100ms | Optimize event handlers |\n| Presentation | \u003C 50ms | Minimize rendering work |\n\n### Common INP issues\n\n**1. Long tasks blocking main thread**\n```javascript\n\u002F\u002F ❌ Long synchronous task\nfunction processLargeArray(items) {\n  items.forEach(item => expensiveOperation(item));\n}\n\n\u002F\u002F ✅ Break into chunks with yielding\nasync function processLargeArray(items) {\n  const CHUNK_SIZE = 100;\n  for (let i = 0; i \u003C items.length; i += CHUNK_SIZE) {\n    const chunk = items.slice(i, i + CHUNK_SIZE);\n    chunk.forEach(item => expensiveOperation(item));\n    \n    \u002F\u002F Yield to main thread\n    await new Promise(r => setTimeout(r, 0));\n    \u002F\u002F Or use scheduler.yield() when available\n  }\n}\n```\n\n**2. Heavy event handlers**\n```javascript\n\u002F\u002F ❌ All work in handler\nbutton.addEventListener('click', () => {\n  \u002F\u002F Heavy computation\n  const result = calculateComplexThing();\n  \u002F\u002F DOM updates\n  updateUI(result);\n  \u002F\u002F Analytics\n  trackEvent('click');\n});\n\n\u002F\u002F ✅ Prioritize visual feedback\nbutton.addEventListener('click', () => {\n  \u002F\u002F Immediate visual feedback\n  button.classList.add('loading');\n  \n  \u002F\u002F Defer non-critical work\n  requestAnimationFrame(() => {\n    const result = calculateComplexThing();\n    updateUI(result);\n  });\n  \n  \u002F\u002F Use requestIdleCallback for analytics\n  requestIdleCallback(() => trackEvent('click'));\n});\n```\n\n**3. Third-party scripts**\n```javascript\n\u002F\u002F ❌ Eagerly loaded, blocks interactions\n\u003Cscript src=\"https:\u002F\u002Fheavy-widget.com\u002Fwidget.js\">\u003C\u002Fscript>\n\n\u002F\u002F ✅ Lazy loaded on interaction or visibility\nconst loadWidget = () => {\n  import('https:\u002F\u002Fheavy-widget.com\u002Fwidget.js')\n    .then(widget => widget.init());\n};\nbutton.addEventListener('click', loadWidget, { once: true });\n```\n\n**4. Excessive re-renders (React\u002FVue)**\n```javascript\n\u002F\u002F ❌ Re-renders entire tree\nfunction App() {\n  const [count, setCount] = useState(0);\n  return (\n    \u003Cdiv>\n      \u003CCounter count={count} \u002F>\n      \u003CExpensiveComponent \u002F> {\u002F* Re-renders on every count change *\u002F}\n    \u003C\u002Fdiv>\n  );\n}\n\n\u002F\u002F ✅ Memoized expensive components\nconst MemoizedExpensive = React.memo(ExpensiveComponent);\n\nfunction App() {\n  const [count, setCount] = useState(0);\n  return (\n    \u003Cdiv>\n      \u003CCounter count={count} \u002F>\n      \u003CMemoizedExpensive \u002F>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n### INP optimization checklist\n\n```markdown\n- [ ] No tasks > 50ms on main thread\n- [ ] Event handlers complete quickly (\u003C 100ms)\n- [ ] Visual feedback provided immediately\n- [ ] Heavy work deferred with requestIdleCallback\n- [ ] Third-party scripts don't block interactions\n- [ ] Debounced input handlers where appropriate\n- [ ] Web Workers for CPU-intensive operations\n```\n\n### INP debugging\n```javascript\n\u002F\u002F Identify slow interactions\nnew PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (entry.duration > 200) {\n      console.warn('Slow interaction:', {\n        type: entry.name,\n        duration: entry.duration,\n        processingStart: entry.processingStart,\n        processingEnd: entry.processingEnd,\n        target: entry.target\n      });\n    }\n  }\n}).observe({ type: 'event', buffered: true, durationThreshold: 16 });\n```\n\n---\n\n## CLS: Cumulative Layout Shift\n\nCLS measures unexpected layout shifts. A shift occurs when a visible element changes position between frames without user interaction.\n\n**CLS Formula:** `impact fraction × distance fraction`\n\n### Common CLS causes\n\n**1. Images without dimensions**\n```html\n\u003C!-- ❌ Causes layout shift when loaded -->\n\u003Cimg src=\"photo.jpg\" alt=\"Photo\">\n\n\u003C!-- ✅ Space reserved -->\n\u003Cimg src=\"photo.jpg\" alt=\"Photo\" width=\"800\" height=\"600\">\n\n\u003C!-- ✅ Or use aspect-ratio -->\n\u003Cimg src=\"photo.jpg\" alt=\"Photo\" style=\"aspect-ratio: 4\u002F3; width: 100%;\">\n```\n\n**2. Ads, embeds, and iframes**\n```html\n\u003C!-- ❌ Unknown size until loaded -->\n\u003Ciframe src=\"https:\u002F\u002Fad-network.com\u002Fad\">\u003C\u002Fiframe>\n\n\u003C!-- ✅ Reserve space with min-height -->\n\u003Cdiv style=\"min-height: 250px;\">\n  \u003Ciframe src=\"https:\u002F\u002Fad-network.com\u002Fad\" height=\"250\">\u003C\u002Fiframe>\n\u003C\u002Fdiv>\n\n\u003C!-- ✅ Or use aspect-ratio container -->\n\u003Cdiv style=\"aspect-ratio: 16\u002F9;\">\n  \u003Ciframe src=\"https:\u002F\u002Fyoutube.com\u002Fembed\u002F...\" \n          style=\"width: 100%; height: 100%;\">\u003C\u002Fiframe>\n\u003C\u002Fdiv>\n```\n\n**3. Dynamically injected content**\n```javascript\n\u002F\u002F ❌ Inserts content above viewport\nnotifications.prepend(newNotification);\n\n\u002F\u002F ✅ Insert below viewport or use transform\nconst insertBelow = viewport.bottom \u003C newNotification.top;\nif (insertBelow) {\n  notifications.prepend(newNotification);\n} else {\n  \u002F\u002F Animate in without shifting\n  newNotification.style.transform = 'translateY(-100%)';\n  notifications.prepend(newNotification);\n  requestAnimationFrame(() => {\n    newNotification.style.transform = '';\n  });\n}\n```\n\n**4. Web fonts causing FOUT**\n```css\n\u002F* ❌ Font swap shifts text *\u002F\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n}\n\n\u002F* ✅ Optional font (no shift if slow) *\u002F\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n  font-display: optional;\n}\n\n\u002F* ✅ Or match fallback metrics *\u002F\n@font-face {\n  font-family: 'Custom';\n  src: url('custom.woff2') format('woff2');\n  font-display: swap;\n  size-adjust: 105%; \u002F* Match fallback size *\u002F\n  ascent-override: 95%;\n  descent-override: 20%;\n}\n```\n\n**5. Animations triggering layout**\n```css\n\u002F* ❌ Animates layout properties *\u002F\n.animate {\n  transition: height 0.3s, width 0.3s;\n}\n\n\u002F* ✅ Use transform instead *\u002F\n.animate {\n  transition: transform 0.3s;\n}\n.animate.expanded {\n  transform: scale(1.2);\n}\n```\n\n### CLS optimization checklist\n\n```markdown\n- [ ] All images have width\u002Fheight or aspect-ratio\n- [ ] All videos\u002Fembeds have reserved space\n- [ ] Ads have min-height containers\n- [ ] Fonts use font-display: optional or matched metrics\n- [ ] Dynamic content inserted below viewport\n- [ ] Animations use transform\u002Fopacity only\n- [ ] No content injected above existing content\n```\n\n### CLS debugging\n```javascript\n\u002F\u002F Track layout shifts\nnew PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (!entry.hadRecentInput) {\n      console.log('Layout shift:', entry.value);\n      entry.sources?.forEach(source => {\n        console.log('  Shifted element:', source.node);\n        console.log('  Previous rect:', source.previousRect);\n        console.log('  Current rect:', source.currentRect);\n      });\n    }\n  }\n}).observe({ type: 'layout-shift', buffered: true });\n```\n\n---\n\n## Measurement tools\n\n### Lab testing\n- **Chrome DevTools** → Performance panel, Lighthouse\n- **WebPageTest** → Detailed waterfall, filmstrip\n- **Lighthouse CLI** → `npx lighthouse \u003Curl>`\n\n### Field data (real users)\n- **Chrome User Experience Report (CrUX)** → BigQuery or API\n- **Search Console** → Core Web Vitals report\n- **web-vitals library** → Send to your analytics\n\n```javascript\nimport {onLCP, onINP, onCLS} from 'web-vitals';\n\nfunction sendToAnalytics({name, value, rating}) {\n  gtag('event', name, {\n    event_category: 'Web Vitals',\n    value: Math.round(name === 'CLS' ? value * 1000 : value),\n    event_label: rating\n  });\n}\n\nonLCP(sendToAnalytics);\nonINP(sendToAnalytics);\nonCLS(sendToAnalytics);\n```\n\n---\n\n## Framework quick fixes\n\n### Next.js\n```jsx\n\u002F\u002F LCP: Use next\u002Fimage with priority\nimport Image from 'next\u002Fimage';\n\u003CImage src=\"\u002Fhero.jpg\" priority fill alt=\"Hero\" \u002F>\n\n\u002F\u002F INP: Use dynamic imports\nconst HeavyComponent = dynamic(() => import('.\u002FHeavy'), { ssr: false });\n\n\u002F\u002F CLS: Image component handles dimensions automatically\n```\n\n### React\n```jsx\n\u002F\u002F LCP: Preload in head\n\u003Clink rel=\"preload\" href=\"\u002Fhero.jpg\" as=\"image\" fetchpriority=\"high\" \u002F>\n\n\u002F\u002F INP: Memoize and useTransition\nconst [isPending, startTransition] = useTransition();\nstartTransition(() => setExpensiveState(newValue));\n\n\u002F\u002F CLS: Always specify dimensions in img tags\n```\n\n### Vue\u002FNuxt\n```vue\n\u003C!-- LCP: Use nuxt\u002Fimage with preload -->\n\u003CNuxtImg src=\"\u002Fhero.jpg\" preload loading=\"eager\" \u002F>\n\n\u003C!-- INP: Use async components -->\n\u003Ccomponent :is=\"() => import('.\u002FHeavy.vue')\" \u002F>\n\n\u003C!-- CLS: Use aspect-ratio CSS -->\n\u003Cimg :style=\"{ aspectRatio: '16\u002F9' }\" \u002F>\n```\n\n## References\n\n- [web.dev LCP](https:\u002F\u002Fweb.dev\u002Farticles\u002Flcp)\n- [web.dev INP](https:\u002F\u002Fweb.dev\u002Farticles\u002Finp)\n- [web.dev CLS](https:\u002F\u002Fweb.dev\u002Farticles\u002Fcls)\n- [Performance skill](..\u002Fperformance\u002FSKILL.md)\n",{"name":5,"license":15,"metadata":42,"description":7},{"author":43,"version":44},"web-quality-skills","1.0","main","90b50ed0778745a80fd5bfb13b1843d45b9e82f7","tests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fdevelopment\u002Fcore-web-vitals",234,"https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust","https:\u002F\u002Fgithub.com\u002FDicklesworthstone","https:\u002F\u002Fwww.jeffreyemanuel.com\u002F","Building in NY","2026-11-13 11:50:27",{"source":8,"skillName":5,"commands":55,"prompt":65,"zipAvailable":66},[56,59,62],{"tool":57,"command":58},"npx","npx skills add Dicklesworthstone\u002Fpi_agent_rust --skill core-web-vitals",{"tool":60,"command":61},"bunx","bunx skills add Dicklesworthstone\u002Fpi_agent_rust --skill core-web-vitals",{"tool":63,"command":64},"pnpm","pnpm dlx skills add Dicklesworthstone\u002Fpi_agent_rust --skill core-web-vitals","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fdevelopment\u002Fcore-web-vitals\u002FSKILL.md",true,[68],{"path":69,"resourceType":70,"resourceTypeDesc":71,"sizeBytes":72,"sourceHtmlUrl":73},"references\u002FLCP.md","REFERENCE","参考文档",4431,"https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fdevelopment\u002Fcore-web-vitals\u002Freferences\u002FLCP.md",[75,113,138,163,190,212],{"skillNo":76,"name":77,"nameZh":6,"description":78,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":79,"licenseFileUrl":80,"sourceHtmlUrl":81,"categories":82,"keywords":88,"publishedAt":37},"01M6FEGPPK0ZTKCCM0R3E3KS33","web-artifacts-builder","Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn\u002Fui). Use for complex artifacts requiring state management, routing, or shadcn\u002Fui components - not for simple single-file HTML\u002FJSX artifacts.","Apache-2.0","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Futilities\u002Fweb-artifacts-builder\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Futilities\u002Fweb-artifacts-builder\u002FSKILL.md",[83],{"categoryNo":84,"categoryName":85,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[89,20,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,30,106,107,108,109,110,26,111,112],"web artifacts builder","artifacts","artifacts builder","builder","frontend","css","components","react","component","routing","state management","state","shadcn","file","ui","Tools","Tailwind","html","single-file","simple","ui components","multi","jsx","suite",{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":79,"licenseFileUrl":117,"sourceHtmlUrl":118,"categories":119,"keywords":124,"publishedAt":37},"01M6FEE9VTK1W4GY21AKREJXRG","webapp-testing","Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fsecurity\u002Fwebapp-testing\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fsecurity\u002Fwebapp-testing\u002FSKILL.md",[120,123],{"categoryNo":121,"categoryName":122,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":84,"categoryName":85,"taxonomyType":86,"taxonomyTypeDesc":87},[125,126,127,93,128,129,20,130,131,132,103,133,134,135,136,137,26],"testing","webapp testing","webapp","screenshots","playwright","debugging","toolkit","browser","logs","applications","behavior","local","with",{"skillNo":139,"name":140,"nameZh":6,"description":141,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":142,"sourceHtmlUrl":143,"categories":144,"keywords":145,"publishedAt":37},"01M6FE20F7ZTQBFWKF4MPJ2SBX","neuropixels-analysis","Neuropixels neural recording analysis. Load SpikeGLX\u002FOpenEphys data, preprocess, motion correction, Kilosort4 spike sorting, quality metrics, Allen\u002FIBL curation, AI-assisted visual analysis, for Neuropixels 1.0\u002F2.0 extracellular electrophysiology. Use when working with neural recordings, spike sorting, extracellular electrophysiology, or when the user mentions Neuropixels, SpikeGLX, Open Ephys, Kilosort, quality metrics, or unit curation.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fscientific\u002Fneuropixels-analysis\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fscientific\u002Fneuropixels-analysis\u002FSKILL.md",[],[146,147,148,149,150,151,32,152,30,153,154,155,156,157,158,159,137,160,161,162,26,24],"analysis","neuropixels analysis","spike","metrics","data","open","user","recordings","Neural","motion","visual","quality metrics","unit","sorting","curation","recording","load",{"skillNo":164,"name":165,"nameZh":6,"description":166,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":79,"licenseFileUrl":167,"sourceHtmlUrl":168,"categories":169,"keywords":173,"publishedAt":37},"01M6FDP0PDK6F1GGX395C7625Z","skill-creator","Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fskill-creator\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fskill-creator\u002FSKILL.md",[170],{"categoryNo":171,"categoryName":172,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000023","Skill与智能体开发",[174,175,176,177,178,179,180,181,182,183,184,185,186,187,137,26,188,24,189],"skill creator","creator","workflows","create","update","capabilities","knowledge","tool","integrations","specialized","guide","new skill","existing skill","users","Existing","new",{"skillNo":191,"name":192,"nameZh":6,"description":193,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":194,"sourceHtmlUrl":195,"categories":196,"keywords":198,"publishedAt":37},"01M6FDM8Y7VK7NYG9SM7QPA3PW","notebooklm","Use this skill to query your Google NotebookLM notebooks directly from Claude Code for source-grounded, citation-backed answers from Gemini. Browser automation, library management, persistent auth. Drastically reduced hallucinations through document-only responses.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fnotebooklm\u002FLICENSE","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fnotebooklm\u002FSKILL.md",[197],{"categoryNo":171,"categoryName":172,"taxonomyType":86,"taxonomyTypeDesc":87},[192,199,200,201,202,203,204,132,205,206,30,207,208,209,210,26,211],"google","browser automation","gemini","automation","auth","code","library","management","only","document","query","Citation","source",{"skillNo":213,"name":214,"nameZh":6,"description":215,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":79,"licenseFileUrl":216,"sourceHtmlUrl":217,"categories":218,"keywords":225,"publishedAt":37},"01M6FDHPXE10J3XGTCH5HBHS1M","slack-gif-creator","Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like \"make me a GIF of X doing Y for Slack.\"","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fenterprise-communication\u002Fslack-gif-creator\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fenterprise-communication\u002Fslack-gif-creator\u002FSKILL.md",[219,222],{"categoryNo":220,"categoryName":221,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000005","邮件与沟通",{"categoryNo":223,"categoryName":224,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000016","图像与多媒体",[226,227,228,175,229,104,230,231,180,30,232,187,233,234,235,26,236,24],"slack gif creator","slack","gif","validation","constraints","Animation","me","make","Utilities","concepts","request"]