Tech Highlight: Improving Cumulative Layout Shift score for Kelley Blue Book
In this blog post, the engineering team at Kelley Blue Book (KBB) – which is part of the Cox Automotive family of brands – walks you through recent web performance optimizations for KBB.com home page.
Written by Valentyna Yurtyn, Principal Software Engineer, Cox Automotive
Our engineering teams develop and maintain several high-traffic consumer web sites, such as KBB.com and Autotrader.com. We aim to combine frictionless user experience with quality, trustworthy content to meet our vision of transforming the way the world buys, sells, owns and uses cars.
Web performance analysis helps us understand how visitors perceive our site performance and measure impact of updates to user experience. Over the last year, our engineering team has worked on various aspects of web performance. Keep reading to learn about the changes we have implemented to reduce Cumulative Layout Shift for KBB.com home page.
Cox employees collaborating at work
What is Cumulative Layout Shift (CLS)?
According to this Google Developers Article, “Cumulative Layout Shift (CLS) is an important, user-centric metric for measuring visual stability…” The metric flags shifting of content that happen without user interaction and as such is considered unexpected by visitors browsing the site.
In the screenshot below, you can see how even a small 50px shift of the element marked with the blue border moves the element marked with the red border down, below its initial placement. As we have all experienced, it is frustrating to accidentally tap on the wrong place or lose focus when the text you started reading unexpectedly shifts. Optimizing CLS score helps avoiding such situations.
To guide us with suggestions on performance improvements, our engineering team utilizes Lighthouse. This open-source automated tool audits page performance and can be run against any web page in Chrome browser DevTools, as well as from command line, or as a Node module. Cumulative Layout Shift is one of six performance metrics tracked by Lighthouse. While the CLS metric accounted for only 5% of the Performance Score in previous versions of Lighthouse, current version 8 updated its weighting to 15%, as can be seen in the Lighthouse Scoring Calculator. Additionally, in 2021 Cumulative Layout Shift metric was included in Google page experience signals as part of Core Web Vitals. Because of the increased emphasis on CLS in Lighthouse and Core Web Vitals, we chose to focus on this metric as a part of our overall performance effort.
The CLS metric is most successful when approaching zero. The lower the CLS score, the less unexpected shifting our visitors see when browsing a page. Thus, our goal was to get the CLS score as close as possible to zero for the most pleasant user experience. As the first step, we decided to optimize Cumulative Layout Shift for the initial (first) viewport – the visible area of the web page and page elements as they come into view and before visitors start interacting with the page. We defined 4 primary areas of improvement: optimizing fonts, server-side rendering styles, adding stability to visual elements on the page and optimizing advertisement modules.
Layout shift related to fonts is usually caused by a slight delay in displaying text with custom fonts (so called “Flash of Unstyled Text”). To display page content as soon as possible, browsers display text with default fonts while downloading custom fonts. When custom fonts become available, browsers apply them to text. The difference in width between default and custom fonts can be significant, causing re-adjustment of text sections of the page.
The screenshot below illustrates how applying custom font to the “Shop Smart – Step by Step” heading causes the text to wrap onto the next line, and shifts the module marked with the blue border below its initial placement:
To address Flash of Unstyled Text, we tested out multiple configuration settings for web fonts and decided on the following setup:
- Load custom fonts from Google Content Delivery Network.
- In addition to custom fonts, add generic family names to “font-family” in CSS that are the closest to our custom fonts by width.
- Set “font-display” value to “swap.”
- Preload custom fonts used in the first viewport.
Even though layout shift with this setup is still possible, these changes minimized layout shift related to fonts.
Layout shift related to styles is usually caused by a slight delay in loading and applying styles (so called “Flash of Unstyled Content”).To avoid a performance impact from downloading external CSS stylesheets, we render styles on the server with the help of Emotion library. With this approach, browsers receive page HTML document with all styling embedded and can efficiently parse and display HTML content.
Minimizing shift for visual elements
Some of the layout shifts for visual elements can happen when server-rendered HTML elements such as img or iframe lack dimensions. Initially rendering such elements with width and height of zero, browsers adjust their dimensions when loading is complete. We were able to improve user experience and prevent such layout shifts by implementing styling rules and adding width/height or aspect ratios to all the images, frames, or their parent containers.
Another potential culprit of increased layout shift and worse user experience is client-side processing. We use React library, and all content rendered on the server goes through additional processing in the browser (client-side); all events are attached, and additional content or data is loaded after network requests. It is important to minimize pushing the content down or collapsing elements during such client-side processing.
We evaluated all modules visible in the first viewport that load in part or entirely on the client and implemented a set of rules to improve CLS and user experience:
- Reserve room for dynamic modules by using “min-height” and “min-width” CSS properties.
- Create skeleton images that closely match the final look of dynamically loaded modules.
- Add initial state to the modules along with loading indicators so that visitors are aware whenever the content is still getting loaded and updated.
- Minimize any updates to visual elements dimensions that are not clearly expected or initiated by our visitors.
An example of a big win with decreasing our CLS score associated with client-side processing was a change we made to an expander component. The expander used to render about 200px of text first, only to collapse this text to 50px of height a second later. By rendering the expander component in collapsed state on the server, we fixed subpar performance of the expander component, lowered our CLS score and ensured our page visitors had a better experience!
Improving advertisement modules
Advertisement content deserves its own section in this blog post, as ad content can be one of the largest contributors to layout shifts. With most ad rendering happening on the client, and ad publishers sharing some of the prominent and highly visible page placements, maintaining satisfactory user experience is particularly important.
Kelley Blue Book uses Google Ads/Google Publisher Tags (GPT) to serve advertisement content. Even though ad slots may be rendered server-side, ad fulfillment always happens on the client. When advertisement creative is displayed within an iframe it is excluded from CLS metric measurements, however advertisement containers themselves are always included in the metric and should be optimized. This article from Google suggests some ideas of how to minimize layout shift from advertisement.
In collaboration with ad sales, operations, and product teams we implemented several important updates to lower layout shift related to advertisement:
- Ad product team established single size ads as much as possible (as opposed to rendering multiple ad sizes). When different ad sizes are allowed to render in the same spot, layout shifts become quite common as we cannot predict which ad size will end up getting fulfilled from the ad server.
- Engineering team styled advertisement containers to fit the expected advertisement size. For example, we updated containers hosting a medium rectangle 300x50px ad to have “min-width” of 300px and “min-height” of 50px.
- Ad operations team created ever-green native content to display in advertisement slots when sponsored content is not available, so that advertisement slots never need to collapse. This was a change to our previous strategy of collapsing unsold advertisement slots to free up screen space in mobile devices.
- Engineering team implemented consistent styling and placement for advertisement labels.
Looking at the end-result of our efforts, you can tell that the homepage now maintains good visual stability of HTML elements:
Example of updated first viewport
Validating the changes
We validated all changes by running Lighthouse tool and tracking improvements of Cumulative Layout Score value (part of Lighthouse Performance Score). After working through all optimizations not only we achieved the best possible CLS score of zero, but also established a set of best practices to maintain this perfect CLS score in the future.
Filmstrip in the Lighthouse report below shows how visually stable KBB.com homepage is after all our updates:
We should consider that CLS metric in Google Lighthouse measures only layout shift within the first viewport and does not emulate user scrolling down the page or interacting with page elements. However, as part of the Core Web Vitals, CLS metric is “…measurable in the field, and reflects the real-world experience.”
I plan on covering Core Web Vitals CLS optimizations in my next blog post, so stay tuned to learn more about the inner workings of Kelley Blue Book Engineering team!
Learn how Cox Automotive technology teams are building a more connected and mobile future here.
Are you interested in joining our engineering team? Explore open positions here.