DEV Community

Cover image for SvelteKit Fontaine: Reduce Custom Font CLS
Rodney Lab
Rodney Lab

Posted on • Originally published at rodneylab.com

SvelteKit Fontaine: Reduce Custom Font CLS

🏄🏽 Font Swap CLS

In this post, we see how you can use SvelteKit with Fontaine to reduce Cumulative Layout Shift (CLS) by up to 100%, when working with custom or self-hosted fonts. We first look at how the problem arises, then turn to a solution, using Fontaine. If that’s what you came here for, then let’s roll our sleeves up!

We focus on SvelteKit here, and use Fontaine to solve the problem. Our solution relies on Vite support, so the same approach should work just fine with Astro, for example. Though if you are working with Astro, or want to know how Fontaine works under the hood, see the recent article on reducing Astro font swap CLS using Capsize.

🤔 Problem: Ensuring Text Remains Visible during Webfont Load

When running Lighthouse web development tooling, if your site uses webfonts or Google fonts, you might get the suggestion: “Ensure text remains visible during webfont load”. This is because text might well be invisible, on a slow connection, while the browser loads the webfont. Somehow making the text visible, even in a font other than the preferred one, will improve user experience, making the page appear to load quicker.

🫣 Solution: Set font-display: swap

The advice offered on the Chrome Developers site, to ensure the font remains visible, is to use the @font-face font-display: swap directive. Browsers will have some fonts available by default. Typically, Times New Roman and Arial are included in this list, though for Android devices, Roboto is your safe default.

Setting the font-display: swap directive instructs the browser to display the fallback font, instead of the webfont, whenever the webfont is not available quickly. Then, once the webfont has fully downloaded the browser swaps out the fallback font with the webfont. Typically, you will set Arial as the fallback for sans fonts and Times New Roman for serif. Then, include Roboto as a second fallback for both (serif and sans-serif), to cover Android devices.

😬 New Problem: Font Swap introduces Layout Shift

SvelteKit Fontaine: text rendered in the fallback Arial font with the words compiled, compact and complete able to fit on just two lines.

SvelteKit Fontaine: text rendered in the webfont font with the words compiled, compact and complete needing to be spread over three lines.

Our approach improves user experience, making the page load faster. However, it introduces a side effect: layout shift. Fonts have different metrics — the height of a capital letter, how far glyphs like l and g ascend or descend, and so on. Using the default values for these metrics, for the fallback font, will typically result in a layout shift, when the browser replaces the fallback with the actual webfont. Let’s see how Fontaine can help!

🎁 Final Solution

Fontaine has metrics for common webfonts, it sources them from the Capsize typography tooling. Using modern CSS, it can override the layout of the fallback font. This might make it look a little more compact or stretched out (depending on font relative sizes), than normal. However, this layout override results in zero, or very little layout shift when the browser swaps fonts during page load.

Using modern CSS, Fontaine adds overrides for the fallback fonts. We will see how to configure it in the next section. Below, is the code generated by Fontaine for the Overpass webfont with Arial and Roboto as fallbacks.

:root {
  /* TRUNCATED... */
  --font-family: "Overpass", "Overpass fallback";
    /* TRUNCATED... */
}
@font-face {
  font-display: swap;
  font-family: Overpass;
  font-style: normal;
  font-weight: 400;
  src: url(../../../fonts/overpass-v12-latin-regular.woff2) format("woff2");
}
@font-face {
  font-family: Overpass fallback;
  src: local("Roboto");
  size-adjust: 100.5896%;
  ascent-override: 87.7824%;
  descent-override: 38.0755%;
  line-gap-override: 0%;
}
@font-face {
  font-family: Overpass fallback;
  src: local("Arial");
  size-adjust: 100.7009%;
  ascent-override: 87.6854%;
  descent-override: 38.0334%;
  line-gap-override: 0%;
}
  /* TRUNCATED... */
Enter fullscreen mode Exit fullscreen mode

In line 3, Fontaine has added Overpass fallback to the --font-family custom property (it was originally --font-family: "Overpass"). As well as that, it has added new @font-face directives for Roboto and Arial (lines 13-28).

⚙️ SvelteKit Fontaine: Configuration

As it happens, Fontaine configuration is quite straightforward. We are assuming you self-host fonts in your project and have already included the font-display: swap directive in your CSS. Use the Google Webfonts Helper by Mario Ranftl to get the CSS snippets for your chosen webfonts, and also download the font WOFF2 files.

  1. Add the fontaine package to your SvelteKit project:

    pnpm add -D fontaine
    
  2. Update your vite.config.ts file to use the FontaineTransforms:

    import { sveltekit } from "@sveltejs/kit/vite";
    import { FontaineTransform } from "fontaine";
    import { defineConfig } from "vite";
    
    export default defineConfig({
      plugins: [
        sveltekit(),
        FontaineTransform.vite({ fallbacks: ["Arial", "Roboto"] }),
      ],
    });
    

    I picked Arial as a fallback here, as I have a sans webfont. Roboto is a second fallback, worth including for Android devices.

  3. Run your project as normal. Inspecting CSS source in dev mode, or for a built site running in preview mode, you should be able to find the CSS modifications outlined above.

💯 SvelteKit Fontaine: Checking your Work

You can check all is good by building your site and the running Lighthouse on it, locally, in preview mode. The difference will be most pronounced on mobile. For the basic layout above, I saw CLS drop from 3% to zero when I switched on Fontaine. Let me know what improvements you get.

🙌🏽 SvelteKit Fontaine: Wrapping Up

In this post, we saw why you would want to use Fontaine in your SvelteKit project, as well as how to use it. More specifically, we saw:

  • what problems Fontaine solves;
  • how to use Fontaine with Vite to reduce CLS; and
  • how to might check CLS is actually reduced.

Please see the full repo code on the Rodney Lab GitHub repo. I do hope you have found this post useful and can use the code in your own Svelte project. Let me know if you have any suggestions for improvements to the post. Drop a comment below or reach out on other channels.

🙏🏽 SvelteKit Fontaine: Feedback

If you have found this post useful, see links below for further related content on this site. I do hope you learned one new thing from the video. Let me know if there are any ways I can improve on it. I hope you will use the code or starter in your own projects. Be sure to share your work on Twitter, giving me a mention, so I can see what you did. Finally, be sure to let me know ideas for other short videos you would like to see. Read on to find ways to get in touch, further below. If you have found this post useful, even though you can only afford even a tiny contribution, please consider supporting me through Buy me a Coffee.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter and also askRodney on Telegram. Also, see further ways to get in touch with Rodney Lab. I post regularly on SvelteKit as well as Search Engine Optimization among other topics. Also, subscribe to the newsletter to keep up-to-date with our latest projects.

Top comments (0)