Back to Blog

Core Web Vitals Architecture: Engineering for LCP, INP, and CLS

May 22, 2026112 min read0 views

Core Web Vitals Architecture: Engineering for LCP, INP, and CLS

Core Web Vitals directly impact user experience and search rankings. Understanding the browser rendering pipeline, compositor thread mechanics, layout shift detection algorithms, and interaction timing measurement is essential for building high-performance web applications.

Browser Rendering Pipeline Deep Dive

┌─────────────────────────────────────────────────────────────────────────┐
│                    Browser Rendering Pipeline                           │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  Main Thread                                                            │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                  │   │
│  │  Parse ──▶ Style ──▶ Layout ──▶ Paint ──▶ Composite             │   │
│  │   │         │         │         │         │                      │   │
│  │   │         │         │         │         │                      │   │
│  │  DOM      CSSOM     Layout   Paint      Layer                   │   │
│  │  Tree      Tree     Tree    Records    Tree                      │   │
│  │                                                                  │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  Compositor Thread                                                      │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                  │   │
│  │  Tiling ──▶ Rasterize ──▶ Draw ──▶ Display                      │   │
│  │   │           │           │         │                            │   │
│  │  Split     GPU Work     Quads     V-Sync                        │   │
│  │  Layers                                                          │   │
│  │                                                                  │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  Timeline                                                               │
│  ├──────────────────────────────────────────────────────────────────┤  │
│  │  FCP          LCP          INP events          CLS shifts        │  │
│  │   │            │              │                   │              │  │
│  │   ▼            ▼              ▼                   ▼              │  │
│  │  First      Largest       Input             Layout              │  │
│  │  Paint      Content       Delay             Instability          │  │
│  │                                                                  │  │
│  └──────────────────────────────────────────────────────────────────┘  │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

LCP (Largest Contentful Paint) Optimization

LCP Element Detection Algorithm

// LCP measurement and optimization
class LCPOptimizer {
  private observer: PerformanceObserver | null = null;
  private lcpCandidates: LCPCandidate[] = [];
  private finalLCP: LCPCandidate | null = null;

  startObserving(): void {
    this.observer = new PerformanceObserver((list) => {
      const entries = list.getEntries() as LargestContentfulPaint[];

      for (const entry of entries) {
        this.lcpCandidates.push({
          element: entry.element,
          startTime: entry.startTime,
          renderTime: entry.renderTime,
          loadTime: entry.loadTime,
          size: entry.size,
          url: entry.url,
          id: entry.id
        });
      }
    });

    this.observer.observe({
      type: 'largest-contentful-paint',
      buffered: true
    });

    // Finalize LCP on user interaction or visibility change
    ['keydown', 'click', 'scroll'].forEach(type => {
      addEventListener(type, () => this.finalizeLCP(), { once: true });
    });

    document.addEventListener('visibilitychange', () => {
      if (document.visibilityState === 'hidden') {
        this.finalizeLCP();
      }
    });
  }

  private finalizeLCP(): void {
    if (this.finalLCP) return;

    this.observer?.disconnect();

    // Last candidate is the final LCP
    this.finalLCP = this.lcpCandidates[this.lcpCandidates.length - 1];

    // Analyze LCP breakdown
    if (this.finalLCP) {
      this.analyzeLCPBreakdown(this.finalLCP);
    }
  }

  private analyzeLCPBreakdown(lcp: LCPCandidate): LCPBreakdown {
    // Get navigation timing
    const navEntry = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;

    // Calculate time breakdown
    const ttfb = navEntry.responseStart - navEntry.requestStart;
    const resourceLoadDelay = lcp.loadTime ? lcp.loadTime - navEntry.responseEnd : 0;
    const resourceLoadDuration = lcp.loadTime && lcp.url
      ? this.getResourceLoadDuration(lcp.url)
      : 0;
    const elementRenderDelay = lcp.renderTime - (lcp.loadTime || navEntry.responseEnd);

    return {
      ttfb,
      resourceLoadDelay,
      resourceLoadDuration,
      elementRenderDelay,
      total: lcp.renderTime,

      // Optimization recommendations
      recommendations: this.generateRecommendations({
        ttfb,
        resourceLoadDelay,
        resourceLoadDuration,
        elementRenderDelay
      })
    };
  }

