← Clarigital·Clarity in Digital Marketing
Technical SEO - Session 2, Guide 3

CLS Optimisation - The Complete Technical Guide

How to diagnose and eliminate Cumulative Layout Shift — the Core Web Vital measuring visual stability. Every cause explained: unsized images, ad slots, web font FOUT, dynamic content injection, and layout-triggering CSS animations — with specific code fixes for each.

Technical SEO2,600 wordsUpdated Apr 2026

What You Will Learn

  • How the CLS score is calculated using impact fraction and distance fraction
  • Why images without width/height attributes are the most common CLS cause
  • How to reserve space for ads, embeds, and iframes to prevent unexpected shifts
  • How web font loading causes FOUT/FOIT and three strategies to prevent it
  • How to handle dynamically injected content (banners, notifications) without causing CLS
  • Which CSS animation properties cause layout shifts and how to avoid them
  • How to use Chrome DevTools Layout Shift Regions to find CLS sources

What is Cumulative Layout Shift

Cumulative Layout Shift (CLS) measures visual stability — how much page content unexpectedly moves during loading or use. A high CLS score causes accidental taps on wrong elements and significant user frustration. Unlike LCP and INP, CLS is a dimensionless score calculated from the geometry of unexpected layout shifts, not a time measurement.

A score of 0 means no unexpected shifts occurred. Google classifies scores of 0.1 or below as Good, 0.1–0.25 as Needs Improvement, and above 0.25 as Poor. The ranking signal uses the 75th percentile of real user sessions.

Good CLS

0.1

Target for 75th percentile of page visits

Poor CLS

0.25

Above this is a significant UX problem

Top cause

~62%

Of CLS issues are image-related (no dimensions)

How the CLS Score is Calculated

Each layout shift is scored as: layout shift score = impact fraction x distance fraction. The impact fraction is the fraction of the viewport area affected (before + after position union). The distance fraction is how far the element moved relative to the viewport's largest dimension.

Google uses the maximum "session window" — the highest-scoring 5-second burst of layout shifts — as the reported CLS value. Layout shifts within 500ms of a user interaction (click, tap, keypress) are excluded because they are expected, intentional shifts.

User-initiated shifts do not count toward CLS

Accordion expansions, "read more" reveals, and any content change within 500ms of a user interaction are excluded. Only unexpected shifts — those that happen without a user trigger — are penalised.

Fix 1 — Images Without Dimensions

The most common and highest-impact CLS fix: add explicit width and height attributes to every img and video element. Without declared dimensions, the browser allocates zero space initially. When the image loads, it pushes surrounding content down — creating a layout shift.

1
Add width and height to all images
High Impact

Add width and height matching the image's intrinsic dimensions. Use CSS to keep images responsive:

<!-- Before: causes shift -->
<img src="hero.jpg" alt="Hero">

<!-- After: reserves space -->
<img src="hero.jpg" alt="Hero" width="1200" height="600">
/* CSS: keeps images fluid */
img { max-width: 100%; height: auto; }

Modern browsers use declared width and height to calculate the aspect ratio before the image loads, reserving the exact space needed. This eliminates the shift entirely.

2
Use CSS aspect-ratio for dynamic images
High Impact

For CMS-generated or user-uploaded images where you cannot add explicit dimensions, use CSS aspect-ratio:

.article-image {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

Fix 2 — Ads, Embeds, and Iframes

Ads loaded asynchronously are one of the largest CLS contributors on commercial sites. Ad slots inject content after initial layout — without reserved space, the ad pushes surrounding content down when it loads.

1
Reserve minimum height for ad slots
High Impact
.ad-slot {
  min-height: 250px; /* IAB medium rectangle */
  background: var(--g50); /* Optional placeholder */
}

For leaderboards (728x90), reserve min-height: 90px. For sidebar rectangles (300x250), reserve 250px. This ensures content below does not shift if a smaller ad loads.

2
Set width and height on all iframes
High Impact
<!-- Fixed-size iframe -->
<iframe src="..." width="560" height="315" 
        title="Video" loading="lazy"></iframe>

<!-- Responsive iframe -->
<div style="aspect-ratio: 16/9; width: 100%;">
  <iframe src="..." 
          style="width:100%;height:100%;border:0;"></iframe>
</div>

Fix 3 — Web Font Layout Shifts

Web fonts cause CLS when the fallback font (used while the web font loads) has different metrics from the intended font. When the web font loads and replaces the fallback, the text reflows — causing a layout shift called FOUT (Flash of Unstyled Text).

1
Use font-display: optional
High Impact
@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter-Regular.woff2') format('woff2');
  font-display: optional; /* No swap = no CLS */
}

font-display: optional gives the web font a very short loading window. If not loaded, browser uses the fallback for the entire page visit with no swap. Returning visitors with the font cached see zero delay.

2
Calibrate fallback font metrics with size-adjust
Medium Impact
@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial');
  size-adjust: 94%;
  ascent-override: 92%;
  descent-override: 24%;
}
body { font-family: 'Inter', 'Inter Fallback', sans-serif; }

This makes the fallback font occupy the same space as Inter, making the swap visually imperceptible even with font-display: swap.

Fix 4 — Dynamic Content Injection

Cookie consent banners, newsletter bars, notification popups, and promotional banners injected above content after load are major CLS sources on publisher and e-commerce sites.

1
Position banners at the bottom, not the top
High Impact
.cookie-banner {
  position: fixed;
  bottom: 0;  /* Expands into empty space */
  left: 0;
  right: 0;
  z-index: 100;
  /* Zero CLS — does not shift existing content */
}
2
Reserve space for expected banners in initial HTML
Medium Impact
<!-- Reserve space in initial HTML for known banner -->
<div class="banner-slot" style="min-height:56px;" 
     aria-hidden="true"></div>

Fix 5 — CSS Animations That Cause Layout Shifts

Animating layout properties (top, left, width, height, margin, padding) forces layout recalculation and causes shifts. Use only compositor-only properties (transform, opacity) for animations.

PropertyTriggers LayoutAlternative
top, left, right, bottomYes — CLS risktransform: translateX() / translateY()
width, heightYes — CLS risktransform: scale()
margin, paddingYes — CLS risktransform: translate()
transformNo — compositorAlways preferred
opacityNo — compositorAlways preferred
/* Bad: triggers layout */
@keyframes slideIn {
  from { left: -100px; }
  to { left: 0; }
}

/* Good: compositor-only, no CLS */
@keyframes slideIn {
  from { transform: translateX(-100px); }
  to { transform: translateX(0); }
}

Authentic Sources

OfficialGoogle web.dev - Cumulative Layout Shift

Official CLS definition, score calculation, and thresholds.

OfficialGoogle web.dev - Optimise CLS

Official guide covering all major CLS causes and fixes.

OfficialGoogle web.dev - Web Font Best Practices

font-display values and size-adjust technique for CLS prevention.

OfficialGoogle web.dev - Debug Layout Shifts

Chrome DevTools Layout Shift Regions and Performance panel usage.

600 guides on digital marketing. All authentic sources.

Official documentation and academic research only.