tech-leads-club/agent-skills

core-web-vitals

Optimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking.

소스 보기

사용 방법

  1. 설치 명령을 복사하고 자신의 에이전트 환경에 설치하기 전에 소스 패키지를 확인하세요.
  2. 필요한 도구, 인증 정보, 지원 환경은 원본 문서에서 확인하세요.
  3. 입력과 원하는 산출물을 설명하고 출처와 결과를 대조하세요.
npx skills add https://github.com/tech-leads-club/agent-skills --skill core-web-vitals

시작 전 확인

  • 설치에는 모델 구독, API 한도, 파일 접근 권한이 포함되지 않습니다.
  • 별점 수와 설치 수는 활동 지표이며 독립적인 품질·보안 검증이 아닙니다.
원본 Skill 문서

원본 저장소의 제목, 예시, 코드, 표, 링크, 이미지를 유지해 표시합니다.

Core Web Vitals optimization

Targeted optimization for the three Core Web Vitals metrics that affect Google Search ranking and user experience.

The three metrics

MetricMeasuresGoodNeeds workPoor
LCPLoading≤ 2.5s2.5s – 4s> 4s
INPInteractivity≤ 200ms200ms – 500ms> 500ms
CLSVisual Stability≤ 0.10.1 – 0.25> 0.25

Google measures at the 75th percentile — 75% of page visits must meet "Good" thresholds.


LCP: Largest Contentful Paint

LCP measures when the largest visible content element renders. Usually this is:

  • Hero image or video
  • Large text block
  • Background image
  • <svg> element

Common LCP issues

1. Slow server response (TTFB > 800ms)

Fix: CDN, caching, optimized backend, edge rendering

2. Render-blocking resources

html
<!-- ❌ Blocks rendering -->
<link rel="stylesheet" href="/all-styles.css" />

<!-- ✅ Critical CSS inlined, rest deferred -->
<style>
  /* Critical above-fold CSS */
</style>
<link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'" />

3. Slow resource load times

html
<!-- ❌ No hints, discovered late -->
<img src="/hero.jpg" alt="Hero" />

<!-- ✅ Preloaded with high priority -->
<link rel="preload" href="/hero.webp" as="image" fetchpriority="high" />
<img src="/hero.webp" alt="Hero" fetchpriority="high" />

4. Client-side rendering delays

javascript
// ❌ Content loads after JavaScript
useEffect(() => {
  fetch('/api/hero-text')
    .then((r) => r.json())
    .then(setHeroText)
}, [])

// ✅ Server-side or static rendering
// Use SSR, SSG, or streaming to send HTML with content
export async function getServerSideProps() {
  const heroText = await fetchHeroText()
  return { props: { heroText } }
}

LCP optimization checklist

markdown
- [ ] TTFB < 800ms (use CDN, edge caching)
- [ ] LCP image preloaded with fetchpriority="high"
- [ ] LCP image optimized (WebP/AVIF, correct size)
- [ ] Critical CSS inlined (< 14KB)
- [ ] No render-blocking JavaScript in <head>
- [ ] Fonts don't block text rendering (font-display: swap)
- [ ] LCP element in initial HTML (not JS-rendered)

LCP element identification

javascript
// Find your LCP element
new PerformanceObserver((list) => {
  const entries = list.getEntries()
  const lastEntry = entries[entries.length - 1]
  console.log('LCP element:', lastEntry.element)
  console.log('LCP time:', lastEntry.startTime)
}).observe({ type: 'largest-contentful-paint', buffered: true })

INP: Interaction to Next Paint

INP 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).

INP breakdown

Total INP = Input Delay + Processing Time + Presentation Delay

PhaseTargetOptimization
Input Delay< 50msReduce main thread blocking
Processing< 100msOptimize event handlers
Presentation< 50msMinimize rendering work

Common INP issues

1. Long tasks blocking main thread

javascript
// ❌ Long synchronous task
function processLargeArray(items) {
  items.forEach((item) => expensiveOperation(item))
}

// ✅ Break into chunks with yielding
async function processLargeArray(items) {
  const CHUNK_SIZE = 100
  for (let i = 0; i < items.length; i += CHUNK_SIZE) {
    const chunk = items.slice(i, i + CHUNK_SIZE)
    chunk.forEach((item) => expensiveOperation(item))

    // Yield to main thread
    await new Promise((r) => setTimeout(r, 0))
    // Or use scheduler.yield() when available
  }
}

2. Heavy event handlers

