With variable fonts, more typographic richness and influence is coming to the web and this at a relatively low file size. You want to benefit from that in your next web project, but still, you don’t want to bet everything on this new technique? This article will show you how to integrate a variable font in your website and use classic web fonts as a fallback.

Quick intro and why to use a fallback

If you are new to the subject of variable fonts, you might want to read How to start with variable fonts on the web that covers all the basics and come back later. But to give you a very brief introduction: traditional (static) web fonts, as we know them, store one style in one individual file; one for regular, one for bold, one for italic and so on. With a variable font, you can store and create several styles using one single font file. This brings more typographic flexibility and might also come at a lower file size. The website v-fonts.com is a great place to find available variable fonts. Some of them are for free and some are commercial. The free ones almost always offer free static web fonts too (e.g. Source Sans Variable and the non-variable Source Sans). Almost all commercial variable fonts can only be purchased as an add-on if you buy the whole family (of static fonts), so you get them anyway.

Variable fonts are now supported by all modern major browsers (October 2018). But what if someone uses an older browser? They would see system fonts as a fallback. This all-or-nothing approach might be too radical for most projects. It compromises the whole typographic voice and uniqueness of a site for everyone who does not use the most recent browser version. Using static web fonts of the same font family as the first fallback option would be the preferable way. If the variable font is supported it should be displayed, if not, the static, non-variable web font should be shown.

The setup

Let’s do a step by step example where you can see how to implement a variable font with fallback web fonts. For this, I made a little test site with some text on it. I’ll keep it very basic for this example and only use a h1 and two p with some strong text in it.

For this example I choose the variable font Venn by Dalton Maag, which has two axes:

a weight axis with a range of 300 to 800 and

with a range of 300 to 800 and a width axis with a range of 75 to 125.

With this you can create a lot of styles, going from Light Condensed to Extra Bold Expanded and everything in between.

If the browser supports the font variations, let’s benefit from this range of widths. Our goal for this example is, that the text should become narrower on small screens to fit more words into one line. On large screens, the text can be wider to distribute more evenly throughout the given space. If the browser does not support the variable font, it should replace it with the static web fonts for Venn Regular and Bold. In this case, the width won’t change.

1. Load the fonts

At first, we have to load the different fonts we need:

Venn_VF.woff2 is the variable font file Venn_Regular.woff2 as a fallback Venn_Bold.woff2 as a fallback

Integrating a variable font by using the @font-face rule is very straightforward, as you can read here. I give the variable font a different name than the static font to make it distinguishable and will call it Venn VF . This is how I load it:

@font-face { font-family: 'Venn VF'; src: url('Venn_VF.woff2') format('woff2-variations'), /* will be the standard and works in Safari now */ url('Venn_VF.woff2') format('woff2'); /* for the other supporting browsers */ }

Now I load the static fallback fonts for the regular and bold style and name it Venn :

@font-face { font-family: 'Venn'; src: url('Venn_Regular.woff2') format('woff2'); } @font-face { font-family: 'Venn'; src: url('Venn_Bold.woff2') format('woff2'); font-weight: 700; }

But wait a minute – won’t the browser download all the fonts now, the variable and the static ones? No, it will not. “A web font is only downloaded when it is used in a CSS selector that matches a DOM connected node” I learned in Bram Stein’s talk about web fonts performance. So modern browsers only download a font when it is needed to display something on a page. For our example, this means the browser won’t download any of the font files for now because we have not styled anything with it yet. Let’s do this next.

2. Style the text with the fallback fonts

A variable font is a progressive enhancement for your typography, so see it as an optional improvement. This means we have to create a solid basis first by styling the site with the static web fonts:

body { font-family: 'Venn', sans-serif; }

To write a really robust font stack you should also add some system fonts, but I’ll keep it simple, for this example (find out more about this in my German article about the CSS font stack). This is how my site looks now showing the static web fonts:

Now you might think why not writing the font stack this way:

body { font-family: 'Venn VF', 'Venn', sans-serif; }

At first glance, this makes perfect sense. If the browser does not support or understand the variable font, it will move on to the static web font. The problem is, that some browsers unnecessarily download all the font files if you write the font stack this way. The most current browsers (Safari 12, iOS Safari 11.4 and Chrome 69) did not download the static fonts, but Firefox 62 did. All of them support variable fonts and should only download the fonts they need. On the other hand Edge 16 (an older version which does not support variable fonts) downloaded the variable font. So how can I get my browser to only download the fonts it needs? Find the answer to this in the next step.

3. Use CSS feature queries

All the browsers that support variable fonts also support CSS feature queries. You can check if variable fonts will work in the browser and only then apply certain styling. The feature query looks like this:

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

I only want to style my text with the variable font if the browser supports it. This means writing another font-family declaration inside a feature query. I put it after the first one so it will override the fallback styling. Now our code looks like this:

body { font-family: 'Venn', sans-serif; } @supports (font-variation-settings: normal) { body { font-family: 'Venn VF', sans-serif; } }

