In this article, we’ll take a deep look at getting set up with variable fonts on the frontend, look at properly loading them, deal with settings, look at some ‘gotchas’ and more—read and learn!
Other parts:
- Variable fonts in real life: how to use and love them
- The joy of Variable Fonts: getting started on the Frontend
In our last article describing why you should use variable fonts and examining some real-life use cases, we learned to love variable fonts (check it out!). This time around, we’re ready for that love to blossom into a beautiful new joy. This post is for web developers who are ready to take the variable font plunge, although designers (and the curious in general) are certainly welcome to read, too!
Loading variable fonts vs. static fonts
Although the process of loading variable fonts via the @font-face
directive is pretty similar to loading static fonts in the same way, there are still some slight differences that should be mentioned. This applies both to how we refer to the source file, and set up style ranges, and how we deal with default styles. Let’s discuss all of these.
How to specify source file formats
First, for the sake of comparison, let’s take a look at a basic example demonstrating how to load a static font in CSS:
@font-face {
src: url('/fonts/MartianGrotesk.woff2') format('woff2');
}
Now, with variable fonts, across various specifications you can find a number of different loading approaches—but which is the correct method? Let’s try to use the same format as with the static font, but we’ll just change the file to the variable version (note the VF
in the filename):
@font-face {
src: url('/fonts/MartianGroteskVF.woff2') format('woff2');
}
Will this work? The simple answer is, yes, it will. But the problem with this approach is that we aren’t providing any indication to the browser that this font is a variable font. This means instead of gracefully falling back, it will load a font which it doesn’t actually know how to use.
There are a couple of ways to specify variable fonts, although some of these are now deprecated, or lack widespread browser support, so it’s a good idea to recognize them if you see them in the wild. Let’s examine three methods, then we’ll reveal our solution.
Indicating variable fonts
First, we could provide an indication that we’re using a variable font with the z*-variations
format:
@font-face {
src: url('/fonts/MartianGrotesk-VF.woff2') format('woff2-variations');
}
The z*-variations
format was the first standard for solving this problem, and most browsers offer support for this method. The problem is it’s deprecated in the most up-to-date specifications.
Second, in a lot of recent articles you might’ve encountered the supports variations
format:
@font-face {
src: url('/fonts/MartianGrotesk-VF.woff2') format('woff2 supports variations');
}
Although the above format was featured in last year’s W3C drafts, they’ve already decided to change it—so there’s no reason to use it.
Finally, according to the latest specification draft, this is how it should be done instead:
@font-face {
src: url('/fonts/MartianGrotesk-VF.woff2') format('woff2') tech('variations');
}
But don’t rush too fast implementing this type of declaration! If you try to make the switch it now, you’d probably just break the entire @font-face
directive due to a lack of widespread support.
TLDR: Which format should I use?
We probably shouldn’t rely on formats that have been deprecated or which aren’t still widely accepted. But, there is one thing that we can use: feature queries.
We can target browsers via the @supports
directive and load our variable fonts only if they support them. And, actually, @supports
has wider support than format('woff2-variations')
!
Plus, when a new standard becomes widely available, we can easily add tech('variations')
and any additional features that new standard might bring with it, and our @supports
directives will probably still be pretty useful since older browsers could have trouble parsing the new format, too.
@font-face {
font-family: Martian Grotesk;
src: url('/fonts/MartianGrotesk.woff2') format('woff2');
}
@font-face {
font-family: MartianGroteskVF;
src: url('/fonts/MartianGroteskVF.woff2') format('woff2');
}
body {
font-family: MartianGrotesk;
}
@supports (font-variation-settings: normal) {
font-family: MartianGroteskVF;
}
Using ranges instead of single values
Another difference when loading variable fonts is that we set font styles, not as single values, but as ranges.
There are two properties that require ranges: font-weight
and font-stretch
, and, if your variable font supports them, they must be declared. If you do not explicitly add these style ranges to variable font declaration there could be unpredictable results. For instance, some browsers might try to get these metrics from the font, but some will try to use the default ones. In any case, it’s always better declare them explicitly.
Let’s compare how we’d add two files to correspond to Regular and Bold styles with how we’d do the same thing with a singular variable font style using range values.
First up, the static font version using numbers and two files:
@font-face {
font-family: MartianGrotesk;
src: url('/fonts/MartianGrotesk.woff2') format('woff2');
font-weight: 400; /* normal */
}
@font-face {
font-family: MartianGrotesk;
src: url('/fonts/MartianGrotesk-bold.woff2') format('woff2');
font-weight: 700; /* bold */
}
How let’s see the variable font version:
@font-face {
font-family: MartianGroteskVF;
src: url('/fonts/MartianGroteskVF.woff2') format('woff2');
font-weight: 100 1000;
font-stretch: 75% 200%;
}
So, where to get these font metrics? Well, your font probably has some kind of documentation, so that would be a great place to start!
…But let’s imagine we’re dealing with a font that doesn’t have documentation.
In these cases, you could always can use services like Dinamo Font Gauntlet or Wakamai Fondue to find out everything possible related to a variable font’s settings.
Default font settings
There are 5 main axes and endless possibilities for custom ones. We’re not going to dig too much into this, mostly there are many variables and this can depend on the details of a particular font. But what should we do if we want to provide our font with some default customization?
It’s possible to use the font-variation-settings
property inside @font-face
directive to apply default settings to variable fonts:
@font-face {
font-family: MartianGroteskVF;
src: url('/fonts/MartianGroteskVF.woff2') format('woff2');
font-weight: 100 1000;
font-stretch: 75% 200%;
font-variation-settings: 'wdth' 100, 'wght' 400;
}
Let’s talk about different properties: font-variation-settings
is a generic, low-level property, while high-level properties include separated, specific properties like font-weight
, font-stretch
, and so on.
Be careful with the units that you use with these. You might notice that font-variation-settings
uses numbers for each property. This is because font-variation-settings
is a low-level property. With high-level properties, such as font-stretch
, font-style
, etc., this may differ. For instance, font-stretch
uses percentages, and if you use font-stretch
without a percent it won’t work.
Using variable fonts in CSS
So, we’ve just declared and loaded our bright and shiny variable fonts, what to do next? We need to figure out how to adjust our styles and which properties we’ll use to do that. We now have great power in our hands with a huge (yet finite) amount of variations.
This level of possibility can pose a number of practical questions, case and point: if we want to have an ultra-bold and ultra-wide font, should we use font-weight
or font-variation-settings
?
This is on the menu:
h1 {
font-weight: 1000;
font-stretch: 200%;
}
But so is this:
h1 {
font-variation-settings: 'wdth' 200, 'wght' 1000;
}
While you might feel that having one property to rule them all (font-variation-settings
) is a good choice, actually, not so much. Primarily, font-variation-settings
is a low-level property that should mostly be used to manipulate custom axes which (obviously) don’t have their own CSS properties.
The low-level font-variation-settings
only works for variable fonts and would not have the same behavior if variable fonts are not supported by the browser and a fallback font is used. So, we recommend using high-level properties as they can predictably work with your regular/fallback font.
Instead, the golden rule is to use high-level properties (
font-weight
,font-stretch
,font-style
) whenever possible.
Keep font-variation-settings
for custom use, or if you need to configure optical-sizing
with a value (since the font-optical-sizing
property doesn’t support number values yet, with only support for auto
/none
at the moment).
Further, there is another downside of using font-variation-settings
—the corresponding properties will not be inherited if another font-variation-settings
has been applied to any children, and will just be overwritten instead.
To illustrate a bit, in the example below, our <span>
content will get the styles as assigned, but will fail to inherit the properties assigned to the parent <p>
element:
<style>
p {
font-variation-settings: 'wght' 1000;
}
span {
font-variation-settings: 'wdth' 200;
}
</style>
<p>Hello, <span>world</span></p>
With modern CSS it’s possible to implement an inheritance mechanism via custom properties. (Although, we think it’s just not worth it unless it’s necessary to make custom axes inheritable.) Still, it would look something like this:
<style>
body {
font-variation-settings: 'wght' var(--wght, 400), 'wdth' var(--wdth, 100);
}
p {
--wght: 1000;
}
span {
--wdth: 200;
}
</style>
<p>Hello, <span>world</span></p>
If we’re going to implement a fallback, it would be convenient to use properties that also apply to static fonts:
@font-face {
font-family: Martian Grotesk;
src: url('/fonts/MartianGrotesk.woff2') format('woff2');
}
@font-face {
font-family: MartianGroteskVF;
src: url('/fonts/MartianGroteskVF.woff2') format('woff2');
}
body {
font-family: MartianGrotesk, sans-serif;
}
@supports (font-variation-settings: normal) {
body {
font-family: MartianGroteskVF, sans-serif;
}
}
h1 {
font-weight: 700;
font-variation-settings: 'GRAD' 1;
}
In this case, using progressive enhancement font-weight
would be applied to both the static and variable fonts, while GRAD
axis would be used only for the variable font. If we had only used font-variation-settings
, we would have had to provide some feature-query fallback; browsers which load static fonts will not recognize font-variation-settings
.
Miscellaneous Variable Font mindfulness
With that basic setup out of the way, let’s take a look at some other things that frontend developers should keep in mind when employing variable font goodness.
Why to avoid keyword values with high-level font properties
Also, let’s take a second to note CSS keywords, as these do exist for some high-level font properties. Still, I wouldn’t recommend using these because, although, they have default mappings according to specification, there is no guarantee that a font will follow this specification. Some fonts will change these metrics and steps to have more steps.
It’s always better to be explicit and use numeric values instead of keywords.
That being said, sure, you can check that everything works as it should and use them.
But also, be mindful that some user agents have predefined styles for tags. For instance, in Chrome, the <strong>
tag has font-weight: bold
which maps to font-weight: 700
and your variable font metrics may differ from those values. To solve this, you can use some CSS-reset or just redefine these to the desired values.
Unexpected behavior with -webkit-text-stroke
Variable fonts provide enormous flexibility, yet, over the course of some extensive use we’ve found one property which does not work as expected.
At times, you would want to customize a font so that it is outlined, and there’s a property to do this: -webkit-text-stroke
. Although this property has a (scary) prefix, it’s widely supported by all major browsers.
But its behavior with variable fonts differs. You may notice that in addition to outlining the letters themselves, with a variable font, it also outlines all the individual parts that this letter consists of.
And this fact, that each letter consists of multiple parts, is necessary for font face interpolation.
We don’t think we would call this a downside, exactly, but still—be mindful.
Wrapping things up
In conclusion, we’re very pleased to finally breathe a sigh of relief—all the richness of typography is now available on the web, and the future is finally here!
We believe variable fonts are a huge breakthrough for the entire font industry. There are benefits for everyone—for users and for developers. All that’s left is for designers and frontend engineers to try them out and start using variable fonts in new projects!
And one more thing: if you’ve got a problem or project that needs help, whether it’s design-related or not, Evil Martians are ready to face any challenge! Get in touch!