Nexmoe

Nexmoe

一个开发者。关于勇敢与热爱,互联网/创造/赛博朋克
twitter
github

Cumulative Layout Shift Optimization: A Complete Guide

What is Layout Shift#

A short video of about ten seconds explains it clearly.

A more detailed explanation is: Layout shift refers to the phenomenon where the position of content on a webpage unexpectedly moves when sudden changes occur. This situation often causes frustration as it can lead to interruptions in reading or accidental clicks. Layout shifts are typically caused by resources loading asynchronously or DOM elements being dynamically added to the page. Possible causes include images or videos with unknown dimensions, fonts rendering at different sizes than their fallback fonts, or third-party ads or widgets dynamically resizing.

The discomfort arises because the functionality during the development process often differs significantly from the user experience. Personalized or third-party content behaves differently in development than in production, test images often exist in the developer's browser cache, and locally run API calls are usually very fast, with delays being nearly imperceptible.

What is CLS#

Cumulative Layout Shift (CLS) is a metric.

It measures the maximum layout shift score for each unexpected layout change that occurs throughout the entire lifecycle of a page.

CLS helps address layout shift issues by measuring the frequency with which actual users encounter layout shifts. It can help developers understand how layout shifts occur among real users, allowing them to take appropriate measures to fix them.

Why Optimize CLS#

Layout shift is a significant issue that affects user experience, as can be understood from the brief video above.

Layout shifts often lead to accidental clicks, loss of page direction, and ultimately user frustration. Users typically do not stay for long. Sometimes it also causes users to deviate from the expected product flow.

Generally, optimizing layout shifts can significantly improve user engagement, time spent on the site, and other metrics.

Yahoo! JAPAN News achieved the following results by reducing CLS by 0.2 points.

image

How to Reduce CLS#

Placeholder for Media Elements like Images#

Always include width and height attributes in media resource elements such as images and videos. Alternatively, use CSS properties like min-height, aspect-ratio, or similar to reserve the required space.

aspect-ratio#

Can be used to directly specify the aspect ratio of the current element.

image

https://developer.mozilla.org/zh-CN/docs/Web/CSS/aspect-ratio

Browser support:

image

padding-bottom#

If considering browser support issues, you can still consider using a widely accepted basic solution known as the "Padding-Top Hack." This solution requires a parent element and an absolutely positioned child element. Then calculate the percentage of the aspect ratio to set as padding-top. For example:

<div class="container">
  <img class="media" src="..." alt="...">
</div>
.container {
  position: relative;
  width: 100%;
  padding-top: 56.25%; /* 16:9 Aspect Ratio */
}

.media {
  position: absolute;
  top: 0;
}

Use CSS That Minimizes Shifts#

Among them, transform performs well; here are a few examples.
Use cases can be found here: https://play.tailwindcss.com/26PxFA6UVI

zoom VS transform: scale#

While zoom enlarges the page and shifts it to the right, transform: scale simply enlarges it in place.

image

margin VS transform: translate#

margin causes the parent element to grow, while transform: translate merely moves the current element.

image

border VS box-shadow#

border pushes the parent element, while box-shadow does not.

image

Be Careful with Lazy Loading#

Lazy loading can cause layout shifts; be cautious when navigating within a long list with lazy loading!
Jumping without animations can help mitigate this issue.

Be Cautious with transition: all#

During the initial page load or when navigating pages, transition: all may cause elements' padding and others to start rendering from a value of 0, resulting in page jitter.

These are painful:
Commit: Table and Link Icon Jitter
Commit: Fix Navigation Bar Jitter Issue

Offset Issues Caused by Tag Order#

Due to the priority of displaying main content on mobile devices, the markup for the sidebar is placed after the main content; on larger screens, the main content is reordered to the center (i.e., the second column) using CSS order, as shown in the pseudocode below:

export default function MainLayout(props) {
  return (
    <Container>
      <Main className={css`@media screen and (min-width: breakpoint) { order: 0 }`} />
      <Left className={css`@media screen and (min-width: breakpoint) { order: -1 }`} />
      <Right className={css`@media screen and (min-width: breakpoint) { order: 1 }`} />
    </Container>
  )
}