javascript
// ❌ All work in handler
button.addEventListener('click', () => {
  // Heavy computation
  const result = calculateComplexThing()
  // DOM updates
  updateUI(result)
  // Analytics
  trackEvent('click')
})

// ✅ Prioritize visual feedback
button.addEventListener('click', () => {
  // Immediate visual feedback
  button.classList.add('loading')

  // Defer non-critical work
  requestAnimationFrame(() => {
    const result = calculateComplexThing()
    updateUI(result)
  })

  // Use requestIdleCallback for analytics
  requestIdleCallback(() => trackEvent('click'))
})

3. Third-party scripts

javascript
// ❌ Eagerly loaded, blocks interactions
;<script src="https://heavy-widget.com/widget.js"></script>

// ✅ Lazy loaded on interaction or visibility
const loadWidget = () => {
  import('https://heavy-widget.com/widget.js').then((widget) => widget.init())
}
button.addEventListener('click', loadWidget, { once: true })

4. Excessive re-renders (React/Vue)

javascript
// ❌ Re-renders entire tree
function App() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <Counter count={count} />
      <ExpensiveComponent /> {/* Re-renders on every count change */}
    </div>
  )
}

// ✅ Memoized expensive components
const MemoizedExpensive = React.memo(ExpensiveComponent)

function App() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <Counter count={count} />
      <MemoizedExpensive />
    </div>
  )
}

INP optimization checklist

markdown
- [ ] No tasks > 50ms on main thread
- [ ] Event handlers complete quickly (< 100ms)
- [ ] Visual feedback provided immediately
- [ ] Heavy work deferred with requestIdleCallback
- [ ] Third-party scripts don't block interactions
- [ ] Debounced input handlers where appropriate
- [ ] Web Workers for CPU-intensive operations

INP debugging

javascript
// Identify slow interactions
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.duration > 200) {
      console.warn('Slow interaction:', {
        type: entry.name,
        duration: entry.duration,
        processingStart: entry.processingStart,
        processingEnd: entry.processingEnd,
        target: entry.target,
      })
    }
  }
}).observe({ type: 'event', buffered: true, durationThreshold: 16 })

CLS: Cumulative Layout Shift

CLS measures unexpected layout shifts. A shift occurs when a visible element changes position between frames without user interaction.

CLS Formula: impact fraction × distance fraction

Common CLS causes

1. Images without dimensions

html
<!-- ❌ Causes layout shift when loaded -->
<img src="photo.jpg" alt="Photo" />

<!-- ✅ Space reserved -->
<img src="photo.jpg" alt="Photo" width="800" height="600" />

<!-- ✅ Or use aspect-ratio -->
<img src="photo.jpg" alt="Photo" style="aspect-ratio: 4/3; width: 100%;" />

2. Ads, embeds, and iframes

html
<!-- ❌ Unknown size until loaded -->
<iframe src="https://ad-network.com/ad"></iframe>

<!-- ✅ Reserve space with min-height -->
<div style="min-height: 250px;">
  <iframe src="https://ad-network.com/ad" height="250"></iframe>
</div>

<!-- ✅ Or use aspect-ratio container -->
<div style="aspect-ratio: 16/9;">
  <iframe src="https://youtube.com/embed/..." style="width: 100%; height: 100%;"></iframe>
</div>

3. Dynamically injected content

javascript
// ❌ Inserts content above viewport
notifications.prepend(newNotification)

// ✅ Insert below viewport or use transform
const insertBelow = viewport.bottom < newNotification.top
if (insertBelow) {
  notifications.prepend(newNotification)
} else {
  // Animate in without shifting
  newNotification.style.transform = 'translateY(-100%)'
  notifications.prepend(newNotification)
  requestAnimationFrame(() => {
    newNotification.style.transform = ''
  })
}

4. Web fonts causing FOUT

css
/* ❌ Font swap shifts text */
@font-face {
  font-family: 'Custom';
  src: url('custom.woff2') format('woff2');
}

/* ✅ Optional font (no shift if slow) */
@font-face {
  font-family: 'Custom';
  src: url('custom.woff2') format('woff2');
  font-display: optional;
}

/* ✅ Or match fallback metrics */
@font-face {
  font-family: 'Custom';
  src: url('custom.woff2') format('woff2');
  font-display: swap;
  size-adjust: 105%; /* Match fallback size */
  ascent-override: 95%;
  descent-override: 20%;
}

5. Animations triggering layout

css
/* ❌ Animates layout properties */
.animate {
  transition:
    height 0.3s,
    width 0.3s;
}

