In the vast ocean of metrics that measure web performance, Cumulative Layout Shift (CLS) emerges as a beacon highlighting visual stability. So, what’s the real story behind this metric, and why should it matter to developers? Let’s dive deep and unravel the mysteries of CLS.
Understanding the Basics
At its core, CLS measures unexpected layout shifts on a page. Ever been reading an article and suddenly, the content jumps, causing you to lose your place? That’s a layout shift. It’s not just annoying; it disrupts the user experience, making your site less user-friendly and, by extension, less attractive to search engines.
The Math Behind CLS
CLS is calculated using the impact fraction and the distance fraction:
CLS = impact fraction × distance fraction
- Impact Fraction: It’s the portion of the viewport that’s affected by the shift.
- Distance Fraction: It measures how far the unstable elements have moved.
For developers, this means that both the size of the shifting element and the distance it moves contribute to the CLS score.
Root Causes of High CLS
1. Images Without Dimensions
The absence of dimensions for images is a common oversight. When a browser encounters an image without specified dimensions, it doesn’t know how much space to allocate, leading to content shifting once the image loads.
Bad Practice:
<img src="image.jpg">
Good Practice:
<img src="image.jpg" width="400" height="300">
However, in responsive designs, using CSS to manage dimensions is preferable:
img.responsive { max-width: 100%; height: auto; }
2. Ads, Embeds, or Iframes Without Dimensions
Much like images, these elements can cause content shifts if they load without predefined sizes. It’s essential, especially with dynamic ads, to reserve the necessary space.
Solution: Using CSS, you can define a container with a fixed aspect ratio to ensure space is reserved:
.ad-container {
position: relative;
width: 100%;
padding-top: 56.25%;
/* 16:9 Aspect Ratio */
}
.ad-content {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
3. Dynamically Injected Content
While dynamic content enhances interactivity, injecting it without considering its position can lead to significant layout shifts.
Recommendation: If content needs to be added dynamically, consider adding it below the current view or ensure user interaction before the addition.
// Instead of this
document.body.prepend(dynamicContent);
// Consider this
document.body.append(dynamicContent);
4. Web Fonts Causing FOIT/FOUT
Flash of Invisible Text (FOIT) and Flash of Unstyled Text (FOUT) occur when custom fonts are loaded, causing visible shifts.
Solution: Using font-display: optional
or font-display: swap
in your @font-face
rule can mitigate this.
@font-face {
font-family: 'CustomFont';
src: url('/path/to/font.woff2') format('woff2');
font-display: swap;
}
5. Animations and Transitions
Unexpected animations, especially those triggered on page load or by scroll events, can contribute to CLS.
Recommendation: Ensure animations are user-initiated and utilize the Web Animations API for better control:
element.animate([
{ transform: 'translateY(0)' },
{ transform: 'translateY(-100px)' }
], {
duration: 1000,
fill: 'both'
})
6. Variable Content from API Responses
Fetching content from APIs can sometimes lead to variable-sized data, especially when dealing with user-generated content or dynamic advertisements.
Solution: When designing layouts, account for the maximum potential size of the content. Alternatively, use skeleton loaders to hold the space until content is fully loaded.
Suppose you’re building a news website, and each news article fetched from an API comes with a headline, an image, and a brief description. Now, while the length of headlines and descriptions might vary, images from the API always have a maximum height of 300px.
To prevent layout shifts, you can predefine the maximum space the content might occupy in your CSS:
.article {
min-height: 500px; /* Considering max height for image, headline, and description */
}
.article img {
max-height: 300px; width: auto;
}
By setting a min-height
for the .article
container, you ensure that even if an article with the largest image and the longest headline and description loads, it won’t push content below it and cause a layout shift.
Using Skeleton Loaders
Skeleton loaders are placeholders that give users a visual indication of where content will appear before it’s fully loaded. They’re especially useful when the content’s size might vary considerably, and you can’t easily define a maximum size.
For our news article example:
<!-- Skeleton Loader for an article -->
<div class="article-skeleton">
<div class="skeleton-image"></div>
<div class="skeleton-headline"></div>
<div class="skeleton-description"></div>
</div>
.article-skeleton {
display: flex;
flex-direction: column;
gap: 10px;
}
.skeleton-image {
background-color: #E0E0E0; /* Grey placeholder */
width: 100%;
height: 300px; /* Maximum height of the image */
}
.skeleton-headline, .skeleton-description {
background-color: #E0E0E0; /* Grey placeholder */
height: 20px; /* Representative height */
width: 70%; /* Making it look like varying lengths of text */
}
.skeleton-description {
width: 90%;
}
As the real content loads, these skeleton loaders will be replaced, ensuring no sudden layout shifts.
7. CSS Grid and Flexbox Reordering
a. Understanding Reordering
Both Grid and Flexbox allow visual reordering of content without changing the underlying DOM structure. This reordering is achieved using the order
property in Flexbox and grid placement properties (grid-column
, grid-row
) in Grid.
For instance, in a Flexbox layout:
.item-1 {
order: 3;
}
.item-2 {
order: 1;
}
.item-3 {
order: 2;
}
In a Grid layout:
.item-1 {
grid-column: 2;
}
.item-2 {
grid-column: 1;
}
While these properties enable responsive designs that adapt to various screen sizes, they can inadvertently cause layout shifts if not used cautiously.
b. Reordering Pitfalls
a. Accessibility Issues: Screen readers follow the DOM order, not the visual order. If content is visually reordered, it might confuse users who rely on assistive technologies.
b. Tab Navigation Confusion: The tab order for keyboard navigation follows the DOM order. Visual reordering can make the tabbing experience unintuitive and frustrating.
c. Potential CLS: If reordering is triggered by certain viewport sizes or other dynamic conditions without adequate space allocation, it can result in CLS.
c. Best Practices
a. Minimize Visual Reordering: Only use visual reordering when it’s essential for the design or user experience. For instance, promoting a primary content section to the top of a mobile view.
b. Consistent Sizing: When reordering, ensure that containers have consistent sizes across different layouts to prevent shifts.
c. Test with Real Users: Regularly test your layouts with actual users, especially those using screen readers or keyboard navigation, to identify and rectify any confusing reorderings.
d. Consider DOM Order Changes: Sometimes, it might be more beneficial to change the actual DOM order instead of relying solely on visual reordering, especially if the change enhances the linear reading experience.
d. Practical Example
Imagine a responsive website layout with a sidebar and main content. On wider screens, the sidebar appears first, but on mobile screens, you want the main content to be prioritized.
Using Flexbox:
.container {
display: flex;
flex-wrap: wrap;
}
.sidebar {
flex: 1;
order: 2; /* Appears second on mobile */
}
.main-content {
flex: 3;
order: 1;
/* Appears first on mobile */
}
While this achieves the desired visual result, remember to test the experience across devices and user scenarios to ensure no unintended layout shifts or accessibility issues.
Controlling Grid and Flexbox Flow
As we dive deeper into the intricacies of CSS Grid and Flexbox, it’s imperative to touch on some advanced techniques that can offer granular control over how content flows and is reordered. Mastering these techniques can not only enhance layout precision but also mitigate potential layout shift issues.
a. Grid Template Areas
One of the standout features of CSS Grid is the grid-template-areas
property, allowing developers to define layout areas and control their placement semantically.
.container {
display: grid;
grid-template-areas: "header header" "sidebar main" "footer footer";
}
.header {
grid-area: header;
}
.sidebar {
grid-area: sidebar;
}
.main {
grid-area: main;
}
.footer {
grid-area: footer;
}
For responsive designs, this property can be redefined to reorder content based on viewport sizes:
/* Mobile-first layout */
.container {
|grid-template-areas: "header" "main" "sidebar" "footer";
}
/* Layout for larger screens */
@media (min-width: 768px) {
.container {
grid-template-areas: "header header" "sidebar main" "footer footer";
}
}
b. Flexbox flex-flow
The flex-flow
property in Flexbox is a shorthand for flex-direction
and flex-wrap
. It dictates the primary axis of the layout and whether items should wrap to the next line.
By combining flex-flow
with media queries, developers can create intricate layouts that adapt seamlessly to various screen sizes.
/* Column-based layout for mobile */
.container {
display: flex;
flex-flow: column nowrap;
}
/* Row-based layout for wider screens */
@media (min-width: 768px) {
.container {
flex-flow: row wrap;
}
}
c. Implicit and Explicit Grids
When working with CSS Grid, understanding the distinction between implicit and explicit grids is crucial. An explicit grid is defined using grid-template-rows
and grid-template-columns
, whereas an implicit grid forms automatically when placing items outside the explicit grid.
Unintentionally creating implicit tracks can lead to unexpected layout behaviors. To control this:
.container {
grid-auto-rows: 50px; /* Sets a default height for implicit rows */
grid-auto-columns: 200px; /* Sets a default width for implicit columns */
}
d. Using auto-fill
and auto-fit
For layouts where the number of items is dynamic, auto-fill
and auto-fit
can be game-changers. They allow the grid to adjust automatically based on the container size and item size:
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
The above code will automatically create as many 200px columns as can fit in the container, adjusting the size if there’s extra space.
Real-world Implications of CLS
A few years ago, I worked as a Chief Technology Officer on a news website plagued with high CLS issues. Advertisements, vital for revenue, were the primary culprits. Without predefined spaces, ads would push content around, leading to frustrated users and increased bounce rates.
By merely setting aside space for ads and optimizing media loading, we reduced the site’s CLS by over 70%. Not only did user engagement improve, but the site also began to rank better in search results, a testament to the importance of CLS and user experience in SEO.
Useful Tools
There are several tools to help diagnose and remedy CLS issues:
- Chrome DevTools: The Performance panel gives insights into elements causing shifts.
- WebPageTest: Provides a visual comparison before and after optimizations.
- PageSpeed Insights: Offers specific recommendations for improving CLS.
By addressing the core issues contributing to layout shifts, developers can significantly improve user experience and website performance.