본문으로 건너뛰기
cccskills
무료GitHub에 공개

core-web-vitals

Optimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when the task involves `improve Core Web Vitals`, `fix LCP`, `reduce CLS`, `optimize INP`, `page experience optimization`, or `fix layout shifts`.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(2)

  • SKILL.md11.5 KB
  • references/LCP.md4.5 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

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

<!-- ❌ 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

<!-- ❌ 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

// ❌ 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

- [ ] 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

// 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

// ❌ 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

// ❌ 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

// ❌ 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)

// ❌ 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

- [ ] 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

// 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

<!-- ❌ 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

<!-- ❌ 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

// ❌ 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

/* ❌ 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

/* ❌ 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

- [ ] 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

// 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
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

// 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

// 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

<!-- 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

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Audit and improve web accessibility following WCAG 2.1 guidelines. Use when the task involves `improve accessibility`, `a11y audit`, `WCAG compliance`, `screen reader support`, `keyboard navigation`, or `make accessible`.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

dallay/agentsync702026년 10월 19일 업데이트

astro

무료

Skill for building with the Astro web framework. Helps create Astro components and pages, configure SSR adapters, set up content collections, deploy static sites, and manage project structure and CLI commands. Use when the user needs to work with Astro, mentions .astro files, asks about static site generation (SSG), islands architecture, content collections, or deploying an Astro project.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

dallay/agentsync702026년 10월 19일 업데이트

Apply modern web development best practices for security, compatibility, and code quality. Use when the task involves `apply best practices`, `security audit`, `modernize code`, `code quality review`, or `check for vulnerabilities`.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

dallay/agentsync702026년 10월 19일 업데이트

Advanced Docker containerization expert for multi-stage builds, image optimization, security hardening, and Compose orchestration. Use when the task involves `working with Dockerfile`, `docker-compose.yml`, `containerization`, `multi-stage builds`, or `optimizing Docker images`.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

dallay/agentsync702026년 10월 19일 업데이트

Create distinctive, production-grade frontend interfaces with high design quality that avoids generic AI aesthetics. Use when the task involves `build web components`, `create frontend interface`, `build web page`, `build web application`, `frontend design`, `create UI component`, or `design web interface`.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

dallay/agentsync702026년 10월 19일 업데이트

Comprehensive guide for building robust, secure, and efficient CI/CD pipelines using GitHub Actions. Use when the task involves `creating or editing GitHub Actions workflows`, `.github/workflows/*.yml`, `CI/CD pipelines`, `GitHub Actions best practices`, or `workflow optimization`.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

dallay/agentsync702026년 10월 19일 업데이트

dallay의 스킬 모두 보기

이 스킬의 문제 신고