  private generateRecommendations(breakdown: TimeBreakdown): string[] {
    const recommendations: string[] = [];

    // TTFB issues
    if (breakdown.ttfb > 800) {
      recommendations.push(
        'High TTFB detected. Consider: CDN, server-side caching, edge computing'
      );
    }

    // Resource delay issues
    if (breakdown.resourceLoadDelay > 100) {
      recommendations.push(
        'LCP resource discovery is delayed. Use preload or inline critical resources'
      );
    }

    // Resource load duration
    if (breakdown.resourceLoadDuration > 500) {
      recommendations.push(
        'LCP resource is large. Optimize image size, use modern formats (WebP/AVIF)'
      );
    }

    // Render delay
    if (breakdown.elementRenderDelay > 100) {
      recommendations.push(
        'Render blocking detected. Check for render-blocking JS/CSS, font loading'
      );
    }

    return recommendations;
  }

  private getResourceLoadDuration(url: string): number {
    const resources = performance.getEntriesByType('resource') as PerformanceResourceTiming[];
    const resource = resources.find(r => r.name === url);
    return resource ? resource.responseEnd - resource.requestStart : 0;
  }
}

interface LCPCandidate {
  element: Element | null;
  startTime: number;
  renderTime: number;
  loadTime: number;
  size: number;
  url: string;
  id: string;
}

interface LCPBreakdown {
  ttfb: number;
  resourceLoadDelay: number;
  resourceLoadDuration: number;
  elementRenderDelay: number;
  total: number;
  recommendations: string[];
}

LCP Resource Prioritization

// Priority hints and resource loading optimization
class LCPResourceOptimizer {
  // Preload critical LCP resources
  injectPreloadHints(lcpResources: LCPResource[]): void {
    const head = document.head;

    for (const resource of lcpResources) {
      const link = document.createElement('link');
      link.rel = 'preload';
      link.href = resource.url;
      link.as = resource.type;

      // High priority for LCP
      link.setAttribute('fetchpriority', 'high');

      // Add crossorigin if needed
      if (resource.crossOrigin) {
        link.crossOrigin = resource.crossOrigin;
      }

      // For images, add imagesrcset and imagesizes
      if (resource.type === 'image' && resource.srcset) {
        link.setAttribute('imagesrcset', resource.srcset);
        link.setAttribute('imagesizes', resource.sizes || '100vw');
      }

      head.appendChild(link);
    }
  }

  // Server-side: Generate optimal resource hints
  generateResourceHints(pageAnalysis: PageAnalysis): string {
    const hints: string[] = [];

    // Preconnect to critical origins
    for (const origin of pageAnalysis.criticalOrigins) {
      hints.push(
        `<link rel="preconnect" href="${origin}" crossorigin>`
      );
    }

    // Preload LCP image
    if (pageAnalysis.lcpImage) {
      const img = pageAnalysis.lcpImage;
      hints.push(
        `<link rel="preload" as="image" href="${img.src}" ` +
        `imagesrcset="${img.srcset}" ` +
        `imagesizes="${img.sizes}" ` +
        `fetchpriority="high">`
      );
    }

    // Preload LCP fonts
    for (const font of pageAnalysis.lcpFonts) {
      hints.push(
        `<link rel="preload" as="font" href="${font.url}" ` +
        `type="${font.type}" crossorigin>`
      );
    }

    return hints.join('\n');
  }

