Improving Website Cumulative Layout Shift (CLS) Metrics

Mohammad Hasan Muktasyim Billah
Blibli.com Tech Blog
7 min readDec 19, 2021

Cumulative Layout Shift (CLS) is one of the web vitals, metrics initiated by Google to become quality guidance for a great experience in the website. In 2020, CLS become one of the core web vitals metrics. The other core web vitals are Large Contentful Pain (LCP) and First Input Delay (FID).

Disclaimer :
This post is made only by my experience. Since each website has different functionality and design, the tricks and methods may or may not be the best solution for your website.

Intro

CLS is a measure of the total number of unexpected layout changes on a website.

Unexpected layout changes referred to the changes of the initial position of visible elements after rendering without being initiated by user activity (or exceeding 500 milliseconds intervals from the last user interaction).

Generally, these changes are caused by asynchronous processes or fetching data from the API.

Layout changes that are calculated are changes that occur in visible elements or are contained in the viewport. Changes that occur immediately in an interval of fewer than 500 milliseconds after the last user interaction are expected and are not included in the calculation.

A good CLS score is less than 0.1. the measurement itself can come from the 75th percentile of the field data.

Layout Shift Score

The layout shift score is calculated for each component that experiences a change in its starting point. The calculation process itself follows this formula

layout shift score = impact fraction * distance fraction

Impact Fraction

Impact fraction is a variable that determines the changes that occur between two frames.

The impact fraction score is obtained through the union impacted area in the first and second frames divided by the total viewport.

The picture above shows a new section inserted. Adding a section makes a shift in the next element. The area marked with pink represents the shift union that occurs from the first frame to the second frame.

The union is about 55% of the total available viewport, so the impact fraction value from the changes is 0.55.

Distance Fraction

Distance fraction is the largest change in position, either horizontally or vertically, of an element divided by the viewport dimensions (width or height)

In the example above, the shift that occurs is in a vertical position. The shift is about 19% from the existing viewport height. So the value of the distance fraction is 0.19.

The total score layout shift is 0.19 * 0.55 = 0.1045

Please remember: The calculation of the layout shift score is always done based on the active viewport.

The picture above shows the example of the impact fraction of an element that shifts out of the viewport. It can be seen by default that the resulting union does not exceed the existing viewport.

From the previous explanation, it can be concluded that the score calculation is carried out using several rules:

  1. The score calculation is very dependent on the viewport used.
  2. The calculations are made of the elements that are visible or are in the viewport and have changed their initial position, especially the top and left positions. Elements that are outside the viewport (not visible) are not included in the calculation.
  3. Changes that occur in a time span of more than 500 milliseconds after the last user interaction is included in the calculation. The interactions that are tolerated are click, tab, and key enter events.

How to improve CLS

By knowing the process of how the CLS score is calculated, there are several ways that can be done to improve the CLS matrix value in a website.

Avoid inserting an element on top of an existing element.

If we add a new element before an existing element, it will cause the element below it to have the layout shifted. The addition of elements in the previous position is only allowed if it occurs after user interaction. Events such as scrolls, drag, pinches don’t include this exception.

Reserve a place in the viewport.

The reserving process can be done by defining the width and height attribute values or using the ratio boxes. These tips can be applied to images or other elements that require loading, such as components that depend on the fetch API process. but don’t forget to adjust the size attribute used with the existing viewport size, if you want to build a responsive website.

The picture above shows how the process of loading images that do not have dimension attributes cause the layout shift.

When we define the dimension attribute, even though the image has not been fully fetched, the browser will still prepare an empty space according to the specified size.

Side notes: defining max or min-size doesn’t make the UI reserve space.

Be careful with if-else switch.

If you build a website using Vue js, just like me, then you need to be careful with the use of v-if on an element. At the time of the switching process, the element can have a big height change (can be down to 0). This can make the element below it change its start position. And when the switching process is complete the element can return to a certain height, so the next element will go down. This process can cause quite large layout shifts.

It can be seen from the picture above that the orange section will have a big shift when the switching process is done.

Then how to prevent any layout shift in the switching process?

The answer is quite simple. We need to provide a wrapper for each section. This wrapper must have specified dimensions.

The dash illustrates the created wrapper. by making a wrapper, the layout shift that occurs can be minimized (although there is still a layout shift in the third frame).

Maximize skeleton size for dynamic content.

Skeleton is generally used when loading an element. The size of the skeleton itself is also generally adjusted to the dimensions of the elements used. However, for dynamic content whose dimensions cannot be predicted, it would be better to use a skeleton with a large size. This is because shifting from outside the viewport to the inside, is better than shifting from inside the viewport to the outside.

Just like the previous example, if the pink section is the skeleton loader and the blue section is dynamic content whose height cannot be predicted, then the orange section will still experience a layout shift after the content is successfully loaded.

However, if the skeleton loader is made with the maximum size, the layout shift can be avoided.

In the third frame (after loaded) the orange container is not considered shifting because in the first or second frame the element is not in the viewport.

So keep in mind, shifting from outside the viewport to the inside, is better than shifting from inside the viewport to the outside.

Avoid to much animation.

Inappropriate or too many animations can lead to a high CLS score. we need to avoid unnecessary animations if we can.

Avoid animation that directly changes the width, height, or other dimension attributes. Instead, use transform:scale to avoid a high CLS score. Also avoid changing position attributes, such as top, right, bottom, or left. We can use transform: translate to avoid any layout shift.

How to measure CLS ?

We can use lab measurement tools such as Lighthouse or field measurement tool such PageSpeed Insight.

Both PageSpeed Insight and lighthouse will also give deep diagnostics about the factor that make the score high. we can also get some suggestions on how to improve them.

Since the measurement itself is very tricky, I personally suggest, before and after doing any improvement, we do a high iteration measurement of the website. So that we know exactly if our improvement really working or not.

The more explanation of how to take measurements with high iteration will be discussed in the next article.

--

--