/* ✅ Use transform instead */
.animate {
  transition: transform 0.3s;
}
.animate.expanded {
  transform: scale(1.2);
}

CLS optimization checklist

markdown
- [ ] All images have width/height or aspect-ratio
- [ ] All videos/embeds have reserved space
- [ ] Ads have min-height containers
- [ ] Fonts use font-display: optional or matched metrics
- [ ] Dynamic content inserted below viewport
- [ ] Animations use transform/opacity only
- [ ] No content injected above existing content

CLS debugging

javascript
// Track layout shifts
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (!entry.hadRecentInput) {
      console.log('Layout shift:', entry.value)
      entry.sources?.forEach((source) => {
        console.log('  Shifted element:', source.node)
        console.log('  Previous rect:', source.previousRect)
        console.log('  Current rect:', source.currentRect)
      })
    }
  }
}).observe({ type: 'layout-shift', buffered: true })

Measurement tools

Lab testing

  • Chrome DevTools → Performance panel, Lighthouse
  • WebPageTest → Detailed waterfall, filmstrip
  • Lighthouse CLI → npx lighthouse <url>

Field data (real users)

  • Chrome User Experience Report (CrUX) → BigQuery or API
  • Search Console → Core Web Vitals report
  • web-vitals library → Send to your analytics
javascript
import { onLCP, onINP, onCLS } from 'web-vitals'

function sendToAnalytics({ name, value, rating }) {
  gtag('event', name, {
    event_category: 'Web Vitals',
    value: Math.round(name === 'CLS' ? value * 1000 : value),
    event_label: rating,
  })
}

onLCP(sendToAnalytics)
onINP(sendToAnalytics)
onCLS(sendToAnalytics)

Framework quick fixes

Next.js

jsx
// LCP: Use next/image with priority
import Image from 'next/image'
;<Image src="/hero.jpg" priority fill alt="Hero" />

// INP: Use dynamic imports
const HeavyComponent = dynamic(() => import('./Heavy'), { ssr: false })

// CLS: Image component handles dimensions automatically

React

jsx
// LCP: Preload in head
;<link rel="preload" href="/hero.jpg" as="image" fetchpriority="high" />

// INP: Memoize and useTransition
const [isPending, startTransition] = useTransition()
startTransition(() => setExpensiveState(newValue))

// CLS: Always specify dimensions in img tags

Vue/Nuxt

vue
<!-- LCP: Use nuxt/image with preload -->
<NuxtImg src="/hero.jpg" preload loading="eager" />

<!-- INP: Use async components -->
<component :is="() => import('./Heavy.vue')" />

<!-- CLS: Use aspect-ratio CSS -->
<img :style="{ aspectRatio: '16/9' }" />

References

관련 작업

같은 저장소의 Skills

더 많은 Skills

모든 Skills
tech-leads-club
커뮤니티

perf-astro

Astro-specific performance optimizations for 95+ Lighthouse scores. Covers critical CSS inlining, compression, font loading, and LCP optimization. Use when optimizing Astro site performance, improving Astro Lighthouse scores, or configuring astro-critters. Do NOT use for non-Astro sites (use perf-web-optimization or core-web-vitals) or running Lighthouse audits (use perf-lighthouse).

설치 수
1
GitHub Stars
7.1천
업데이트
10월 24일
tech-leads-club
커뮤니티

tlc-spec-driven

Feature planning and implementation with 4 adaptive phases (Specify, Design, Tasks, Execute). Auto-sizes depth by complexity. Writes testable requirements in EARS notation, atomic tasks, atomic Conventional Commits, and requirement traceability. Ships deterministic Python validation scripts so structural gates are enforced by code, not memory. Features an independent Verifier (author != verifier, evidence-or-zero), a discrimination sensor, a decision log (STATE.md), a test-coverage matrix, and a

설치 수
2
GitHub Stars
7.1천
업데이트
10월 22일
tech-leads-club
커뮤니티

chrome-devtools

Browser debugging, performance profiling, and automation via Chrome DevTools MCP. Use when user says "debug this page", "take a screenshot", "check network requests", "profile performance", "inspect console errors", or "analyze page load". Do NOT use for full E2E test suites (use playwright-skill) or non-browser debugging.

설치 수
1
GitHub Stars
7천
업데이트
10월 8일
tech-leads-club
커뮤니티

coding-guidelines

Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, modifying, or reviewing code — implementation tasks, code changes, refactoring, bug fixes, or feature development. Do NOT use for architecture design, documentation, or non-code tasks.

설치 수
1
GitHub Stars
7천
업데이트
10월 8일