HTML Tutorial 0/139 lessons ~6 min read Lesson 100

    Cumulative Layout Shift (CLS)

    cumulative layout shift (cls) cumulative layout shift captures visual instability from layout changes. chrome cumulative layout shift (cls) quantifies

    Course progress0%
    Focus
    18 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Cumulative Layout Shift (CLS) quantifies unexpected layout movement during page lifetime. The Layout Instability API in Chrome measures shift scores (impact fraction × distance fraction); Core Web Vitals requires CLS ≤ 0.1 at p75. The Chrome team explains CLS on web.dev — most fixes are HTML attributes (width/height on img) and font metrics, not CSS tweaks alone.

    CLS is the metric marketing blames when "the page jumped" — engineers fix it at DOM construction and resource load boundaries.

    Business problem

    Business pressure: Ad slot injected without reserved height pushed "Confirm order" button at click moment — users double-charged, chargebacks spiked. CLS 0.42 on checkout CrUX. Legal involved; fix was aspect-ratio container in HTML template, not faster API.

    • Conversion: web.dev documents mis-clicks on shifting buttons — direct revenue loss on mobile.
    • Compliance: WCAG 2.2.1 Timing Adjustable — shifting targets harm motor-impaired users.
    • SEO: CLS Poor in Search Console CWV report — executive visibility alongside LCP/INP.

    Why this feature exists

    Platform motivation: Users hate content jumping while reading or tapping. Quantifying layout instability enables field measurement (CrUX) and accountability — previously anecdotal "janky layout" complaints.

    • History: Layout Instability API → CLS in CWV 2020; session window rules refined 2021 per web.dev updates.
    • Alternative rejected: Manual QA "does it jump?" — not scalable, not correlated with revenue.
    • Modern role: CLS pairs with explicit size reservation in HTML — width, height, aspect-ratio.

    Browser internals

    Inside the engine: Layout Instability API fires entries when visible elements change position between frames without recent user input (500ms exclusion window). Had recent input shifts excluded from CLS. Engine tracks previous and current rects; calculates score per shift; accumulates in session windows.

    • Impact fraction: Unstable area / viewport area.
    • Distance fraction: Max move distance / viewport dimension.
    • Root causes: Unsized images, fonts, ads, dynamic DOM insert above viewport content.
    • DevTools: Experience → Layout shifts panel shows culprits and scores.
    text
    Shift score = impact fraction × distance fraction
    Session window (max 5s, gap 1s) accumulates shifts
    CLS = sum of scores in worst window
    Common HTML fixes:
    <img width="800" height="600" ...>
    <video width="640" height="360" ...>
    <link rel="preload" as="font" ...>
    font-display: optional | swap + size-adjust

    Rendering workflow

    Rendering path: CLS occurs at layout stage when geometry changes without user-initiated navigation. Image decode without reserved space expands layout; web font swap changes text metrics; iframe/ad load pushes siblings.

    • Critical path: Reserve LCP element space in first HTML — prevents LCP + CLS dual failure.
    • Layout: Each unsized embed triggers potential shift at load complete.
    • Paint: Not separate from CLS — shift detected at layout commit between frames.

    Feature deep dive

    CLS prevention checklist from web.dev: always include width and height on img/video; reserve ad/embed space; avoid inserting content above existing content unless user action; prefer transform animation; use font-display and metric overrides.

    • aspect-ratio CSS: Backup when only one dimension known — pair with HTML width/height.
    • Skeleton placeholders: Same dimensions as final content — HTML/CSS contract.
    • Dynamic banners: Insert below fold or in reserved slot — not above hero.
    • Animations: prefer-transform over height/width animations causing layout.
    html
    <img src="/product.webp" width="400" height="400" alt="Product">
    <div class="ad-slot" style="min-height:250px" aria-label="Advertisement">
    <!-- ad loads here without shifting page -->
    </div>

    Accessibility analysis

    A11y architecture: Layout shift moves focus targets and reading position for zoom users. Screen reader virtual cursor may point to wrong element after shift — disorienting. Stable layout is accessibility requirement, not polish.

    • Screen readers: Content reorder mid-read breaks comprehension — especially news articles.
    • Keyboard: Focused button moves under click — activates wrong control.
    • WCAG: 1.4.10 Reflow related; 2.5.2 Pointer Cancellation — shifting targets violate spirit of operable UI.

    SEO impact

    SEO architecture: CLS in CrUX affects page experience evaluation. Content jumping on landing pages increases bounce — engagement signal. No separate "CLS ranking factor" name, but CWV bundle matters in Search Console.

    • Crawl: Indirect via UX; WRS sees layout for rendering.
    • Rich results: Stable price display matches schema — trust signal.
    • Core Web Vitals: CLS ≤ 0.1 Good threshold at p75.

    Security considerations

    Security boundary: Third-party ad iframes without reserved space are both CLS and malvertising vector. Sandboxed iframe dimensions in HTML contain layout blast radius.

    • XSS: Injected banner HTML shifts page — user confusion aids phishing.
    • CSP: frame-src limits unexpected embed shifts.
    • Clickjacking: CLS + overlay — user clicks shifted legitimate button covering malicious layer.

    Performance impact

    Performance: CLS optimization often improves LCP predictability too — sized hero loads into fixed box. web.dev CLS guide is primary reference. RUM should report CLS with web-vitals library matching CrUX methodology.

    • LCP: Sized LCP element avoids late expansion shift.
    • INP: Independent metric — but same DOM inserts may hurt both.
    • CLS: Fix at HTML source — dimensions, slot reservation, font strategy.

    Real production example

    Production pattern: CNN and major publishers enforce min-height ad slots in HTML templates — CLS dropped from Poor to Good band in CrUX after industry-wide adoption. Pattern codified in web.dev CLS case studies and Chrome DevTools layout shift debugging workflow.

    • Pattern: ad slot min-height + placeholder skeleton in SSR HTML.
    • Monitoring: web-vitals.js CLS + Layout Shift Regions in DevTools filmstrip.
    • Fix: font-display:optional eliminated font swap shift — trade brief FOIT for stability.

    Enterprise usage

    Enterprise: CMS required fields: image width/height on every upload. Automated lint fails publish if img lacks dimensions. Ad ops uses fixed IAB sizes mapped to HTML slots.

    • Design system: Image component requires aspect ratio prop — emits HTML attributes.
    • CMS: Embed whitelist with fixed dimensions per provider.
    • CI gates: Lighthouse CLS assertion < 0.1 on checkout template.

    Common production failures

    What breaks in prod: Cookie banner injected at top without transform animation — pushed entire page down 120px, CLS 0.35, CrUX Poor globally. Fix: position fixed banner or reserve space with empty div in initial HTML.

    • Incident: A/B test hero swapped aspect ratio — CLS spike on variant B only — hard to debug without RUM segmentation.
    • SEO regression: Poor CLS on top landing pages for holiday campaign.
    • Perf regression: Removed skeleton loaders "for cleaner UI" — CLS returned.

    Architecture review questions

    • Do all img/video/iframe in templates have explicit dimensions?
    • Are ads and third-party embeds in reserved min-height slots?
    • What is font-display strategy — swap causing shift?
    • CrUX CLS p75 vs lab — dynamic content explanation?
    • Do we insert banners above content after load without reservation?
    • DevTools Layout Shift Regions — worst URL culprits identified?

    Hands-on project

    Project: Build two versions of article page: (A) unsized images, (B) width/height + aspect-ratio. Measure CLS in Lighthouse and DevTools Experience panel. Document score delta.

    • Deliverable: Filmstrip showing shifts vs stable load.
    • Verify: web.dev debug CLS; Search Console if available.
    • Stretch: Add cookie banner without layout shift using fixed positioning.

    Interview questions

    How is CLS calculated?(Intermediate)

    Layout Instability API measures shift when elements move between frames. Score = impact fraction × distance fraction. Shifts accumulate in session windows; CLS is max window total. User input excludes shifts within 500ms per web.dev spec.

    Follow-up: Are all shifts bad?

    Top three HTML fixes for CLS?(Advanced)

    width and height on img/video; reserve space for ads/embeds with min-height containers; avoid inserting content above viewport unless user triggered. Preload fonts with font-display and size-adjust to reduce metric shift.

    Follow-up: Does transform animation cause CLS?

    CLS Good in lab but Poor in CrUX — why?(Advanced)

    Ads/third parties load only in production; geo-specific banners; slower networks cause late resource layout; A/B tests; user scroll before shift excluded differently. Segment RUM by template and country.

    Follow-up: Role of iframe without dimensions?

    Try it yourself

    Edit the HTML, CSS, or JS panels — the preview updates as you type.

    Try it yourself

    Preview

    Summary

    Cumulative Layout Shift captures visual instability from layout changes. Chrome team web.dev documentation ties CLS fixes to HTML dimension attributes and reserved embed slots — validated in CrUX field data and RUM.

    Ready to mark this lesson complete?Track your journey across the entire course.