How To Improve Cumulative Layout Shift Score

By Josip Miskovic
A painting being shredded.

Cumulative Layout Shift (CLS) is a metric for visual stability of a webpage. CLS describes how often users experience unexpected layout shifts.

In this post, we'll see why CLS is important and how to prevent it.

Since it's a well defined metric, it's easy to see how you website is doing and if you need to improve.

I'll give you specific instructions on how to improve your CLS score by optimizing fonts, dynamic content, images, and ads.

Why CLS matters?

  • Cumulative Layout Shift causes bad use experience for website visitors
  • CLS is part of Google’s newly released Web Vitals. It's a set of critical performance metrics that will affect your SEO placement.
  • It will become a ranking factor in May 2021

What causes CLS?

Web fonts, dynamic content, images, and ads are most common causes of a cumulative layout shift.

How is CLS calculated?

  • CLS score is a multiple of Impact and Distance:
    • Impact fraction is a measurement of how much space an element takes
      • impact fraction = area of impact region / area of viewport
    • Distance fraction
      • Distance it has moved on the horizontal or vertical axis (whichever is greater), relative to the viewport.
      • distance fraction = max move distance / viewport width or height
  • CLS score only counts shifts visible in the viewport (above the fold)
  • Google counts a 0.5 second (500ms) of delay after the last user interaction before it starts counting it as your CLS score.

What is a good CLS score?

  • Good CLS score is under 0.1.
  • Score between 0.1 and 0.25 needs improvement.
  • Score above 0.25 is poor.
Green, orange and red bar. `0.1` indicator between green and orange.  `0.25` indicator between orange and red.

What is my CLS score?

Measure your CLS score using Lighthouse. https://web.dev/measure

Screenshot showing cumulative layout score of 0.01 in Google's Lighthouse tool.

Lighthouse is a quick way to check the cumulative layout shift score.

How to prevent CLS caused by Fonts

Custom fonts look pretty, but they take time to load. Which can cause a layout shift.

With web fonts, we make a tradeoff between style, speed and usability.

The most common approaches are:

  • Hide text until font loads

    • 🟢 Text shows up styled from the first paint.
    • 🔴 Flash of Invisible Text - Text is invisible until the first paint.

  • Show text in default font ASAP and swap the font once the desired font loads

    • 🟢 Text shows up immediately.
    • 🔴 Flash of Unstyled Text - Text changes style when the swap happens

  • Fallback. Give fonts a short time to load. If it doesn't load in a time, use a fallback font.

    • 🟢 Text shows up after a short time. Custom font gets a chance.
    • 🔴 Not-perfect style - Fallback font might end up getting used.

Hiding text until custom font loads usually causes the biggest layout shift.

We can control this behavior using the font-display CSS.

Use font-display CSS property

The font-display descriptor determines how a font face is displayed based on whether and when it is downloaded and ready to use.

To fix the layout shift caused by fonts, the two best font-display options are:

font-display: fallback;

  • If the desired font is not loaded, text renders with a fallback font face. It’s swapped in as soon as font loads.
  • Gives the browser:
    • 100ms to get the font before showing a fallback.
    • 3 seconds to swap the font. Otherwise, uses the fallback font.

font-display: optional;

  • If the font is available at the first paint, the browser will use it. Otherwise, it'll use the fallback font.
  • Gives the browser:
    • A chance to get the font before the first paint.
    • No opportunity to swap the font.

According to CSS Fonts Module Level 4 spec:

An 'optional' font must never cause the layout of the page to "jump" as it loads in.

Use font-display: optional; if you want to prevent cumulative layout shift caused by fonts and you can make a tradeoff with styles.

Use a fallback font

Choose a fallback font that matches sizes of your desired webfont to prevent layout shifts caused by different widths and heights.

For example, The Wall Street Journal's fallback font and intended font overlap perfectly, not causing any shifts in layout:

The Wall Street Journal article with the fallback font.

The fallback font
Before the desired font loads