@supports is the secret ingredient that makes it possible to style things differently when variable fonts work in the browser. Now Firefox 62 will only download variable font and Edge 16 will only download the static fonts.

4. Add styling for the variable font

In the next step, we will tweak the typography in our example. We start mobile first and go upwards towards bigger screens.

Base styling and small screens

We defined the font-family in the body, now let’s start styling the fallback web font first to have a typographic foundation. To keep it simple I only focus on the very basic typographic settings here:

h1 { font-size: 1.8rem; line-height: 1.1; } p { font-size: 1.1rem; line-height: 1.5; }

Now let’s add some styling for the variable font where we change the width of the heading and the paragraph:

h1 { font-size: 1.8rem; line-height: 1.1; font-variation-settings: "wdth" 75; } p { font-size: 1.1rem; line-height: 1.5; font-variation-settings: "wdth" 88; }

From now on we don’t need a feature query, because browsers that don’t get the font variation will just ignore it. You would need a feature query if you want to adapt the font-size or any other typographic setting only for the variable font, that an older browsers would understand. Depending on the capabilities of the browser our site will look like this:

Medium screens

The next breakpoint might be a large phone for example. I’ll add a media query and since the screen is wider now, I want to increase the line-height of the paragraphs:

@media screen and (min-width: 460px) and (max-width: 699px) { p { line-height: 1.6; } }

Because I have more space now, the heading and the paragraphs can become a bit wider than on the smallest screen. I’ll change the settings of the variable font for the h1 and p :

@media screen and (min-width: 460px) and (max-width: 699px) { h1 { font-variation-settings: "wdth" 85; } p { line-height: 1.6; font-variation-settings: "wdth" 95; } }

This result in:

Larger screens

Now let’s go for our last breakpoint in this example, wider screens of 700 px and above. Here I want to bump up the font size of the text and add even more line height to the paragraphs. I’ll put this into another media query:

@media screen and (min-width: 700px) { h1 { font-size: 2.5rem; } p { font-size: 1.2rem; line-height: 1.7; } }

For the variable font I want the paragraphs to be at 100% width (like the fallback font) the h1 should still be a bit condensed to be more compact. Again I add the font-variation-settings to the h1 and p selectors inside the media query:

@media screen and (min-width: 700px) { h1 { font-size: 2.5rem; font-variation-settings: "wdth" 90; } p { font-size: 1.2rem; line-height: 1.7; font-variation-settings: "wdth" 100; } }

That’s it. In classic CSS it looks pretty complex, but of course, you could easily write more clear code by using SCSS or another preprocessor. There is only one tiny optimization left to do.

5. Prevent artificial browser styles

This is an error that unfortunately occurs in most browsers at the moment when user agent styling and variable fonts come together. Then most browsers don’t access the weight axis of the variable font and create a faux bold. Depending on the browser this will result in a sometimes bold-but-not-enough or too-bold-bold.

One solution is declaring the range of the weight within the @font-face rule:

@font-face { font-family: "Venn VF"; src: … font-weight: 300 800; }

This will help Safari to know that the variable font contains the weight it needs for bold text, used on strong or h1 to h6 by the user agent default styling. Sadly Chrome and Firefox do not get it and won’t access the weight axis of the variable font. So for them, you have to define the exact weight:

h1, strong { font-variation-settings: "wght" 700; font-weight: 700; }

Why writing the font-variation-settings and font-weight ? For some reason Chrome won’t make the text bold when using only the high level property font-weight . This seems rather stupid and redundant, but that’s the way it is right now. Eventually it will be fixed, but for now, this is an important step because faux bolding can look very problematic in some browsers as you have seen in the example before.

And to make it even more complicated, you can only define font-variation-settings once. The last time you write it, overrides everything you wrote before. So if you want to change the weight and the width you will have to write this:

strong { font-variation-settings: "wght" 700, "wdth" 88; }

Update 5 November 2018: Laurence Penney pointed out to me that it actually does work in Chrome and Firefox when you add the weight range within the @font-face rule. I did some testing and confim that. Firefox does not require the range, Chrome and Safari do. So the changes describe above are not necessary.

Summing it up

Adding a web font as a fallback for the variable font is not that complicated and makes a lot of sense if you don’t want to lose the whole typographic experience of a website to every browser where font variations are not supported. The typographic system of the website should be as solid as possible with the fallback web fonts. Then add the variable font styling on top. Maybe you want to use a feature query for that, if it effects some styling that would change the fallback font too. But if it’s only the font-variation-settings , the a browser that does not understand it will ignore it. I prepared this example on CodePen where I added a toggle so you can compare the site with the fallback fonts and the web font. I also commented the code, so you can take a closer look.

See the Pen Using a variable font with fallback fonts by Oliver (@glyphe) on CodePen.

Any suggestions? All the things I wrote about here I found out by doing some tests in different browsers. If you observed some other behavior, spotted an error or have different approaches, write it in the comments below or hit me up on Twitter!