It happens to everyone: you are about to tap a link and, a moment before, the text slides down because a banner has appeared above it. Cumulative Layout Shift measures exactly this annoyance. A value up to 0.1 is considered good, above 0.25 is poor. Unlike LCP and INP, CLS is not a time but a unitless score, and it can almost always be brought close to zero with HTML and CSS changes, without touching the server.
How it is calculated, briefly
Each unexpected shift gets a score given by the product of two factors: how much of the viewport is occupied by the unstable elements (impact fraction) and how far they moved relative to the viewport's height or width (distance fraction). Shifts close together in time are grouped into windows, and the page's CLS is the value of the worst window, not the sum over the whole visit.
An important detail: shifts that occur within half a second of a user interaction, such as an accordion opening after a click, are not counted. CLS penalizes what the user does not expect.
Cause 1: images and videos without dimensions
If an img tag has no width and height, the browser does not know how much space to reserve until the file arrives, and all the content below gets pushed down. The fix is to always state the intrinsic dimensions: modern browsers use them to calculate the aspect ratio even when CSS makes the image fluid.
<img src="photo.webp" width="1600" height="900" alt="…">
img { max-width: 100%; height: auto; }
For containers without a native image, such as a map iframe or an embedded video box, use the aspect-ratio property:
.embedded-video { aspect-ratio: 16 / 9; width: 100%; }
Cause 2: content inserted above existing content
Cookie banners, promotional notices, «download the app» bars and error messages added at the top of the page after load push everything down. There are three routes:
- reserve the space from the start with a minimum height, if the content will definitely arrive;
- show the banner as an overlay, with
position: fixed, so it does not alter the document flow; - insert dynamic content below the visible area, not above it.
For a consent banner, an overlay at the bottom is the simplest and most stable choice.
Cause 3: ad slots and external widgets
Ads and third-party widgets have variable sizes and arrive late. The correct practice is to give the container a minimum height equal to the most likely size. If the slot stays empty, a neutral box is better than a sudden collapse: making a reserved space disappear also causes a shift.
Cause 4: web fonts that change metrics
When a web font replaces the fallback font, wider or taller letters can make lines wrap in different places, and paragraphs get longer. The effect is reduced by choosing a similar fallback font and adjusting its metrics with @font-face descriptors such as size-adjust, ascent-override and descent-override, so that the swap is nearly invisible.
@font-face {
font-family: "Adjusted Fallback";
src: local("Arial");
size-adjust: 104%;
ascent-override: 92%;
}
body { font-family: "SiteFont", "Adjusted Fallback", sans-serif; }
The exact values are found by comparing the two fonts; there are tools that calculate them automatically.
Cause 5: animations that move the layout
Animating top, left, margin or height actually moves elements and can generate CLS. Animations built with transform, on the other hand, do not change the layout and are not counted. A slider that moves with transform: translateX() is stable; one that changes margin-left is not.
How to find the shifts
In DevTools, in the Performance panel, the «Layout shifts» track highlights each shift and the elements involved. There is also a rendering option that paints the areas that shift in blue while you browse. In the field, the web-vitals library with attribution reports the selector of the element that moved the most, valuable information because some shifts only happen on slow connections or on long pages where the user scrolls.
Beware of a difference between lab and reality: a tool like Lighthouse measures only the initial load, while real data also includes shifts that happen during scrolling, for example when lazy-loaded images have no dimensions. That is why a good lab score does not guarantee a good CLS in the field.
A checklist
| Element | Check |
|---|---|
| Images | width and height always present |
| Iframes and videos | aspect-ratio or reserved height |
| Banners | overlay or reserved space |
| Ads | container with minimum height |
| Fonts | fallback with adjusted metrics |
| Animations | only transform and opacity |
In short
- CLS measures unexpected shifts; good up to 0.1, and the worst window counts.
- Explicit dimensions and
aspect-ratiosolve most cases. - Banners and notices belong in an overlay or in a space already reserved.
- Adjusted fallback fonts reduce jumps when the typeface swaps.
- Animate with
transformand check with real data too, not just in the lab.