The Wall Street Journal article with the intended font.

The intended font
When the desired font loads

Can you spot the difference?
Fonts overlap perfectly

To define a fallback font, use the font-family property:

css
body {
  font-family: Helvetica, /* top choice */
      Verdana, /* if Helvetica is not available, use Verdana */
      sans-serif; /* if Helvetica and Verdana are not available, use any sans-serif that browser has */
}

Font style matcher is a great tool to find the best fallback font.
It makes it easy to find which fonts overlap the best.

Preload fonts

We can help a browser discover fonts quickly by adding:

html
<link
  rel="preload"
  href="fonts/font.woff2"
  as="font"
  type="font/woff2"
  crossorigin
/>

rel="preload" increases the chance that font will be available at first paint.

How to prevent CLS caused by Dynamic Content

Dynamic or lazy-loaded content is often a cause of layout shifts.

Using placeholders with a consistent width-to-height ratio (called an aspect ratio) is a great way to keep the layout consistent.

Avoid dynamic content above the fold

Not having dynamic content above the fold is the best way to prevent layout shifts.

Think about layout shifts should be part of web design.

Use CSS aspect-ratio property

The new aspect-ratio CSS property is the cleanest way of setting the aspect ratio for an element.

Image maintains the 2:3 aspect ratio despite different widths. © web.dev

css
aspect-ratio: 2 / 3;

Aspect-ratio reduces or eliminates layout shifts because it reserves consistent width-to-height ratio for an element.

Unfortunately, many browsers don't support aspect-ratio property yet.

Check: Can I use aspect-ratio? for latest browser support.

If it's not supported yet in your targeted browser, see how to achieve the same using the padding trick.

Use the padding trick

We can reserve fluid space by maintaining aspect-ratio.

We can achieve a steady aspect-ratio using CSS padding properties.

padding-top and padding-bottom are based on the parent element’s width.

If we have a box with 900px width and we set padding-top to 50%. What will the height be?

The height of the box will be 450px.

Since padding is a percentage, it adjusts to changes in width. So, if width goes down to 400px, height will be 200px.

See the responsive demo in the video below. As I change the screen width, the box adjusts width and height to maintain 16:9 aspect ratio.

RatioBuddy is a fun tool to visualize, calculate and experiment with aspect ratios. As I change the screen width, the box maintains 16:9 aspect ratio.

