Optimizing for web vitals on chloe.com

Andrea Verlicchi
YNAP Tech
Published in
7 min readJul 1, 2020

In this article we explain how we at YOOX NET-A-PORTER optimized the Chloé website for a couple of Core Web Vitals: Largest Contentful Paint (LCP) and Cumulative Layout Shift (CLS).

Web vitals

Web Vitals is an initiative by Google to provide unified guidance for quality signals that are essential to delivering a great user experience on the web.

Core Web Vitals

Core Web Vitals are the subset of Web Vitals that apply to all web pages, should be measured by all site owners, and will be surfaced across all Google tools. Each of the Core Web Vitals represents a distinct facet of the user experience, is measurable in the field, and reflects the real-world experience of a critical user-centric outcome.

The metrics that make up Core Web Vitals will evolve over time (at most once a year). The current set for 2020 focuses on three aspects of the user experience — loading, interactivity, and visual stability — and includes the following metrics (and their respective thresholds):

  • Largest Contentful Paint (LCP): measures loading performance. To provide a good user experience, LCP should occur within 2.5 seconds of when the page first starts loading.
  • First Input Delay (FID): measures interactivity. To provide a good user experience, pages should have a FID of less than 100 milliseconds.
  • Cumulative Layout Shift (CLS): measures visual stability. To provide a good user experience, pages should maintain a CLS of less than 0.1.

For each of the above metrics, to ensure you’re hitting the recommended target for most of your users, a good threshold to measure is the 75th percentile of page loads, segmented across mobile and desktop devices.

Optimizing for LCP

To optimize for LCP, we made the following interventions.

Measuring LCP

The first thing to do is to find out which element of the DOM generates the Largest Contentful Paint. For example, it could be the hero image. The new Chrome Dev Tools allows you to find it in the performance panel. The best way to do this is:

  • Open a new Chrome in incognito mode and with all extensions disabled
  • Launch an audit with Lighthouse
  • Click on the View Trace button, which will open the Performance panel
  • In the performance panel → Timings row, click on the LCP marker

This will open the Summary panel showing details on the DOM node that generated the LCP.

Find out other ways to measure LCP

Optimize loading of above-the-fold images

Above-the-fold images should be loaded as quickly as possible without lazy loading (which delays the loading of images) and with the highest priority. In Chloé’s website, above-the-fold images are the first 4 products of a product listing page, or a hero image (the largest above-the-fold image that draws the user’s attention).

Above-the-fold images requests before optimization

To increase the priority of the network calls to those images we have lowered the priority of the network calls that arrived earlier and with higher priority. Note that this optimization was more effective than the application of priority hints e.g. setting the importance attribute on the images.

Above-the-fold images requests after optimization
Optimization result in the network waterfall

Read more on resource prioritization.

Capping image pixel density to 2x

Limiting the size of images served to screens with a pixel density higher than 2x doesn’t produce any human-eye-perceivable difference, so we did that. Now an iPhone X or a Google Pixel 3 XL, which both have 3x density screens, will receive the image optimized for 2x screens. This has improved performance, reducing image weight by 50%.

A 2x image weights the half of the 3x image (820 px vs 1230 px wide)…
…and that’s true even for WebP images

On Chloé’s website we have images that vary in size depending on the browser width. There are two ways to code responsive images:

  1. using the img tag with a set of sources is the srcset tag and the image sizes in the sizes attribute
  2. wrapping the img tag in a picture tag and providing alternative sources via the source tag

Since using the first one the browser will decide which image to select from the given srcset based on the image size and screen density, the only way to cap responsive images density to 2x is to use the picture tag which is imperative.

Picture tag used to cap responsive images density to 2x

Serving modern image formats

Some years ago Google introduced WebP, a new image format for the web. This new format is now supported by all Chromium browsers (Chrome, Microsoft Edge and Opera) and by Mozilla Firefox. It will be supported by Safari 14 later this year.