  // Responsive image optimization for LCP
  generateResponsiveLCPImage(image: ImageConfig): string {
    const breakpoints = [640, 750, 828, 1080, 1200, 1920, 2048, 3840];

    const srcset = breakpoints
      .filter(bp => bp <= image.maxWidth * 2)
      .map(bp => `${image.baseUrl}?w=${bp}&q=75 ${bp}w`)
      .join(', ');

    // Generate sizes based on layout
    const sizes = this.calculateOptimalSizes(image);

    return `
      <img
        src="${image.baseUrl}?w=1200&q=75"
        srcset="${srcset}"
        sizes="${sizes}"
        alt="${image.alt}"
        width="${image.width}"
        height="${image.height}"
        fetchpriority="high"
        decoding="sync"
        loading="eager"
      />
    `;
  }

  private calculateOptimalSizes(image: ImageConfig): string {
    // Generate sizes attribute based on layout breakpoints
    const rules: string[] = [];

    // Mobile first
    rules.push('(max-width: 640px) 100vw');

    // Tablet
    if (image.layout === 'full') {
      rules.push('(max-width: 1024px) 100vw');
    } else {
      rules.push('(max-width: 1024px) 50vw');
    }

    // Desktop
    rules.push(`${image.displayWidth}px`);

    return rules.join(', ');
  }
}

interface LCPResource {
  url: string;
  type: 'image' | 'font' | 'script' | 'style';
  crossOrigin?: 'anonymous' | 'use-credentials';
  srcset?: string;
  sizes?: string;
}

INP (Interaction to Next Paint) Optimization

Event Processing Pipeline

// INP measurement and optimization
class INPOptimizer {
  private interactions: InteractionEntry[] = [];
  private eventTimings: Map<number, EventTiming> = new Map();

  startObserving(): void {
    // Observe event timing
    const observer = new PerformanceObserver((list) => {
      const entries = list.getEntries() as PerformanceEventTiming[];

      for (const entry of entries) {
        // Only track discrete events (not continuous like mousemove)
        if (!entry.interactionId) continue;

        this.recordInteraction(entry);
      }
    });

    observer.observe({
      type: 'event',
      buffered: true,
      durationThreshold: 16 // 1 frame at 60fps
    });

    // Report INP on page hide
    document.addEventListener('visibilitychange', () => {
      if (document.visibilityState === 'hidden') {
        this.reportINP();
      }
    });
  }

  private recordInteraction(entry: PerformanceEventTiming): void {
    const interactionId = entry.interactionId;

    // Get or create interaction entry
    let interaction = this.interactions.find(i => i.id === interactionId);

    if (!interaction) {
      interaction = {
        id: interactionId,
        startTime: entry.startTime,
        duration: entry.duration,
        events: [],
        breakdown: null
      };
      this.interactions.push(interaction);
    }

    // Update with longest event in this interaction
    if (entry.duration > interaction.duration) {
      interaction.duration = entry.duration;
    }

    // Record event breakdown
    interaction.events.push({
      name: entry.name,
      startTime: entry.startTime,
      processingStart: entry.processingStart,
      processingEnd: entry.processingEnd,
      duration: entry.duration,
      cancelable: entry.cancelable,
      target: this.getElementIdentifier(entry.target as Element)
    });

    // Calculate timing breakdown
    interaction.breakdown = this.calculateBreakdown(entry);
  }

  private calculateBreakdown(entry: PerformanceEventTiming): INPBreakdown {
    // Input delay: time from event timestamp to processing start
    const inputDelay = entry.processingStart - entry.startTime;

    // Processing time: event handler execution
    const processingTime = entry.processingEnd - entry.processingStart;

    // Presentation delay: time from processing end to next paint
    const presentationDelay = entry.duration - inputDelay - processingTime;

    return {
      inputDelay,
      processingTime,
      presentationDelay,
      total: entry.duration
    };
  }