css
.outer {
  position: relative;
}
.outer:before {
  display: block;
  content: "";
  width: 100%;
  padding-top: calc(
    1244 / 429 * 100%
  ); /* Let the browser calculate the aspect ratio 
																				based on our 1244x429px box */
}
.outer > .inner {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

How to prevent CLS caused by Images

Set height and width on images

Set the image width and height attributes on the img element, and browsers will figure out how much space to reserve for the image.

html
<img src="example.jpg" width="500" height="250" />

How does the browser figure out how much space to reserve for an image?

Browsers have a built-in logic to maintain a consistent width-to-height ratio.

For example, Chrome adds the following CSS based on specified width and height :

css
img[Attributes Style] {
  width: 1000px;
  aspect-ratio: auto 1000 / 600;
  height: 600px;
}

It is using the aspect-ratio CSS property.

What if I don't know the image size?

Make the image fit your pre-defined space.

Add CSS declaration object-fit: cover; to the image.

Browser will clip the image to fill element’s entire content box.

html
<section
  style="
    width: 230px;
    height: 500px;"
>
  <img src="example.jpg" style="object-fit: cover;" />
</section>

Use the padding trick

See Use the padding trick. It applies to images too!

How to prevent CLS caused by Ads

CLS score only counts layout shifts that happen above the fold.

And above-the-fold ads earn the most money. And that's where most people put them.

So it's very important to load them properly for a good CLS score.

Since the ad loading process is usually the slowest, they load after the page content and cause a layout shift.

The most common technique is to reserve fixed space on the webpage for ads.

Reserve fixed space for responsive ads

Responsive display ads automatically adjust their size to fit in the designated space on the webpage.

Responsive display ads have the most reach of all display ads because they're made to fit nicely across different device sizes. So it makes sense that Google has been pushing for responsive ads since 2019.

But the variability of ad dimensions causes cumulative layout shift because we don't know exactly which advert will load.

Google recommends providing exact ad dimensions for different screen sizes.

html
<style>
  .responsive_ad {
    width: 320px;
    height: 100px;
  }
  @media (min-width: 500px) {
    .responsive_ad {
      width: 468px;
      height: 60px;
    }
  }
  @media (min-width: 800px) {
    .responsive_ad {
      width: 728px;
      height: 90px;
    }
  }
</style>

<script
  async
  src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"
></script>

<ins
  class="adsbygoogle responsive_ad"
  style="display:inline-block"
  data-ad-client="ca-pub-XXXXXXX11XXX9"
  data-ad-slot="8XXXXX1"
></ins>

...

What do these styles mean?

  • For browser windows:
    • Narrower than 500px → use 468×60 banner ad
    • Wider than 500px but narrower than 800px → use 468×60 banner ad
    • Wider than 800px → use 728×90 leaderboard ad

Keep in mind that different ad sizes perform differently. Experiment to find which ad sizes bring the best results for you.

Example: Ads on The Wall Street Journal

The Wall Street Journal homepage loading. Articles titles are visible, with empty space around.
Placeholder for ads
The Wall Street Journal homepage loaded with a big ad in place of the main placeholder.
Placeholder filled by 970x250 ad
The Wall Street Journal homepage loaded with a small ad in place of the main placeholder.
Placeholder filled by 728x90 ad

Loading ads is a dynamic process.

There are many factors that impact which ads will show up. Some of them that WSJ looks at are:

  • User's geolocation
  • User's screen size
  • Number of pages viewed
  • Is GDPR required?

The WSJ currently has 6 bidders and different dimensions for each ad slot. For example, two different ad sizes can show up just below the WSJ headline. 728x90px or 970x250px.

The WSJ takes the safe approach and reserves 250px in height to prevent the cumulative layout shift down.

In case that the bigger ad loads, the content won't shift at all.

But if smaller ad loads, the content will shift up rather than down.

Since the ad size is dynamic, the shift up is a better tradeoff because it prevents people from accidentally clicking on the ad. The worst it can happen is accidentally clicking on the wrong article.

Takeaways

Cumulative Layout Shift (CLS) describes how often users experience unexpected layout shifts.

  • CLS will affect SEO starting May 2021.
  • CLS score is a multiple of:
    • Impact (how much space element takes), and
    • Distance (how far element moves).
  • Aim for CLS score under 0.1.
  • Layout shifts within 500ms of the last user interaction are OK.
  • Use Google Lighthouse to measure your score.
  • Web fonts, dynamic content, images, and ads are most common causes of a cumulative layout shift.

To prevent CLS caused by Fonts:

  • Experiment with the font-display CSS property.
  • Pick an alternative font that matches style of your desired font.
  • Use rel=preload directive to quicker notify browsers of custom webfonts.

To prevent CLS caused by Dynamic content:

  • Try to avoid dynamic content above-the-fold.
  • Set an aspect-ratio using the CSS aspect-ratio property
  • Use padding to reserve space for an element

To prevent CLS caused by Images:

  • Set Height And Width On Images
  • Use object-fit CSS property to make images fit dynamic containers.
  • Use the padding trick to set the aspect-ratio

To prevent CLS caused by Ads:

  • Reserve fixed space on the web page
Josip Miskovic
About Josip

Josip Miskovic is a software developer at Americaneagle.com. Josip has 10+ years in experience in developing web applications, mobile apps, and games.

Read more posts →
Published on:
Download Free Software Developer Career Guide

I've used these principles to increase my earnings by 63% in two years. So can you.

Dive into my 7 actionable steps to elevate your career.