How to subset your font
Custom web fonts can be large and impact performance. If we only use a subset of characters for a site logo or heading you can take steps to improve the impact a font has.
On this blog I use a custom web font for my name Dan Stranberg, It's from Google Fonts and it's called Kaushan Script. If I were to include the whole font on my page using the code below, it would fetch ~ 23.8kb.
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Kaushan+Script&display=swap" rel="stylesheet">
It's fairly large, but I know which letters I need, so why not include only those, so we can
quickly improve on this by specifying the text we want:
&text=DanStrandberg.
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Kaushan+Script&display=swap&text=DanStrandberg" rel="stylesheet">
Now we are down to 2.2kb, that's a big improvement! But even if the font is a lot smaller we get
Flash of unstyled content (FOUC)
because we are using
&dispay=swap
when loading the font. We can use
tools like font-style-matcher
to help, but it's not always easy to find style fallback. We could set
it to
&display=block
and the text would be hidden until the font loads but let's try to avoid this.
Avoid blocking rendering with font-display: block.
The approach above loads the fonts from another origin, it's simple, it works, but it would be better
to load it from our own origin, because
http2
can then reuse the same connection. We can drop the
preconnect
hints, and with Google Fonts we can download the
woff2
file and host it on our own. If we
visit
https://fonts.googleapis.com/css2?family=Kaushan+Script&…
we would get the css.
@font-face { font-family: 'Kaushan Script'; font-style: normal; font-weight: 400; font-display: swap; src: url(https://fonts.gstatic.com/l/font?kit=vm8vdRfvXFLG3OLnsO15WYS5DF76ytV3MqUR08SYa061fDRGbw&skey=31e8905c8b129a4e&v=v9) format('woff2');}
We can download the font from the url https://fonts.gstatic/l/fontkit=k3…, and put it in our static folder. Then use preload to mark it as high priority to minimize font swap time.
<link rel="preload" as="font" href="/font.woff2" type="font/woff2" crossorigin="anonymous"><style> @font-face { font-family: 'Kaushan Script'; src: url('/font.woff2') format('woff2'); font-weight: normal; font-style: normal; font-display: swap; }</style>
Fix the FOUC
We will still hit the Flash of unstyled content (FOUC) though, especially on slower connections, and it's not always possible to find a good fallback font as base before the swap. The differences before and after a swap can be very noticeable and may impact the impression of the site.
We did however get the font down to 2.2kb, small (enough?) to be inlined using base64 encoding. The size we get depends on the font and the number of characters we need. If you're on a Mac, you can simply do base64 font.woff2.
Then inline it like so:
<style> @font-face { font-family: 'Kaushan Script'; src: url(data:font/woff2;charset=utf-8;base64,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) format('woff2'); font-style: normal; font-weight: 400; font-display: swap; }</style>
More resources
This post does leverage Google Fonts to do the heavy lifting of generating a subset of the font. I like this approach because it's easy, and you don't need to install external tools.
You can do it yourself with python library fonttools and glyphhanger. The latter can crawl your pages to only include letters you use, because you might want to use a custom font for more than just a logo or header.