  private reportINP(): void {
    if (this.interactions.length === 0) return;

    // Sort by duration
    const sorted = [...this.interactions].sort(
      (a, b) => b.duration - a.duration
    );

    // INP is the 98th percentile interaction
    const percentileIndex = Math.min(
      sorted.length - 1,
      Math.floor(sorted.length * 0.98)
    );

    const inp = sorted[percentileIndex];

    // Report with breakdown
    console.log('INP Report:', {
      value: inp.duration,
      breakdown: inp.breakdown,
      interactionCount: this.interactions.length,
      worstInteractions: sorted.slice(0, 5).map(i => ({
        duration: i.duration,
        events: i.events.map(e => e.name),
        target: i.events[0]?.target
      }))
    });
  }

  private getElementIdentifier(element: Element | null): string {
    if (!element) return 'unknown';

    const parts: string[] = [element.tagName.toLowerCase()];

    if (element.id) {
      parts.push(`#${element.id}`);
    }

    if (element.className) {
      parts.push(`.${element.className.split(' ').join('.')}`);
    }

    return parts.join('');
  }
}

interface InteractionEntry {
  id: number;
  startTime: number;
  duration: number;
  events: EventDetail[];
  breakdown: INPBreakdown | null;
}

interface EventDetail {
  name: string;
  startTime: number;
  processingStart: number;
  processingEnd: number;
  duration: number;
  cancelable: boolean;
  target: string;
}

interface INPBreakdown {
  inputDelay: number;      // Time until handler starts
  processingTime: number;  // Handler execution time
  presentationDelay: number; // Time to next paint
  total: number;
}

Input Delay Reduction Strategies

// Strategies for reducing input delay
class InputDelayOptimizer {
  // Yield to main thread to reduce input delay
  async yieldToMain(): Promise<void> {
    // scheduler.yield() is the modern API
    if ('scheduler' in globalThis && 'yield' in (globalThis as any).scheduler) {
      return (globalThis as any).scheduler.yield();
    }

    // Fallback: setTimeout with 0 delay
    return new Promise(resolve => setTimeout(resolve, 0));
  }

  // Break up long tasks
  async processInChunks<T>(
    items: T[],
    processor: (item: T) => void,
    chunkSize: number = 5
  ): Promise<void> {
    for (let i = 0; i < items.length; i += chunkSize) {
      const chunk = items.slice(i, i + chunkSize);

      // Process chunk
      for (const item of chunk) {
        processor(item);
      }

      // Yield between chunks if more work remains
      if (i + chunkSize < items.length) {
        await this.yieldToMain();
      }
    }
  }

  // Idle callback for non-critical work
  scheduleIdleWork(
    work: () => void,
    options: IdleWorkOptions = {}
  ): number {
    const deadline = options.timeout || 1000;

    return requestIdleCallback(
      (idleDeadline) => {
        if (idleDeadline.timeRemaining() > 0 || idleDeadline.didTimeout) {
          work();
        } else {
          // Reschedule if no time available
          this.scheduleIdleWork(work, options);
        }
      },
      { timeout: deadline }
    );
  }

  // Debounce expensive handlers
  createDebouncedHandler<T extends (...args: any[]) => void>(
    handler: T,
    wait: number
  ): T {
    let timeoutId: number | null = null;

    return ((...args: Parameters<T>) => {
      if (timeoutId) {
        cancelAnimationFrame(timeoutId);
      }

      timeoutId = requestAnimationFrame(() => {
        // Use double-RAF for after-paint execution
        requestAnimationFrame(() => {
          handler(...args);
        });
      });
    }) as T;
  }

  // Passive event listeners for scroll/touch
  addPassiveListener(
    element: EventTarget,
    event: string,
    handler: EventListener
  ): void {
    element.addEventListener(event, handler, { passive: true });
  }

  // Event delegation to reduce listener count
  setupDelegatedHandler(
    container: Element,
    selector: string,
    event: string,
    handler: (target: Element, event: Event) => void
  ): void {
    container.addEventListener(event, (e) => {
      const target = (e.target as Element).closest(selector);
      if (target && container.contains(target)) {
        handler(target, e);
      }
    });
  }
}

interface IdleWorkOptions {
  timeout?: number;
}

