Variable fonts with legacy fallback

Variable fonts are OpenType fonts that can support several variations of a typeface from a single font file. That can mean a faster website experience for your visitors. Not all browsers support variable fonts yet, but that can be managed with progressive enhancement.

For our example, we’ll use the typeface Lora, by Cyreal Fonts. Using variable fonts, Lora requires just two font files to support 4 different font weights: one file for normal styles, and one for italics.

Each of Lora’s variable font files (as woff2) is about 75% bigger than a single static (non-variable) version, but even if we only use two font weights (normal and bold) the combined static files are bigger than the variable font file, so typically we will reduce bandwidth consumption with variable fonts.

As an added benefit, variable fonts can be much more flexible. For example, instead of being limited to font weights on the hundreds like 400, 500, 700, a variable font can support a font weight of, say, 625. That allows us to really fine-tune our type presentation.

Finding variable fonts

A web search will find us lots of variable font typefaces. For simplicity, however, we’ll stick with tried-and-true Google Webfonts. When we search for a typeface, there’s now an option we can tick to “Show only variable fonts”:

Google Webfonts search with variable fonts option ticked
Google Webfonts search with variable fonts option ticked

Having selected our typeface, the usual thing to do next is to select some styles and get the code to embed them into our website, either as an HTML link element or a CSS import statement. Google very helpfully provides the appropriate @font-face CSS for the visitor’s browser, so if our website visitor is using Google Chrome they’ll get the variable fonts, but if they’re using Internet Explorer they’ll get static font files.

If we want to host the font locally, we can click the “Download family” button and save the Zip file for use in our website. This is also how we’d get variable fonts from other sources. But now, we can’t rely on Google to provide the @font-face CSS for us — we need to do that ourselves.

Preparing the font files

One really nice thing about using Google webfonts as embedded fonts is that Google prepares the font files for us, in different formats that the supported browsers can use. But when we download the font as a Zip file, we don’t get that nice service — it’s self-serve from here on! What we get is the OpenType font files as .ttf, which are not compressed and are pretty big.

We have three targets to prepare for:

  • modern browsers that can load variable fonts in woff2 format
  • legacy browsers that can load static fonts in woff format
  • legacy browsers that can load static fonts in woff2 format

Among the legacy browsers that don’t handle variable fonts, there’s Internet Explorer 11 which also can’t handle woff2 font files, and some others like the UC and Baidu browsers that can handle woff2. Also, Firefox only supports variable fonts on recent operating system releases. For best results, we need to compress the variable fonts as woff2, and the static fonts as both woff and woff2.

There are two tools we can use for this, available from our package manager (or build from source files); Ubuntu installation instructions are shown here:

NB: there are other woff2 compressors, but many don’t preserve variable font properties, so stick with the Google compressor!

For each font file in the static folder, compress as both .woff and .woff2 like this:

sfnt2woff Lora-Regular.ttf
woff2_compress Lora-Regular.ttf

For each variable font (outside the static folder), we only need to compress as .woff2 because all browsers that support variable fonts also support the woff2 font format.

woff2_compress Lora-VariableFont_wght.ttf

Load fonts for legacy browsers

Web browsers need to be told how and when to load font files, and this is done by the @font-face rule. We’ll create the rules for legacy browsers first, because we need to override these later on for modern browsers.

Each weight and style we expect to use on the website needs a matching @font-face rule. To support just the normal and bold font weights (400 and 700 respectively), in both normal and italics styles, we thus need four rules.

Fonts are only loaded when the page contains text in that font style and weight, so for example if there’s only normal weight text with no italics, only the first font file will be loaded.

/* specify static fallbacks for old browsers that don't do variable fonts */

@font-face
{
    font-family: Lora;
    src: url('Lora/static/Lora-Regular.woff2') format('woff2'),
         url('Lora/static/Lora-Regular.woff')  format('woff');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face
{
    font-family: Lora;
    src: url('Lora/static/Lora-Italic.woff2') format('woff2'),
         url('Lora/static/Lora-Italic.woff')  format('woff');
    font-weight: 400;
    font-style: italic;
    font-display: swap;
}

@font-face
{
    font-family: Lora;
    src: url('Lora/static/Lora-Bold.woff2') format('woff2'),
         url('Lora/static/Lora-Bold.woff')  format('woff');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

@font-face
{
    font-family: Lora;
    src: url('Lora/static/Lora-BoldItalic.woff2') format('woff2'),
         url('Lora/static/Lora-BoldItalic.woff')  format('woff');
    font-weight: 700;
    font-style: italic;
    font-display: swap;
}

Load fonts for modern browsers

Now that we’ve set the rules for old browsers that don’t support variable fonts, we need to override them for modern browsers that do support variable fonts. We can test for those browsers with the @supports feature query. The feature we’ll use is the font-variation-settings property which legacy browsers won’t understand — and really old browsers like Internet Explorer won’t even understand @supports!

We only need to test that a browser supports the default values for font-variation-settings, like this:

@supports (font-variation-settings : normal) { ... }

Now, we need to duplicate all of the @font-face rules for the legacy browsers, but replace the font URLs with their variable font counterparts. For Lora, there are two variable font files: one for font style normal and one for italics. That means that there will be two rules for the normal font file, and two for the italics font file.

Don’t worry, the font files will only be loaded by the web browsers once — except in Edge version 17 or 18, which we can ignore since Microsoft have replaced the old Edge with a Chromium-based browser now!

/* override with the variable font for modern browsers, using ALL of the style combos above */

@supports (font-variation-settings : normal)
{

    @font-face
    {
        font-family: Lora;
        src: url('Lora/Lora-VariableFont_wght.woff2') format('woff2');
        font-weight: 400;
        font-style: normal;
        font-display: swap;
    }

    @font-face
    {
        font-family: Lora;
        src: url('Lora/Lora-Italic-VariableFont_wght.woff2') format('woff2');
        font-weight: 400;
        font-style: italic;
        font-display: swap;
    }

    @font-face
    {
        font-family: Lora;
        src: url('Lora/Lora-VariableFont_wght.woff2') format('woff2');
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }

    @font-face
    {
        font-family: Lora;
        src: url('Lora/Lora-Italic-VariableFont_wght.woff2') format('woff2');
        font-weight: 700;
        font-style: italic;
        font-display: swap;
    }

}

CSS to use the variable fonts

We can now make some use of our variable fonts. We’ll set the font family on the body tag, with a sensible fallback font. Then we’ll tweak the font’s variable settings a little, just for fun — strong elements will have a font weight of 650 instead of 700, but only on browsers that support variable fonts.

body
{
    font-family: Lora, serif;
    font-size: 16px;
}

strong
{
    font-variation-settings: "wght" 650;
}

Examples

Enough of words and code snippets, time to look at real examples. Here’s two simple HTML pages that use the Lora typeface. Load in a browser and view source to see it all come together, and perhaps check in the Network tab of Developer Tools to verify that the variable font files really are being used. Job is done.