Module 04 advanced 25 min

Visual Stability — CLS

Layout shift sources, space reservation, fonts, embeds, transform-only motion, and bfcache interactions.

  • CLS

CLS measures unexpected layout movement. User-initiated shifts within 500 ms often don’t count — but ads, fonts, and late images still ruin scores.

How CLS is calculated

Each shift has:

  • Impact fraction — how much of viewport moved
  • Distance fraction — how far elements traveled

CLS = sum of impact × distance for unexpected shifts in the session (with session window rules for SPAs).

Good ≤ 0.1 · Poor > 0.25

Common shift sources

SourceFix
Images without dimensionswidth/height or aspect-ratio
Web fontssize-adjust fallbacks, font-display: optional
Ads/embedsReserved slot with min-height
Late-injected bannersReserve space in skeleton
Animationstransform not top/height
insertBefore above contentInsert below fold or reserve

CLS Playground — reserve space or let it shift

Content below will jump when the image loads without reserved space.

This paragraph shifts when layout is unstable.

CLS score: measuring… (interact to trigger load)

Space reservation patterns

<!-- Always -->
<img src="/photo.jpg" width="800" height="600" alt="…">

<!-- Or CSS -->
.hero-media {
  aspect-ratio: 16 / 9;
  width: 100%;
}
<div class="ad-slot" style="min-height: 250px;">
  <!-- ad loads here -->
</div>

Transform-only animations

Properties that trigger layout (expensive + shift risk): width, height, top, left, margin.

Compositor-friendly: transform, opacity.

.panel {
  transform: translateY(100%);
  transition: transform 0.3s ease;
}
.panel.is-open {
  transform: translateY(0);
}

Fonts & CLS

FOIT/FOUT swaps change text metrics → shift.

  1. Preload critical fonts
  2. Match fallback metrics (size-adjust, ascent-override, descent-override)
  3. Consider font-display: optional for body text

Embeds & iframes

<iframe
  src="https://…"
  width="560"
  height="315"
  title="Video"
  loading="lazy"
></iframe>

For dynamic embeds, use aspect-ratio wrapper:

.embed { aspect-ratio: 16 / 9; width: 100%; }
.embed iframe { width: 100%; height: 100%; border: 0; }

CLS + SPA navigations

Soft navigations can reset CLS session windows. Avoid injecting large blocks above existing content on route change without skeleton placeholders.

bfcache note

Back/forward cache restores pages instantly. Some APIs break bfcache eligibility (unload listeners, open WebSockets) — see Module 07. CLS is measured per full navigation session.

Checklist

  • All media have explicit dimensions
  • Ad/embed slots reserved
  • Animations use transform/opacity
  • Font fallbacks metric-matched
  • RUM attribution shows which elements shifted

Next: Module 05 — rendering pipeline and layout thrashing.

Live on this page

TTFB
FCP
LCP
INP
CLS