Using an image CDN service we developed at YNAP, we can still point to JPG images and the service will take care of converting the images to the WebP format and serving it to browsers that support it.

This specific WebP image weights 75% then in the JPG format

At YNAP we use an image CDN service developed in-house, but if you don’t want to go down that path, there are other similar services on the market, such as Akamai Image and Video Manager and Cloudinary.

Removal of render-blocking CSS

To reach the LCP optimization one of the most important actions has been the removal of render-blocking CSS by inlining the style required to render the above-the-fold elements (critical CSS) in the page head, and asynchronously calling the rest of the stylesheet.

To dynamically generate the critical CSS we developed a script that runs at build-time extracting all the CSS blocks containing the custom critical: this property and inlining them in the head of the page. The inlined CSS rules are removed from the original CSS files, which are loaded with low priority using the media=’print’ technique.

The following source SCSS file…

Source SCSS file

…generates this HTML with inlined critical CSS…

HTML with critical CSS inlined

…and leaves non critical CSS rules in the deferred CSS

Non critical CSS rules

Preload of web fonts

Delayed loading of web fonts can generate a delay in the display of the text, generating an additional paint therefore increasing the LCP metric. The solution we adopted is to preload important fonts for above-the-fold content.

Preloading of important fonts for above-the-fold content

We also used the font-display: swap CSS property which allows you to view a fallback font (avoiding a FOIT: flash of invisible text) while the final font is being loaded.

Using font-display to show a fallback font while loading

Read more on web fonts optimization.

Loading animation

In addition, to improve the user experience, we animated the background of the product images containers while loading. The animated background is a linear gradient that starts from the same background color as the product images.

We call this effect “bruschetta loading” (pronunciation: /brusˈket.ta/)

Optimizing for CLS

To optimize for CLS you need to reserve space for the content that might be rendered later in time causing an unexpected page elements shifting.

Reserve space for images

The traditional way to reserve space for the images is to use the vertical padding trick.

The modern way is to define an aspect ratio (width / height), either implicitly by defining the width and height attributes on images and videos or explicitly using the aspect-ratio CSS rule.

As we are writing, aspect-ratio is not supported by Safari, but it will be later this year on version 14.

Reserve space for other above-the-fold elements

In the world of e-commerce, one of the most common above-the-fold elements is the horizontal band containing promotional messages.

If the promotion must be loaded client-side with an asynchronous fetch, the best practice is to reserve space immediately. In this case, the problem is to know on the server-side whether or not the element will be there, so we make a server-side call to find out if there are active promotions, and in that case, we reserve space for the promotional message.

Skeleton on user-triggered async actions

Since the CLS metric does not increase if a layout shift occurs within 500 ms from a user input, we use the skeleton pattern to reserve space immediately without waiting for the call to complete. In this way, even if the async call takes more than 500 ms, the layout change can be excluded from calculations.

Choose and style the default font

Choose the font that most resembles to the final one and use the critical CSS to give it the same dimensions as the final loaded font.

Summary

To optimize for LCP:

  • Optimize loading of above-the-fold images
  • Capping image density to 2x
  • Serving WebP images
  • Removal of render-blocking CSS
  • Preload of web fonts
  • (Loading animation)

To optimize for CLS:

  • Reserve space for images
  • Reserve space for other above-the-fold elements
  • Skeleton on user-triggered async actions
  • Choose and style the default font

A never-ending story

Keep web vitals monitored and improve them in small cycles (development, deployment, and measurement). This will allow you to learn new techniques and understand how to optimize more and more.

About the authors

This article has been co-authored with Riccardo Petracchini and Davide Barletta, members of the YNAP performance guild, by Andrea Verlicchi, front-end architect at YNAP.

--

--

Andrea Verlicchi
YNAP Tech

Web performance consultant at Cognizant Netcentric, formerly YOOX NET-A-PORTER. Technical writer, speaker at web conferences.