When the browser first renders, it does not fully parse the DOM; it only knows that <Main /> exists but does not know about <Left /> or <Right />, which is why <Main /> is rendered in the first column instead of the second. It is only during the second render that the browser renders <Main /> in the second column and <Left /> in the first.

Chrome does not parse HTML in one complete pass; it pauses parsing and starts rendering and painting in the following two cases:

  1. The Chrome parser pauses after reading 65,535 bytes of HTML.
  2. Chrome continues reading about 50 "tokens" after encountering a <script> tag before pausing.

For more details, see: Optimizing Blog's Cumulative Layout Shift (CLS) Issues

Page Navigation and Back/Forward Cache#

By default, all browsers use bfcache, but for various reasons, some sites are not suitable for using bfcache. For more detailed information on how to test and identify any issues preventing bfcache usage, read the bfcache article.

After you leave, bfcache keeps the page in the browser's memory for a short period, so if you return to it, it will be restored exactly as it was when you left. This means fully loaded pages are immediately available without any changes.

Current SPA applications can also easily ensure consistency in layout during route transitions. Always remember to keep your directory and navigation bar in fixed positions on the page.

Fonts#

Before downloading and rendering web fonts, there are typically two handling methods:

  1. Use web fonts to replace fallback fonts (FOUT—Flash of Unstyled Text).
  2. Use fallback fonts to display "invisible" text until the web font is available and the text is visible (FOIT—Flash of Invisible Text).

Both methods can lead to layout changes. Even if the text is invisible, it still uses the fallback font for layout. This means that text blocks using that font and surrounding content will experience layout changes when the web font loads, just like FOUT with visible fonts.

The following methods can help minimize this issue:

  1. Using font-display: optional can avoid re-layout since the web font will only be used if it is available during the initial layout.
  2. Use closely matching fallback fonts. For example, using font-family: "Google Sans", sans-serif; ensures that the browser's sans-serif fallback font is used while loading the "Google Sans" font. If you only use font-family: "Google Sans" without specifying a fallback font, the default font will be used, which is "Times" in Chrome, a worse match than the default sans-serif font.
  3. Use the new size-adjust, ascent-override, descent-override, and line-gap-override APIs to minimize size differences between fallback and web fonts; for more details, see the “Improved font fallbacks” article.
  4. Use the Font Loading API to reduce the time it takes to obtain the required fonts.
  5. Use <link rel=preload> to load critical web fonts as early as possible. Preloaded fonts have a higher chance of being available for first paint, thus preventing layout shifts.
  6. Read the “Best practices for fonts” article for font best practices.

Use Real Skeleton Screens#

Example of Good and Bad Skeleton Screens

Measuring CLS Score#

Production Stage#

Experimental Stage#

Lighthouse in DevTools#

Can generate actual performance reports for web pages for both mobile and desktop devices and provide suggestions on how to improve the respective web pages.

Running Lighthouse from DevTools during local development is very convenient.

image

PageSpeed Insights#

This is essentially the online version of Lighthouse.

image

Performance in DevTools#

The performance tab records all page behaviors in Chrome's DevTools profile over a period. A layer marked "Experience" appears on the timeline, highlighting layout changes and the elements that changed.

image

Web Vitals Extension#

It is best to view the Web Vitals extension as a spot-check tool for identifying performance issues rather than a comprehensive debugging tool—that's the job of the performance tab in Chrome's DevTools.

image

Conclusion#

As someone with high standards for their projects, I often encounter layout shift optimization or Lighthouse. However, I didn't have a clear concept of CLS when I was previously experimenting. Now I have a clearer understanding of CLS.
As a fundamental optimization metric, CLS is very important for user experience, and every project should optimize for CLS.

If there are any corrections, please point them out in a timely manner. Thank you!

References#

  1. https://web.dev/cls/
  2. https://web.dev/optimize-cls
  3. https://developers.google.com/publisher-tag/guides/minimize-layout-shift
  4. https://web.dev/yahoo-japan-news/
  5. https://addyosmani.com/blog/infinite-scroll-without-layout-shifts/
  6. https://blog.skk.moe/post/fix-blog-cls/
  7. https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.