// React hook for INP-friendly state updates
function useINPFriendlyState<T>(initialValue: T): [T, (value: T | ((prev: T) => T)) => void] {
  const [state, setState] = React.useState(initialValue);
  const pendingUpdate = React.useRef<T | null>(null);

  const setStateDeferred = React.useCallback((value: T | ((prev: T) => T)) => {
    // Calculate new value immediately for optimistic UI
    const newValue = typeof value === 'function'
      ? (value as (prev: T) => T)(state)
      : value;

    pendingUpdate.current = newValue;

    // Use startTransition for non-urgent update
    React.startTransition(() => {
      setState(pendingUpdate.current as T);
      pendingUpdate.current = null;
    });
  }, [state]);

  return [state, setStateDeferred];
}

// Worker offloading for heavy computation
class ComputeWorkerPool {
  private workers: Worker[] = [];
  private taskQueue: QueuedTask[] = [];
  private idleWorkers: Worker[] = [];

  constructor(workerScript: string, poolSize: number = navigator.hardwareConcurrency || 4) {
    for (let i = 0; i < poolSize; i++) {
      const worker = new Worker(workerScript);
      worker.onmessage = (e) => this.handleWorkerMessage(worker, e);
      this.workers.push(worker);
      this.idleWorkers.push(worker);
    }
  }

  async compute<T>(taskType: string, data: any): Promise<T> {
    return new Promise((resolve, reject) => {
      const task: QueuedTask = {
        type: taskType,
        data,
        resolve,
        reject
      };

      const worker = this.idleWorkers.pop();
      if (worker) {
        this.runTask(worker, task);
      } else {
        this.taskQueue.push(task);
      }
    });
  }

  private runTask(worker: Worker, task: QueuedTask): void {
    (worker as any).currentTask = task;
    worker.postMessage({ type: task.type, data: task.data });
  }

  private handleWorkerMessage(worker: Worker, event: MessageEvent): void {
    const task = (worker as any).currentTask as QueuedTask;
    (worker as any).currentTask = null;

    if (event.data.error) {
      task.reject(new Error(event.data.error));
    } else {
      task.resolve(event.data.result);
    }

    // Process next queued task
    const nextTask = this.taskQueue.shift();
    if (nextTask) {
      this.runTask(worker, nextTask);
    } else {
      this.idleWorkers.push(worker);
    }
  }
}

interface QueuedTask {
  type: string;
  data: any;
  resolve: (value: any) => void;
  reject: (error: Error) => void;
}

CLS (Cumulative Layout Shift) Prevention

Layout Shift Detection

// CLS measurement and prevention
class CLSOptimizer {
  private shifts: LayoutShiftEntry[] = [];
  private sessionWindow: LayoutShiftEntry[] = [];
  private sessionStartTime = 0;
  private sessionValue = 0;
  private maxSessionValue = 0;

  startObserving(): void {
    const observer = new PerformanceObserver((list) => {
      const entries = list.getEntries() as LayoutShift[];

      for (const entry of entries) {
        // Ignore user-initiated shifts
        if (entry.hadRecentInput) continue;

        this.recordShift(entry);
      }
    });

    observer.observe({ type: 'layout-shift', buffered: true });
  }

  private recordShift(entry: LayoutShift): void {
    const shiftEntry: LayoutShiftEntry = {
      value: entry.value,
      startTime: entry.startTime,
      sources: entry.sources?.map(source => ({
        node: source.node,
        previousRect: source.previousRect,
        currentRect: source.currentRect,
        element: this.identifyElement(source.node)
      })) || []
    };

    this.shifts.push(shiftEntry);

    // Update session window (5 second max, 1 second gap)
    if (
      this.sessionWindow.length === 0 ||
      entry.startTime - this.sessionStartTime > 5000 ||
      entry.startTime - this.sessionWindow[this.sessionWindow.length - 1].startTime > 1000
    ) {
      // Start new session
      this.sessionWindow = [shiftEntry];
      this.sessionStartTime = entry.startTime;
      this.sessionValue = entry.value;
    } else {
      // Continue session
      this.sessionWindow.push(shiftEntry);
      this.sessionValue += entry.value;
    }

    // Track max session value (CLS)
    if (this.sessionValue > this.maxSessionValue) {
      this.maxSessionValue = this.sessionValue;
    }
  }

