Skip to content

This website uses a variable font

Around the end of 2021, this little corner of the web started using a variable font. Newsreader is a beautiful design from Production Type commissioned by Google Fonts, whose sources are available on GitHub under the Open Font license (OFL).

I'd been reading about variable fonts for a while, but had probably never actually typed up the CSS with my fingers for a project up to that point. Here are a few things I learned about self-hosting variable fonts in the process of freshening up the typography on here.

A first good thing to do is add all the necessary properties to the @font-face declaration

Newsreader comes with two separate font files, one for roman and one for italics. Each file has two variation axes: the weight (wght) and the optical size (opsz).

My initial @font-face declaration for the roman style was pretty straightforward, and replacing the old typeface worked well out of the box.

@font-face {
font-family: Newsreader;
font-style: normal;
src: url('/fonts/Newsreader.woff2') format('woff2-variations');
}

body {
font-family: Newsreader;
}

It became apparent rather soon, however, that this setup only works in Firefox. Things that were supposed to look bold — headings, <strong>, and the like — didn't look as good in Safari and Chrome, as seen in this demo. Both had trouble, in different ways, rendering font-weight: 700.

A grid showing the rendering of font weight 400 and 700 across Firefox, Safari and Chrome. Firefox looks as expected, while Safari produces an exaggerated bold, and Chrome looks almost like the normal weight.

Each browser renders font-weight: 700 differently.

Safari produces what seems to be twice-bolded text by obtaining the appropriate bold weight from the variable font but then synthesizing a bolded version of that. In fact, disabling the browser's font synthesis with font-synthesis: none fixes the issue. As for Chrome, whatever it's doing, it's not improved by this declaration.

What gives? wght was supposed to be a registered variation axis that can be controlled via font-weight but, as seen in the demo, only font-variant-settings: "wght" 700" works consistently across browsers for bold text.

It was only obvious in retrospect that a webfont with a weight variation axis absolutely needs its weight range spelled out in the @font-face declaration, for the axis to be correctly mapped to the font-weight property. You do that with the @font-face/font-weight property:

@font-face {
font-family: Newsreader;
font-style: normal;
font-weight: 200 800;
src: url('/fonts/Newsreader.woff2') format('woff2-variations');
}

Similar declarations are needed for other registered axes:

Adding the correct @font-face properties enables declarations such as font-style: oblique 30deg or font-width: 125% to work as expected.

Variable fonts can also technically include the registered ital axis to bundle both roman and italic styles in a single file. Using such a font in CSS is a bit more complicated, so as a consequence pretty much everyone bundles romans and italics in separate files.

A note on format() in the src descriptor

By the time I got to reading up on variable fonts, the syntax for @font-face/src in the spec did no longer match the recommendations I'd found elsewhere for hinting to the browser that the url() points to a variable font.

Where were format('woff2-variations') and format('woff2 supports variations') coming from? But, more importantly, how to usefully hint variable fonts today? I made a browser test to check support for the various syntaxes and came to these conclusions:

With these findings in mind (which you can now find in the compatibility table on MDN), variable fonts using the WOFF2 format only really need the format('woff2-variations') hint to ensure only browsers that have support for variations download the font file. This syntax works today and will most likely work indefinitely.

It's also probably a good idea to optimize variable web fonts (as long as you're allowed to)

Between the two variation axes (weight and optical size), Newsreader packs the information from nine separate source fonts. That adds up to a total of 455kb for the two WOFF2 files it ships. This is the overhead you have to pay for being able to produce as many fonts as you like, everywhere across the design-variation space.

Newsreader is based on nine master fonts, here distributed horizontally along the Weight axis (wght) and vertically along the Optical size (opsz) axis.

This overhead is not always justified for web pages, so a common thing to do is split the font into many pieces across an aspect of the font. fontTools is a collection of libraries and command-line tools for all sorts of font manipulation written in Python.

Richard Rutter has a guide on installing fonttools on macOS and using it to subset variable fonts. One understanding of subsetting is to cherry-pick a set of Unicode ranges that cover all the glyphs your content needs and call it a day. A more resilient approach is to effectively split the font in a series of scripts („alphabets”). As long as you know the Unicode bounds of these scripts — and Paul Hebert reminds us that, in a pinch, you can grab some ranges from Google Fonts — this lets you serve the entire glyph set the font offers, and let the browser fetch whatever it needs to render the content of the web page.

Narrowing the variation space

In addition to glyph sets, there's another direction along which to subset a variable font. fonttools comes with the varLib.instancer module that allows you to create full instances (i.e. static fonts) from variable fonts, as well as 'partial' variable fonts that only contain a subset of the original variation space.

There is such a thing as variable lite. If, for example, you can make do with a single optical size, or you've decided wght: 376 is the perfect weight for body text, and you only need another bold weight to go with it, it might be worth it to either:

The command below pins the optical size at 12 while reducing the weight range to [300–700] for the Newsreader roman font:

fonttools varLib.instancer ./Newsreader.woff2 wght=300:700 opsz=12 -o ./Newsreader-partial.woff2

This cuts the file size in half, from 215kb to 98kb. I've intentionally picked a "bad" example, because the reduction is foremost attributable to removing the optical size variation axis. If we omit the wght=300:700 option in our command we end up with mostly the same reduction (215kb to 102kb):

fonttools varLib.instancer ./Newsreader.woff2 opsz=12 -o ./Newsreader-partial.woff2

Recall that Newsreader uses font data from wght=200,400,800, corresponding to the minimum, default, and maximum weight. To render the wght=300:700 range we still need data for the new minimum and maximum weight. To truly see a reduction in size, our weight range would have to be defined in such a way that at least one set of font data can be dropped from the variable font, for example wght=400:800:

fonttools varLib.instancer ./Newsreader.woff2 wght=400:800 opsz=12 -o ./Newsreader-partial.woff2

Legal aspects of subsetting

Fonts are distributed under a variety of licenses that govern their use. The following apply to fonts distributed under the SIL Open Font License, as is the case for Newsreader. Obviously, I'm not a lawyer so this is just my layperson understanding of the matter.

When producing a derivative work you need to change the font's name to something completely different when and if the OFL license mentions a Reserved Font Name (RFN) as part of its copyright line. Newsreader has the following copyright line:

Copyright 2020 The Newsreader Project Authors (http://github.com/productiontype/Newsreader)

A sample copyright line from the OFL license that does not reserve the font name.

If the authors wanted Newsreader to be an RFN they would have mentioned that in the copyright line, and I would then have been compelled by the license to rename my subsets some cringeworthy pun like, I dunno, Oldswriter.

Copyright 2020 The Newsreader Project Authors (http://github.com/productiontype/Newsreader), with Reserved Font Name Newsreader

A sample copyright line from the OFL license that reserves the font name.

Anyways, just something to be aware of. And if I'm getting this wrong, I would appreciate a nudge!


Addenda: readings, tools, and resources

I can't help myself from turning every article into a little compendium, so here are some tangentials I've found useful.

OpenType Font Variations is part of the OpenType specification. John Hudson announced this addition to OpenType 1.8 with Introducing OpenType Variable Fonts, highly recommended reading.

A few guides to getting started:

An excellent tool for inspecting variable fonts (and, really, any kind of webfont) is Wakamai Fondue, available on the web and at the command line.

Finally, a couple of fun facts that surfaced while researching this article: