Solving Cumulative Layout Shifts At Scale

Preface Source: https://www.axongarside.com/blog/core-web-vitals-will-become-ranking-factor On June 2021, Google shipped an update where the metrics falling under core web vitals would be given some weightage in the ranking of a webpage. The content that follows is a documented journey of all the enhancements that have been made on Housing.com to improve the CLS scores of a webpage. Origins […]

Preface

Source: https://www.axongarside.com/blog/core-web-vitals-will-become-ranking-factor

On June 2021, Google shipped an update where the metrics falling under core web vitals would be given some weightage in the ranking of a webpage. The content that follows is a documented journey of all the enhancements that have been made on Housing.com to improve the CLS scores of a webpage.

Origins

Back when the front-end realm hadn’t evolved much, the story was simpler. If you need a product start by picking up a framework, connect the database, play around with the data and send some/all of the data back to the view and Voila!

Your page is up and running.

But as the requirements started progressing there was a need to decouple the view from the framework and hence, libraries/frameworks of various kinds (Angular, React, Vue, Svelte etc.) were introduced which not only eased the process of development but also provided it’s users a lot of ways with how things were being developed.

Using this new found power, it changed the way how a website was perceived, from content being partially loaded, elements could be added anywhere in the DOM with ease to pagination free infinitely long pages, everything suddenly became a bit easier to achieve.

But as it’s rightly put

With power comes responsibilities

Identifying the Problem Statement

The capability of adding and deleting elements with relative ease, gave a rise to a new problem, called as layout shifts

What are layout shifts?

Let’s say we are reading an article on a website, something pops on the page and you eventually loose track of what you were reading. That’s because content was being dynamically attached and was causing the whole content to shift up/down. The content could be anything from a simple advertisement to a banner looking for leads.

Shifting Content

An excerpt from an article on web.dev:

Layout shifts are defined by the Layout Instability API, which reports layout-shift entries any time an element that is visible within the viewport changes its start position (for example, its top and left position in the default writing mode) between two frames. Such elements are considered unstable elements.

Approaching the problem

The initial set of urls were available from the report generated by Core Web Vitals (available on search console) which shows a similar set of urls with their average CLS score. Upon analysing the problem statement was divided into two parts namely above the fold and below the fold.

Debugging above the fold

To ease fixing of CLS, there are plenty of tools available which load the page and check for unstable elements on the first fold. We used Lighthouse to detect discrepancies on the page which reports the score and identifies all the elements which have shifted from their original position. One can either use the Lighthouse tool present in Google Chrome itself or could use a hosted service known as Page Speed Insights

Understanding the report

Page Speed Insights | Highlighted score represents the current value for CLS for above the fold items

There are two categories, Lab Data and Field Data as is evident from the screenshot, the score differs for various metrics between the categories. Field Data represents the data that is collected from the user’s browsers where more variables are introduced such as, user’s proximity to the server, internet bandwidth, computational capacity of the device etc. these variables mostly vary from user to user whereas Lab Data represents the data that belongs to the current executed test.

Inference

After careful scrutinisation of the elements, we found that problems could be classified into one of the following categories:

  • Fonts causing disturbance in dimensions
Capture of an item causing CLS due to fonts

If custom fonts on the website are used, it shows the desired outcome after it’s associated font files are downloaded. (denoted by .woff/.woff2). Till the time the font is downloaded, a default font is shown by the browser. A layout shift can occur due to differences in basic dimensional properties like width, height of a text between the default and the downloaded variants. For example, the font-weight property could render differently on a default and a downloaded font. This was solved using preloading font files where prioritised first as a resource implying that whenever the page rendered, it would render with the downloaded font causing no changes in height and width of the element.

  • Images causing elements to shift
Highlighted sections caused layout shifts

Images are assets which are downloaded via a network request. Till the time the request is completed, the image tag (<img />) remains empty, implying that the tag itself has no height and width. Upon arrival of the image, the container then adjusts itself to whatever space is available, causing shift in position of related content. This problem was rectified by declaring image tags with fixed height and width, causing the whole section to be rendered with a final height and width

  • Client side rendered elements appearing on first fold
    The basic difference with client side and server side elements is that data is provided at runtime whereas, for the latter, elements are rendered preloaded data. Ideally, all elements present in the first fold should be server side rendered, as it’s also beneficial for other metrics ( eg: TTI/TBT/LCP ) along with our target of achieving zero CLS score. But somehow, if client side rendering is not avoidable on first fold, then how do we solve it?
Highlighted section causes CLS due to creation at some later point in time

Let’s assume the highlighted box is a client side rendered element, data of which arrives at runtime. When our page initially loads, the first screenshot depicts what is initially sent by the server. At some later point in time, client side data is requested and the whole element shifts down in order to incorporate this newly rendered feature. To solve this, a loader can be put in place from the server side response itself, which should essentially be of the same height and width as the element in question, and would avoid other elements from shifting up/down post an api call is completed. However, this approach only solves the problem partially.

What if, we add the loader from the server side response, but there is no data available at runtime? In that case, this section should be hidden, leading to a layout shift again :/

Improving CLS score is a continuous process. Look out for Chapter 2 of this journey which would highlight some advanced techniques that were used to improve the scores.


Solving Cumulative Layout Shifts At Scale was originally published in Engineering @ Housing/Proptiger/Makaan on Medium, where people are continuing the conversation by highlighting and responding to this story.

Source: Housing.com