⚡ Web performance

CLS: eliminating layout shifts one cause at a time

The most common causes of Cumulative Layout Shift, from images without dimensions to banners and fonts, with the CSS and HTML fixes that keep a page stable.

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-ratio solve 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 transform and check with real data too, not just in the lab.
MM
Marco Marzaduri

Marco Marzaduri works in cybersecurity, digital forensics and web development. A computer science graduate, he combines rigorous technical analysis with attention to the people who use technology.

Read also