  getCLS(): CLSReport {
    // Find the worst shift sources
    const worstShifts = [...this.shifts]
      .sort((a, b) => b.value - a.value)
      .slice(0, 5);

    return {
      value: this.maxSessionValue,
      totalShifts: this.shifts.length,
      worstSources: worstShifts.flatMap(s => s.sources),
      recommendations: this.generateRecommendations(worstShifts)
    };
  }

  private identifyElement(node: Node | null): string {
    if (!node || !(node instanceof Element)) return 'unknown';

    let identifier = node.tagName.toLowerCase();

    if (node.id) {
      identifier += `#${node.id}`;
    } else if (node.className) {
      identifier += `.${node.className.split(' ')[0]}`;
    }

    // Add parent context
    if (node.parentElement) {
      const parent = node.parentElement.tagName.toLowerCase();
      identifier = `${parent} > ${identifier}`;
    }

    return identifier;
  }

  private generateRecommendations(worstShifts: LayoutShiftEntry[]): CLSRecommendation[] {
    const recommendations: CLSRecommendation[] = [];

    for (const shift of worstShifts) {
      for (const source of shift.sources) {
        const element = source.element;
        const prevRect = source.previousRect;
        const currRect = source.currentRect;

        // Image without dimensions
        if (element.includes('img') && prevRect.width === 0 && currRect.width > 0) {
          recommendations.push({
            element,
            issue: 'Image loaded without reserved space',
            fix: 'Add width and height attributes, use aspect-ratio CSS',
            priority: 'high'
          });
        }

        // Dynamic content injection
        if (prevRect.height === 0 && currRect.height > 0) {
          recommendations.push({
            element,
            issue: 'Content dynamically injected',
            fix: 'Reserve space with min-height or skeleton placeholder',
            priority: 'high'
          });
        }

        // Font swap causing shift
        if (element.includes('text') || element.includes('p') || element.includes('h')) {
          recommendations.push({
            element,
            issue: 'Possible font swap causing shift',
            fix: 'Use font-display: optional or size-adjust',
            priority: 'medium'
          });
        }
      }
    }

    return recommendations;
  }
}

interface LayoutShiftEntry {
  value: number;
  startTime: number;
  sources: ShiftSource[];
}

interface ShiftSource {
  node: Node | null;
  previousRect: DOMRectReadOnly;
  currentRect: DOMRectReadOnly;
  element: string;
}

interface CLSReport {
  value: number;
  totalShifts: number;
  worstSources: ShiftSource[];
  recommendations: CLSRecommendation[];
}

interface CLSRecommendation {
  element: string;
  issue: string;
  fix: string;
  priority: 'high' | 'medium' | 'low';
}

Layout Shift Prevention Techniques

// Techniques for preventing layout shifts
class LayoutShiftPreventer {
  // Reserve space for images
  createResponsiveImageWithSpace(config: ImageConfig): string {
    // Calculate aspect ratio
    const aspectRatio = (config.height / config.width) * 100;

    return `
      <div class="img-container" style="
        position: relative;
        width: 100%;
        padding-bottom: ${aspectRatio}%;
      ">
        <img
          src="${config.src}"
          srcset="${config.srcset}"
          sizes="${config.sizes}"
          alt="${config.alt}"
          width="${config.width}"
          height="${config.height}"
          loading="${config.loading || 'lazy'}"
          decoding="async"
          style="
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            object-fit: cover;
          "
        />
      </div>
    `;
  }

