What is Layout Shift#
A short video of a few 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. The behavior of personalized or third-party content during development often differs from that in the production environment, test images are usually cached in the developer's browser, and API calls running locally are often very fast, with delays being almost imperceptible.
What is CLS#
Cumulative Layout Shift (CLS) is a metric.
It measures the maximum layout shift score of 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 affecting 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 not to follow the expected product flow.
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.
How to Reduce CLS#
Placeholder for Media Elements like Images#
Always include width and height attributes in media resource elements like 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.
https://developer.mozilla.org/zh-CN/docs/Web/CSS/aspect-ratio
Browser support:
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
only enlarges it in place.
margin
VS transform: translate
#
margin
causes the parent element to grow, while transform: translate
simply moves the current element.
border
VS box-shadow
#
border
will push the parent element, while box-shadow
will not.
Be Careful with Lazy Loading#
Lazy loading can cause layout shifts; be cautious when navigating within a long list that has lazy loading!
Jumping without animation 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, causing the page to shake.
This is all painful:
Commit: Table and Link Icon Shaking
Commit: Fix Navigation Bar Shaking 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 sorted to the middle (i.e., the second column) by setting the 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 it renders <Main />
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:
- The Chrome parser pauses after reading 65,535 bytes of HTML.
- Chrome continues reading about 50 "tokens" after encountering a
<script>
tag before pausing.
For more details, see: Optimizing Blog's Cumulative Layout Shift (CLS) Issue
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 fully restore to how it was when you left. This means that 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:
- Use web fonts to replace fallback fonts (FOUT—Flash of Unstyled Text).
- 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 shifts when the web font loads, just like the visible fonts in FOUT.
The following methods can help minimize this issue:
- Using
font-display: optional
can avoid re-layout since the web font will only be used if it's available during the initial layout. - 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 usefont-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. - Use the new
size-adjust
,ascent-override
,descent-override
, andline-gap-override
APIs to minimize size differences between fallback fonts and web fonts; for more details, see the “Improved font fallbacks” article. - Use the Font Loading API to reduce the time it takes to fetch the required fonts.
- Use
<link rel=preload>
to load critical web fonts as early as possible. Preloaded fonts have a higher chance of being available for the first render, thus avoiding layout shifts. - Read the “Best practices for fonts” article for font best practices.
Use Real Skeleton Screens#
Measuring CLS Score#
Production Stage#
- Chrome User Experience Report
- PageSpeed Insights
- Search Console (Core Web Vitals report)
- web-vitals JavaScript library
Experimental Stage#
Lighthouse in DevTools#
Can generate actual performance reports for web pages for 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.
PageSpeed Insights#
Should be considered the online version of Lighthouse.
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.
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.
Conclusion#
As someone with high standards for their projects, I often encounter layout shift optimization or Lighthouse, but I didn't have a clear concept of CLS when I was tinkering around before. Now I have a clearer understanding of CLS.
As a very fundamental optimization metric, CLS is crucial 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#
- https://web.dev/cls/
- https://web.dev/optimize-cls
- https://developers.google.com/publisher-tag/guides/minimize-layout-shift
- https://web.dev/yahoo-japan-news/
- https://addyosmani.com/blog/infinite-scroll-without-layout-shifts/
- https://blog.skk.moe/post/fix-blog-cls/
- https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio