Proper websites, done properly

Improving font performance

10 minute read time.

Webfonts are a wonderful thing. They allow us so much more creative freedom than we were able to have in the past.

A bit of history

In years gone by, you were stuck with your operating system default fonts. For sans serif you'd likely be seeing Arial (Windows)/Helvetica (Mac), for serif fonts you'd get Times New Roman (Windows)/Times (Mac), and for monospaced fonts you'd be seeing Courier or Courier New (Windows)/Monaco (Mac).

These have changed over the years and depending on what version of Windows or macOS you were on you could see any number of different typefaces. These are commonly known as 'System UI' fonts. In CSS, when we describe our fonts we define a 'family'. In essence, this is just a prioritised list of what fonts we want to request and allow. The first in the list are our most preferred, and those at the end of the list are our fallbacks and last-ditch attempts at getting something nice to display.

:root {
    --font: system-ui, -apple-system, Segoe UI,
        Roboto, Noto Sans, Ubuntu, Cantarell, Helvetica Neue;
}

Currently, macOS will use the San Francisco sans serif family, and Windows 11 will use Segoe UI Variable. Linux/Unix/Solaris/BSD systems could use any number of different fonts, and it will completely depend on how a user has their system configured. Chances are you'll see Arial as a Windows-compatible font family if they have a generic free Windows font package installed.

System UI fonts

It's possible these days to just called on a System UI font family and your browser should, for the most part, give you something sensible. There's also a PostCSS plugin that will give you a hand if you're still supporting older browsers that won't let you do this just yet:

:root {
    --font: system-ui;
}

This would give us the same output from the first example, and is supported in all current evergreen browsers.

This is a great choice for performance if you don't need a specific brand font or font style on your site - these fonts are always available and don't require additional downloading to display your site properly. The only real down side is that you cannot be sure which font users will see, and how it looks will vary across different operating systems.

This isn't uncommon now that the major operating systems have decent base fonts, and font rendering on screen is so much better now than several years ago. Even big sites like GitHub use this approach to save on serving fonts.

There's even sites dedicated to helping you decide on font families for your site that require no downloading and use only system fonts, and practical guides (some outdated content here!) on implementing them too.

Variable fonts

I've previously mentioned variable fonts as a great new development for for front end development (and indeed on this site I'm using the variable version of Rubik), but in general practice I'm still using standard web fonts - a file for each different font weight that I need. This is fine, until you have to build out designs that have normal, medium, semi-bold, bold and black font weights and then all of a sudden your fonts might be the biggest part of downloading any page on your site.

For this site, I'm using the font family Rubik, and the variable web font for this is actually a very slim 35KB for essentially unlimited font weights and all the glyphs I'm likely to need.

If we're going to use web fonts, we should be more sensible. We don't really need more than two weights in a font for the most part. Three at a push. Let's look at an example of a web font and see what we can do to improve its general performance.

Subsetting

Roboto is a Google sans serif font family, it's free and it's got a lot of font weights. Downloading it from Google Web fonts and looking over the desktop font sizes, we see that for regular in TrueType Font (TTF) format, the filesize is 168KB. Bold is another 167KB file.

Webfont generators (such as Font Squirrel) allow us some great choices when creating webfonts from downloaded font files.

One of the best things we can do here is called subsetting. We can tell the webfont generator what characters/glyphs of a font we want, or what ranges of characters we want and discard bits we're never likely to use in order to slim down big font files.

Converting Roboto regular and bold weights using the 'basic subsetting' option results in a WOFF2 file of 19KB for regular, and 20KB for bold, an reduction of over 88% compared to the desktop fonts. The best part of subsetting is that if you know for certain you're only going to use a very specific range or set of characters you can really squish the file size a good amount.

Subsetting Roboto to only have lowercase, uppercase, numbers and basic punctuation slims the file sizes down from 20KB to 11KB for bold and 19KB down to 11KB for the regular weight.

A real world example

On a recent project, I noticed an issue with an advert placement using three entire font family weights (3873 glyphs total) to render an advert space that used only 64 characters from all the font weights: 14 from semi-bold, 38 from regular and 12 from the light font weight. The site was loading over 210KB of fonts for a single advert.

Doing a bit of clever subsetting, I managed to get the three font file sizes down from 72.6KB (regular), 70.1KB (light), and 67.7KB (semi-bold) to a much slimmer 12.6KB (regular), 13.7KB (light), and 4.9KB (bold) - totalling 31.2KB instead of the previous 210.4KB.

We looked to improve a few other things in this performance sweep, but overall we managed to reduce the advert size from over 250KB to just 43KB by doing sensible, logical improvements.

Finding unique characters

function findUnique(str){
    return [...str].reduce((acc, curr)=>{
        return acc.includes(curr) ?  acc  :  acc + curr;
    }, '')
}

const subsetChars = findUnique('someRandOMstring0fl3tt3r54ndnum83rs and punctuation!.!hahaha_');

console.log(subsetChars);

This will print out: someRandOMtrig0fl354u8 pc!.h_. (length of 29 characters)

Had we needed to subset Roboto regular into just these 29 characters, we could have a font file size of just 5KB. This is a phenomenal way to shave off page weight and improve performance.

Final thoughts

Choosing fonts and making sensible performance choices boils down to this: Do you really need to use a custom typeface, and if you do really need it - do you really need to use so many variants of it?

If your brand doesn't demand a custom typeface, go for System UI fonts. From a performance perspective you cannot lose here: zero download time, and generally decent looking fonts.

If you do need a custom font, consider using the minimum number of weights, and subsetting them to the fewest glyphs you can to get the smallest download file size for them.