  // Font loading without CLS
  generateFontLoadingCSS(fonts: FontConfig[]): string {
    let css = '';

    for (const font of fonts) {
      // Calculate size-adjust to match fallback metrics
      const sizeAdjust = this.calculateSizeAdjust(font);

      css += `
        @font-face {
          font-family: '${font.family}';
          src: url('${font.src}') format('${font.format}');
          font-weight: ${font.weight || 'normal'};
          font-style: ${font.style || 'normal'};
          font-display: optional;
          size-adjust: ${sizeAdjust}%;
          ascent-override: ${font.ascentOverride || 'normal'};
          descent-override: ${font.descentOverride || 'normal'};
          line-gap-override: ${font.lineGapOverride || 'normal'};
        }
      `;
    }

    return css;
  }

  private calculateSizeAdjust(font: FontConfig): number {
    // Font-specific adjustments (simplified)
    const adjustments: Record<string, number> = {
      'inter': 107,
      'roboto': 100,
      'open-sans': 105,
      'lato': 97,
      'arial': 100
    };

    return adjustments[font.family.toLowerCase()] || 100;
  }

  // Skeleton placeholder for async content
  createSkeletonPlaceholder(config: SkeletonConfig): string {
    return `
      <div
        class="skeleton-placeholder"
        style="
          width: ${config.width || '100%'};
          height: ${config.height};
          background: linear-gradient(
            90deg,
            #f0f0f0 25%,
            #e0e0e0 50%,
            #f0f0f0 75%
          );
          background-size: 200% 100%;
          animation: skeleton-shimmer 1.5s infinite;
          border-radius: ${config.borderRadius || '4px'};
        "
        aria-hidden="true"
      ></div>
      <style>
        @keyframes skeleton-shimmer {
          0% { background-position: 200% 0; }
          100% { background-position: -200% 0; }
        }
      </style>
    `;
  }

  // Transform animations instead of layout-triggering properties
  createSafeAnimation(config: AnimationConfig): string {
    // Use compositor-only properties
    const safeProperties = ['transform', 'opacity', 'filter'];

    // Validate animation doesn't use layout-triggering properties
    const unsafeProperties = ['width', 'height', 'top', 'left', 'right', 'bottom', 'margin', 'padding'];

    for (const keyframe of config.keyframes) {
      for (const prop of Object.keys(keyframe)) {
        if (unsafeProperties.includes(prop)) {
          console.warn(
            `Animation uses layout-triggering property: ${prop}. ` +
            `Consider using transform instead.`
          );
        }
      }
    }

    return `
      @keyframes ${config.name} {
        ${config.keyframes.map((kf, i) => `
          ${(i / (config.keyframes.length - 1)) * 100}% {
            ${Object.entries(kf).map(([k, v]) => `${k}: ${v};`).join('\n')}
          }
        `).join('\n')}
      }

      .${config.className} {
        animation: ${config.name} ${config.duration}ms ${config.easing || 'ease'};
        will-change: ${safeProperties.filter(p => config.keyframes.some(kf => p in kf)).join(', ')};
      }
    `;
  }
}

interface FontConfig {
  family: string;
  src: string;
  format: string;
  weight?: string | number;
  style?: string;
  ascentOverride?: string;
  descentOverride?: string;
  lineGapOverride?: string;
}

interface SkeletonConfig {
  width?: string;
  height: string;
  borderRadius?: string;
}

interface AnimationConfig {
  name: string;
  className: string;
  duration: number;
  easing?: string;
  keyframes: Record<string, string | number>[];
}

// React component for CLS-safe dynamic content
function CLSSafeContent({
  children,
  minHeight,
  aspectRatio,
  fallback
}: {
  children: React.ReactNode;
  minHeight?: string;
  aspectRatio?: string;
  fallback?: React.ReactNode;
}) {
  const [isLoaded, setIsLoaded] = React.useState(false);
  const containerRef = React.useRef<HTMLDivElement>(null);
  const [measuredHeight, setMeasuredHeight] = React.useState<number | null>(null);

  React.useEffect(() => {
    if (containerRef.current) {
      const observer = new ResizeObserver(entries => {
        const entry = entries[0];
        if (entry && !isLoaded) {
          setMeasuredHeight(entry.contentRect.height);
        }
      });

      observer.observe(containerRef.current);
      return () => observer.disconnect();
    }
  }, [isLoaded]);

  return (
    <div
      ref={containerRef}
      style={{
        minHeight: measuredHeight ? `${measuredHeight}px` : minHeight,
        aspectRatio: aspectRatio,
        contain: 'layout'
      }}
    >
      {isLoaded ? children : fallback}
    </div>
  );
}

