How we eliminated almost all the Cumulative Layout Shift (CLS) from our website
At Bixoto we strive to have a fast and efficient website so our customers can buy their Christmas gifts even if they are outside on a 3G connection. One of our goal in the last months was to completely eliminate the layout shifts from all the pages, something that’s measured as the “Cumulative Layout Shift” score (CLS).
This issue is caused by pages that render content before they loaded all their resources (fonts, images, CSS) and don’t properly prepare the space for these resources, creating layout shifts when the browser loads these. A simple example would be some text with a large image above it: when you open the page on a bad connection, the text is quick to load, but a few seconds later the image loads and “pushes” the text down. To avoid this, one should leave the space for the image right from the beginning.
We did this on all pages, but we still had a small amount of CLS on some of them. In the end, we found the culprit: the font. We use a non-standard font on all pages, and this causes some shift between the time the page is first rendered and the font is fully loaded.
The first objective was to reduce the font to the bare minimum. Since we sell only in Western Europe, we could remove some glyphs we don’t use from the font. Using fontcharlist
we were able to list all the characters supported by the font. To remove all characters except the one we are interested in (Latin and Latin-1 supplement characters), we used Fonttools’ pyftsubset
:
poetry run pyftsubset INPUT.woff2 \ --output-file=OUTPUT.woff \ --unicodes=U+0020-007F,U+00A0-00FF \ --with-zopfli
This reduced our font files by 3x! In addition to this, we declared the font files as resources to preload:
<link rel="preload" as="font" href="/fonts/….woff2" crossorigin>
These two optimizations reduced our CLS close to 0.000 on all pages.
If this is applicable to you, make sure the Unicode ranges you use to subset your font include all the characters you need. As everything, we validated this approach on our development instance before putting it in production.