A new font specification that can significantly reduce font file sizes
In this article, we will look at what variable fonts are, the benefits they offer, and how we can use them in our work. First, let's review how typography works on the web, and what innovations variable fonts bring.
Browser compatibility
As of May 2020 variable fonts are supported in most browsers. See Can I use variable fonts? and Fallbacks.
Introduction
The terms font and typeface are often used interchangeably by developers. However there is a difference: A typeface is the underlying visual design that can exist in many different typesetting technologies, and a font is one of these implementations, in a digital file format. In other words, a typeface is what you see, and the font is what you use.
Another concept that is often overlooked is the distinction between a style, and a family. A style is a single and specific typeface, such as Bold Italic, and a family is the complete set of styles.
Before variable fonts, each style was implemented as a separate font file. With variable fonts, all styles can be contained in a single file.
Challenges for the designer and developer
When a designer creates a print project they face some constraints, such as the physical size of the page layout, the number of colors they can use (which is determined by the kind of printing press that will be used), and so on. But they can use as many typeface styles as they like. This means that the typography of print media is often rich and sophisticated, so that the reading experience is truly delightful. Think of the last time you enjoyed browsing an excellent magazine.
Web designers and developers have different constraints than print designers, and an important one is the associated bandwidth costs of our designs. This has been a sticking point for richer typographic experiences, as they come at a cost. With traditional web fonts, each style used in our designs requires users to download a separate font file, which increases latency and page rendering time. Only including the Regular and Bold styles, plus their italic counterparts, can amount to 500 KB or more of font data. This is even before we have dealt with how the fonts are rendered, the fallback patterns we need to use, or undesirable side-effects such as FOIT and FOUT.
Many font families offer a much wider range of styles, from Thin to Black weights, narrow and wide widths, a variety of stylistic details, and even size-specific designs (optimized for large or small text sizes.) Since you'd have to load a new font file for every style (or combinations of styles), many web developers choose not to use these capabilities, reducing the reading experience of their users.
Anatomy of a variable font
Variable fonts address these challenges, by packing styles into a single file.
This works by starting with a central or 'default' style, usually the 'Regular'–an upright roman design with the most typical weight and width that is most suitable for plain text. This is then connected to other styles in a continuous range, called an 'axis.' The most common axis is Weight, which can connect the default style through to a Bold style. Any individual style can be located along an axis, and is called an 'instance' of the variable font. Some instances are named by the font developer, for example Weight axis location 600 is called SemiBold.
The variable font Roboto Flex has three styles for its Weight axis. The Regular style is at the center, and there are two styles at the opposite ends of the axis, one lighter and the other heavier. Between these, you can choose from 900 instances:
The font developer can offer a set of different axes. You can combine them because they all share the same default styles. Roboto has three styles in a Width axis: the Regular is at the center of the axis, and two styles, narrower and wider, are at each end. These provide all the widths of the Regular style, and combine with the Weight axis to provide all the widths for every weight.
This means there are thousands of styles! This may seem like massive overkill, but the quality of the reading experience can be remarkably enhanced by this diversity of type styles. And, if it is without performance penalty, web developers can use a few or as many styles as they wish–it's up to their design.
Italics
The way that Italics are handled in variable fonts is interesting, as there are two difference approaches. Typefaces like Helvetica or Roboto have interpolation compatible contours, so their Roman and Italic styles can be interpolated between and the Slant axis can be used to get from Roman to Italic.
Other typefaces (such as Garamond, Baskerville, or Bodoni) have Roman and Italic glyph contours that are not interpolation compatible. For example, the contours that typically define a Roman lowercase "n" do not match the contours used to define an Italic lowercase "n". Instead of interpolating one contour to the other, the Italic axis toggles from Roman to Italic contours.
After the switch to Italic, the axes available to the user should be the same as those for the Roman, just as the character set should be the same.
A glyph substitution capability can also be seen for individual glyphs, and used anywhere in the design space of a variable font. For example, a dollar sign design with two vertical bars works best at larger point sizes, but at smaller point sizes a design with only one bar is better. When we have fewer pixels for rendering the glyph, a two bar design can become illegible. To combat this, much like the Italic axis, a glyph substitution of one glyph for another can occur along the Optical Size axis at a point decided by the type designer.
In summary, where the contours allow for it, type designers can create fonts that interpolate between various styles in a multi-dimensional design space. This gives you granular control over your typography, and a great deal of power.
Axes definitions
There are five registered axes, which control known, predictable features of the font: weight, width, optical size, slant and italics. Besides those, a font can contain custom axes. These can control any design aspect of the font the type designer wishes: the size of serifs, the length of swashes, the height of ascenders or the size of the dot on the i.
Even though axes can control the same feature, they might use different values. For example, in the Oswald and Hepta Slab variable fonts there is only one axis available, Weight, but the ranges are different–Oswald has the same range as before it was upgraded to be variable, 200 to 700, but Hepta Slab has an extreme hairline weight at 1 that goes all the up to 900.
The five registered axes have 4-character lowercase tags that are used to set their values in CSS:
Axis names and CSS values | |
---|---|
Weight |
wght
|
Width |
wdth
|
Slant |
slnt
|
Optical Size |
opsz
|
Italics |
ital
|
Since the font developer defines which axes are available in a variable font, and which values they can have, it is essential to find out what each font offers. The font's documentation should provide this, or you can inspect the font using a tool like Wakamai Fondue.
Use cases and benefits
Setting the axes values comes down to personal taste and applying typographic best practices. The danger with any new technology is possible misuse, and settings that are overly artistic or exploratory could also decrease legibility of the actual text. For titles, exploring different axes to create great artistic designs are exciting, but for body copy this risks making the text illegible.
Exciting expression
One great example of artistic expression is shown above, an exploration of the typeface Decovar by Mandy Michael.
You can view the working example and source code for the above sample here.
Animation
There is also a possibility to explore animating characters with variable fonts. Above is an example of different axes being used with the typeface Zycon. See the live animation example on Axis Praxis.
Anicons is the world's first animated color icon font, based on Material Design Icons. Anicons is an experiment that combines two cutting edge font technologies: variable fonts and color fonts.
Finesse
Roboto Flex and Amstelvar offer a set of "Parametric Axes." In these axes, the letters are deconstructed into 4 fundamental aspects of form: black or positive shapes, white or negative shapes, and the x and y dimensions. In the same way that primary colors can be blended with any other color to adjust it, these 4 aspects can be used to fine tune any other axis.
The XTRA axis in Amstelvar allows you to adjust the "white" per mille value, as shown above. Using little bits of XTRA in opposite directions, the words' widths are evened out.
Variable fonts in CSS
Loading variable font files
Variable fonts are loaded though the same @font-face
mechanism as traditional
static web fonts, but with two new enhancements:
@font-face {
font-family: 'Roboto Flex';
src: url('RobotoFlex-VF.woff2') format('woff2-variations');
src: url('RobotoFlex-VF.woff2') format('woff2') tech('variations');
font-weight: 100 1000;
font-stretch: 25% 151%;
}
1. Source Formats: We don't want the browser to download the font if it
doesn't support variable fonts, so we add format
and tech
descriptions: once in the
future
syntax
(format('woff2') tech('variations')
), once in the deprecated but supported among browsers
syntax (format('woff2-variations')
). If the browser supports variable fonts and supports
the upcoming syntax, it will use the first declaration. If it supports variable
fonts and the current syntax, it will use the second declaration. They both
point to the same font file.
2. Style Ranges: You'll notice we're supplying two values for font-weight
and font-stretch
. Instead of telling the browser which specific weight this
font provides (for example font-weight: 500;
), we now give it the range of
weights supported by the font. For Roboto Flex, the Weight axis ranges from 100
to 1000, and CSS directly maps the axis range to the font-weight
style
property. By specifying the range in @font-face
, any value outside this range
will be "capped" to the nearest valid value. The Width axis range is mapped in
the same way to the font-stretch
property.
If you're using the Google Fonts API, this will all be taken care of. Not only will the CSS contain the proper source formats and ranges, Google Fonts will also send static fallback fonts in case variable fonts aren't supported.
Using weights and widths
Currently, the axes you can reliably set from CSS are the wght
axis through
font-weight
, and the wdth
axis through font-stretch
.
Traditionally, you would set font-weight
as a keyword (light
, bold
) or as
a numerical value between 100 and 900, in steps of 100. With variable fonts, you
can set any value within the font's Width range:
.kinda-light {
font-weight: 125;
}
.super-heavy {
font-weight: 1000;
}
Likewise, we can set font-stretch
with keywords (condensed
,
ultra-expanded
) or with percentage values:
.kinda-narrow {
font-stretch: 33.3%;
}
.super-wide {
font-stretch: 151%;
}
Using italics and obliques
The ital
axis is intended for fonts that contain both a regular style, and an
italic style. The axis is meant to be an on/off switch: value 0
is off and
will show the regular style, value 1
will show the italics. Unlike other axes,
there's no transition. A value of 0.5
won't give you "half italics".
The slnt
axis is different from italics in that it's not a new style, but
just slants the regular style. By default its value is 0
, which means the
default upright letter shapes. Roboto Flex has a maximum slant of -10 degrees,
meaning the letters will lean to the right when going from 0 to -10.
It would be intuitive to set these axis through the font-style
property, but
as of April 2020, how to exactly do this is still being worked
out. So for now, you should
treat these as custom axes, and set them through font-variation-settings
:
i, em, .italic {
/* Should be font-style: italic; */
font-variation-settings: 'ital' 1;
}
.slanted {
/* Should be font-style: oblique 10deg; */
font-variation-settings: 'slnt' 10;
}
Using optical sizes
A typeface can be rendered very small (a 12px footnote) or very large (a 80px headline). Fonts can respond to these size changes by changing its letter shapes to better suit its size. A small size might be better off without fine details, while a large size might benefit from more details and thinner strokes.
A new CSS property has been introduced for this axis: font-optical-sizing
. By
default it's set to auto
, which makes the browser set the axis value based on
the font-size
. This means the browser will pick the best optical size
automatically, but if you wish to turn this off you can set
font-optical-sizing
to none
.
You can also set a custom value for the opsz
axis, if you deliberately want an
optical size that doesn't match the font size. The following CSS would cause
text to be displayed at a large size, but at an optical size as if it were
printed in 8pt
:
.small-yet-large {
font-size: 100px;
font-variation-settings: 'opsz' 8;
}
Using custom axes
Unlike registered axes, custom axes will not be mapped to an existing CSS
property, so you will always have to set them through font-variation-settings
.
Tags for custom axes are always in uppercase, to distinguish them from
registered axes.
Roboto Flex offers a few custom axes, and the most important is Grade (GRAD
).
A Grade axis is interesting as it changes the weight of the font without
changing the widths, so line breaks do not change. By playing with a Grade axis,
you can avoid being forced to fiddle with changes to Weight axis that affects
the overall width, and then changes to the Width axis that affect the overall
weight.
As GRAD
is a custom axis, with a range of -200 to 150 in Roboto Flex.
We need to address it with font-variation-settings
:
.grade-light {
font-variation-settings: `GRAD` -200;
}
.grade-normal {
font-variation-settings: `GRAD` 0;
}
.grade-heavy {
font-variation-settings: `GRAD` 150;
}
Variable fonts on Google Fonts
Google Fonts has expanded their catalog with variable
fonts, and adding new ones regularly.
The interface is currently aimed at picking single instances from the font:
you select the variation you want, click "Select this style", and it will be
added to the <link>
element that fetches the CSS and fonts from Google Fonts.
To use all the available axes, or ranges of values, you will have to manually compose the URL to the Google Fonts API. The variable fonts overview lists all axes and values.
The Google Variable Fonts Links tool can also give you the latest URLs for the full variable fonts.
Font-variation-settings inheritance
While all registered axes will soon be supported through existing CSS
properties, for the time being you might need to rely on
font-variation-settings
as a fallback. And if your font has custom axes,
you'll need font-variation-settings
too.
However, here's a little gotcha with font-variation-settings
. Every property
you don't explicitly set will automatically be reset to its default.
Previously set values aren't inherited! This means the following will not work
as expected:
<span class="slanted grade-light">
I should be slanted and have a light grade
</span>
First the browser will apply font-variation-settings: 'slnt' 10
from the
.slanted
class. Then it will apply font-variation-settings: 'GRAD' -200
from
the .grade-light
class. But this will reset the slnt
back to its default of
0! The result will be text in a light grade, but not slanted.
Luckily, we can work around this by using CSS variables:
/* Set the default values */
:root {
--slnt: 0;
--GRAD: 0;
}
/* Change value for these elements and their children */
.slanted {
--slnt: 10;
}
.grade-light {
--grad: -200;
}
.grade-normal {
--grad: 0;
}
.grade-heavy {
--grad: 150;
}
/* Apply whatever value is kept in the CSS variables */
.slanted,
.grade-light,
.grade-normal,
.grade-heavy {
font-variation-settings: 'slnt' var(--slnt), 'GRAD' var(--GRAD);
}
CSS variables will cascade, so if an element (or one of its parents) will have
set the slnt
to 10
, it will keep that value, even if you set GRAD
to
something else. See
Fixing variable font inheritance
for an in-depth explanation of this technique.
Note that animating CSS variables doesn't work (by design), so something like this doesn't work:
@keyframes width-animation {
from { --wdth: 25; }
to { --wdth: 151; }
}
These animations will have to happen directly on font-variation-settings
.
Performance gains
OpenType variable fonts allow us to store multiple variations of a type family into a single font file. Monotype ran an experiment by combining 12 input fonts to generate eight weights, across three widths, across both the Italic and Roman styles. Storing 48 individual fonts in a single variable font file meant a 88% reduction in file size.
However, if you are using a single font such as Roboto Regular and nothing else, you might not see a net gain in font size if you were to switch to a variable font with many axes. As always, it depends on your use-case.
On the flip side, animating the font between settings may cause performance
issues. Although this will improve once variable font support in browsers gets
more mature, the problem can be reduced somewhat by only animating fonts that are
currently on screen. This handy snippet by
Dinamo pauses
animations in elements with the class vf-animation
, when they're not on
screen:
var observer = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
// Pause/Play the animation
if (entry.isIntersecting) entry.target.style.animationPlayState = "running"
else entry.target.style.animationPlayState = "paused"
});
});
var variableTexts = document.querySelectorAll(".vf-animation");
variableTexts.forEach(function(el) { observer.observe(el); });
If your font responds to user interaction, it's a good idea to throttle or debounce input events. This will prevent the browser from rendering instances of the variable font that changed so little from the previous instance the human eye wouldn't see the difference.
If you're using Google Fonts, it's a good idea to
preconnect to https://fonts.gstatic.com
,
the domain where Google's fonts are hosted. This will make sure the browser
knows early on where to get the fonts when it comes across them in the CSS:
<link rel="preconnect" href="https://fonts.gstatic.com" />
This tip works for other CDNs as well: the sooner you let the browser set up a network connection, the sooner it can download your fonts.
Find more performance tips for loading Google Fonts in The Fastest Google Fonts.
Fallbacks and browser support
All modern browsers support variable fonts. In case you need to support older browsers, you can choose to build your site with static fonts, and use variable fonts as progressive enhancement:
/* Set up Roboto for old browsers, only regular + bold */
@supports not (font-variation-settings: normal) {
@font-face {
font-family: Roboto;
src: url('Roboto-Regular.woff2');
font-weight: normal;
}
@font-face {
font-family: Roboto;
src: url('Roboto-Bold.woff2');
font-weight: bold;
}
body {
font-family: Roboto;
}
.super-bold {
font-weight: bold;
}
}
/* Set up Roboto for modern browsers, all weights */
@supports (font-variation-settings: normal) {
@font-face {
font-family: 'Roboto';
src: url('RobotoFlex-VF.woff2') format('woff2 supports variations'),
url('RobotoFlex-VF.woff2') format('woff2-variations');
font-weight: 100 1000;
font-stretch: 25% 151%;
}
.super-bold {
font-weight: 1000;
}
}
For older browsers, text with the class .super-bold
will get rendered in the
normal bold, as that's the only bold font we have available. When variable fonts
are supported, we can actually use the heaviest weight of 1000.
The @supports
rule is not supported by Internet Explorer, so this browser would
not show any styling. If this is a problem, you could always use one of the
oldschool hacks to target relevant
older browsers.
If you are using the Google Fonts API, it will take care of loading the proper fonts for your visitor's browsers. Say you request the font Oswald in weight range 200 to 700, like so:
<link href="https://fonts.googleapis.com/css2?family=Oswald:wght@200..700&display=swap" rel="stylesheet">
Modern browsers that can handle variable fonts will get the variable font, and will have every weight between 200 and 700 available. Older browsers will get served individual static fonts for every weight. In this case, this means they'll download 6 font files: one for weight 200, one for weight 300, and so on.
Thanks
This article would have only been made possible with the help of the following people:
- Mustafa Kurtuldu, UX designer and design advocate at Google
- Roel Nieskens, UX designer/developer and typography expert at Kabisa
- Dave Crossland, Program Manager, Google Fonts
- David Berlow, type designer and typographer at Font Bureau
- Laurence Penney, developer of axis-praxis.org
- Mandy Michael, developer of variablefonts.dev
Hero image by Bruno Martins on Unsplash.