Performance Monitoring Pipeline

// Unified Core Web Vitals monitoring
class CoreWebVitalsMonitor {
  private metrics: Map<string, MetricValue> = new Map();

  async collectMetrics(): Promise<WebVitalsReport> {
    const [lcp, inp, cls, fcp, ttfb] = await Promise.all([
      this.getLCP(),
      this.getINP(),
      this.getCLS(),
      this.getFCP(),
      this.getTTFB()
    ]);

    return {
      lcp,
      inp,
      cls,
      fcp,
      ttfb,
      timestamp: Date.now(),
      url: window.location.href,
      deviceType: this.getDeviceType(),
      connectionType: this.getConnectionType(),
      navigationType: this.getNavigationType()
    };
  }

  private async getLCP(): Promise<MetricValue> {
    return new Promise(resolve => {
      new PerformanceObserver(list => {
        const entries = list.getEntries();
        const lastEntry = entries[entries.length - 1] as LargestContentfulPaint;

        resolve({
          value: lastEntry.renderTime || lastEntry.loadTime,
          rating: this.getRating('LCP', lastEntry.renderTime || lastEntry.loadTime),
          attribution: {
            element: lastEntry.element?.tagName,
            url: lastEntry.url,
            size: lastEntry.size
          }
        });
      }).observe({ type: 'largest-contentful-paint', buffered: true });
    });
  }

  private getRating(metric: string, value: number): 'good' | 'needs-improvement' | 'poor' {
    const thresholds: Record<string, [number, number]> = {
      'LCP': [2500, 4000],
      'INP': [200, 500],
      'CLS': [0.1, 0.25],
      'FCP': [1800, 3000],
      'TTFB': [800, 1800]
    };

    const [good, poor] = thresholds[metric];

    if (value <= good) return 'good';
    if (value <= poor) return 'needs-improvement';
    return 'poor';
  }

  // Send metrics to analytics
  async reportMetrics(report: WebVitalsReport): Promise<void> {
    // Use sendBeacon for reliability
    const payload = JSON.stringify(report);

    if (navigator.sendBeacon) {
      navigator.sendBeacon('/api/vitals', payload);
    } else {
      await fetch('/api/vitals', {
        method: 'POST',
        body: payload,
        keepalive: true
      });
    }
  }
}

interface WebVitalsReport {
  lcp: MetricValue;
  inp: MetricValue;
  cls: MetricValue;
  fcp: MetricValue;
  ttfb: MetricValue;
  timestamp: number;
  url: string;
  deviceType: string;
  connectionType: string;
  navigationType: string;
}

interface MetricValue {
  value: number;
  rating: 'good' | 'needs-improvement' | 'poor';
  attribution?: Record<string, any>;
}

Key Takeaways

  1. LCP optimization requires fast TTFB, early resource discovery, and optimized rendering
  2. Preload hints with fetchpriority="high" can dramatically improve LCP resource loading
  3. INP has three components: input delay, processing time, and presentation delay
  4. Yielding to main thread between long tasks improves input responsiveness
  5. CLS prevention requires reserving space for dynamic content before it loads
  6. Font metrics (size-adjust, ascent-override) can eliminate font-swap layout shifts
  7. Compositor-only animations (transform, opacity) don't cause layout shifts
  8. Continuous monitoring with attribution data enables targeted optimization

What did you think?

© 2026 Vidhya Sagar Thakur. All rights reserved.