Layout Movement (CLS)
What is Cumulative Layout Shift (CLS)?
Cumulative Layout Shift (CLS) is one of Google's three Core Web Vitals metrics and measures the visual stability of a webpage. CLS quantifies how often unexpected layout shifts occur during the entire lifecycle of a page.
Definition and Significance
CLS measures the sum of all layout shifts that are not caused by user interactions. These shifts occur when visible elements change their position after they have already been rendered.
Key Metrics:
- Good: CLS ≤ 0.1
- Needs Improvement: 0.1 < CLS ≤ 0.25
- Poor: CLS > 0.25
How is CLS Calculated?
Calculation Formula
CLS is calculated using the following formula:
CLS = Σ (Impacted Space × Displacement Distance)
Impact Fraction: Proportion of the viewport affected by the layout shift
Distance Fraction: Largest distance an element was shifted (relative to viewport size)
Measurement Timing
CLS is measured throughout the entire lifetime of a page, not just during initial load. This includes:
- Initial loading
- Dynamic content
- User interactions
- Asynchronous resources
Common Causes of Layout Shifts
1. Images Without Dimensions
Problem: Images are loaded without fixed width and height, causing the layout to jump once the image loads.
Solution:
<!-- Bad -->
<img src="image.jpg" alt="Description">
<!-- Good -->
<img src="image.jpg" alt="Description" width="800" height="600">
2. Dynamically Inserted Content
Problem: Content is added after initial load and shifts existing elements.
Examples:
- Ad banners
- Social media widgets
- Comments
- Recommendations
3. Web Fonts Without Fallback
Problem: Web fonts are loaded asynchronously and replace fallback fonts, causing text sizes and spacing to change.
Solution:
@font-face {
font-family: 'Custom Font';
font-display: swap; /* Prevents FOIT */
src: url('font.woff2') format('woff2');
}
4. Asynchronous Scripts
Problem: JavaScript loads content asynchronously and shifts the layout.
Examples:
- Analytics tracking
- Chat widgets
- Cookie banners
CLS Optimization Strategies
1. Optimize Images
Reserved Space for Images:
<div class="image-container" style="aspect-ratio: 16/9;">
<img src="image.jpg" alt="Description" loading="lazy">
</div>
CSS Solution:
.image-container {
width: 100%;
height: 0;
padding-bottom: 56.25%; /* 16:9 Aspect Ratio */
position: relative;
}
.image-container img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
2. Optimize Web Fonts
Font Display Strategies:
@font-face {
font-family: 'Custom Font';
font-display: swap; /* Faster text visibility */
src: url('font.woff2') format('woff2');
}
/* Define fallback font */
body {
font-family: 'Custom Font', Arial, sans-serif;
}
3. Prepare Dynamic Content
Reserved Space for Dynamic Content:
<div class="content-area">
<div class="placeholder" style="height: 200px;">
<!-- Content will be inserted here -->
</div>
</div>
4. Use CSS Transformations
Instead of Layout Changes:
/* Bad - causes layout shift */
.element {
margin-top: 20px;
}
/* Good - uses transform */
.element {
transform: translateY(20px);
}
CLS Measurement and Monitoring
Tools for CLS Measurement
1. Google PageSpeed Insights
- Lab data for quick tests
- Real-world data from Chrome User Experience Report
2. Google Search Console
- Core Web Vitals report
- Field data for real users
3. Chrome DevTools
- Performance tab
- Layout Shift records
4. Web Vitals Extension
- Real-time measurement
- Instant feedback
Using Chrome DevTools
Step-by-Step Guide:
- Open DevTools (F12)
- Select Performance tab
- Click Record button
- Load page and interact
- Press Stop
- Search for Layout Shifts in the timeline
Analyze Layout Shift Records:
- Cumulative Score: Total CLS value
- Sources: Cause of the shift
- Nodes: Affected DOM elements
Best Practices for CLS Optimization
1. Define Dimensions for All Media
<!-- Images -->
<img src="image.jpg" width="800" height="600" alt="Description">
<!-- Videos -->
<video width="800" height="450" controls>
<source src="video.mp4" type="video/mp4">
</video>
<!-- Iframes -->
<iframe src="embed.html" width="800" height="600"></iframe>
2. Use CSS Containers
.media-container {
position: relative;
width: 100%;
height: 0;
padding-bottom: 56.25%; /* 16:9 */
overflow: hidden;
}
.media-container img,
.media-container video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
3. Optimize Font Loading
<!-- Preload important fonts -->
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<!-- Font display swap -->
<style>
@font-face {
font-family: 'Custom Font';
font-display: swap;
src: url('font.woff2') format('woff2');
}
</style>
4. Prepare Dynamic Content
/* Placeholder for dynamic content */
.dynamic-content {
min-height: 200px;
background: #f5f5f5;
}
/* Skeleton Loading */
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
@keyframes loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
CLS Debugging Techniques
1. Layout Shift Debugging
// Enable CLS measurement
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
console.log('Layout Shift:', entry.value);
console.log('Sources:', entry.sources);
}
}
}).observe({type: 'layout-shift', buffered: true});
2. Visual Debugging Tools
Chrome DevTools:
- Rendering tab → Enable "Layout Shift Regions"
- Performance tab → Analyze Layout Shift records
Browser Extensions:
- Web Vitals Extension
- Lighthouse CI
3. Automated Tests
// Puppeteer/Playwright Test
const { chromium } = require('playwright');
async function measureCLS() {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
const cls = await page.evaluate(() => {
return new Promise((resolve) => {
let clsValue = 0;
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
}
resolve(clsValue);
}).observe({type: 'layout-shift', buffered: true});
});
});
console.log('CLS Score:', cls);
await browser.close();
}
Common CLS Errors and Solutions
1. Cookie Banner Without Reserved Space
Problem: Cookie banner appears suddenly and shifts content.
Solution:
/* Reserve space for cookie banner */
body {
padding-bottom: 60px; /* Height of cookie banner */
}
.cookie-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 60px;
transform: translateY(100%);
transition: transform 0.3s ease;
}
.cookie-banner.show {
transform: translateY(0);
}
2. Lazy Loading Without Dimensions
Problem: Lazy-loaded images shift layout when loading.
Solution:
<div class="image-wrapper" style="aspect-ratio: 16/9;">
<img
src="placeholder.jpg"
data-src="real-image.jpg"
alt="Description"
loading="lazy"
class="lazy-image"
>
</div>
3. Dynamic Advertising
Problem: Ad banners load asynchronously and shift content.
Solution:
<!-- Reserved space for advertising -->
<div class="ad-container" style="height: 250px; background: #f0f0f0;">
<div id="ad-slot"></div>
</div>
<script>
// Only load ad when container is visible
const adContainer = document.getElementById('ad-slot');
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadAd();
observer.disconnect();
}
});
observer.observe(adContainer);
</script>
CLS and SEO Impact
Ranking Factor Since 2021
CLS has been an official Google ranking factor since May 2021 and is part of Core Web Vitals. Poor CLS values can lead to:
- Ranking losses in search results
- Reduced visibility in Google Search Console
- Poor user experience and higher Quick Exit Rate
- Loss of conversions and engagement
Mobile-First Indexing
Since Google indexes mobile-first, CLS is particularly important for:
- Mobile performance and user experience
- Mobile search rankings
- Core Web Vitals on mobile devices
CLS Monitoring Strategies
1. Continuous Monitoring
Tools for Continuous Monitoring:
- Google Search Console
- PageSpeed Insights API
- Custom analytics integration
- Real User Monitoring (RUM)
2. Alerts and Notifications
// CLS alert system
function checkCLS() {
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.value > 0.25) {
// Send alert
sendAlert('CLS Warning', `CLS: ${entry.value}`);
}
}
}).observe({type: 'layout-shift', buffered: true});
}
3. A/B Testing for CLS
Test Design for CLS Optimization:
- Variant A: Original layout
- Variant B: Optimized layout with reserved spaces
- Metrics: CLS, bounce rate, Goal Rate
Future of CLS Measurement
Advanced CLS Metrics
Google is working on advanced CLS metrics:
- CLS 2.0: Improved measurement of layout shifts
- Subframe CLS: Measurement in iframes
- Windowed CLS: Measurement in specific time windows
New Optimization Opportunities
CSS Container Queries:
@container (min-width: 300px) {
.content {
font-size: 1.2rem;
}
}
CSS Scroll Timeline:
@scroll-timeline scroll-timeline {
source: selector(#scroll-container);
orientation: vertical;
}
CLS Optimization Checklist
✅ Technical Checklist
- All images have fixed dimensions
- Web fonts use
font-display: swap - Dynamic content has reserved space
- CSS transformations instead of layout changes
- Lazy loading with aspect ratio
- Cookie banner without layout shift
- Advertising with fixed dimensions
✅ Monitoring Checklist
- Monitor Google Search Console
- Test PageSpeed Insights regularly
- Use Chrome DevTools Performance tab
- Implement Real User Monitoring
- Set up alerts for CLS thresholds
✅ Content Checklist
- All media files optimized
- Font loading strategy defined
- Dynamic content prepared
- Mobile-first approach implemented
- User experience prioritized
Last Updated: October 21, 2025