Today, I saw an interesting effect on CodePen - GSAP 3 ETC Variable Font Wave.

Preview Link: https://codepen.io/Chokcoco/pen/BamYMYg

It is implemented with the JS animation library GSAP. Take a look.

GSAP

I wondered if I could use CSS to achieve the same effect. After trying it out, I managed to achieve the original effect using pure CSS.

The core of the above effect is the animation of the text, which changes from thinner and tighter to thicker and farther apart. Some people may think this is transform: scale(), but it is not.

scale is to zoom in and out of an object in equal proportion, and a closer look at the above effect, it is clear that there is a change in the thickness of the font, the font’s word width. Here, in fact, a relatively new feature of CSS is used – variable fonts, that is, font-variation.

In this article, we will use this effect to introduce what is CSS font-variation.

What is CSS font-variation, variable fonts?

According to MDN – Variable fonts, Variable fonts are an evolution of the OpenType font specification that allows multiple variants of the same font to be combined into a single font file. This eliminates the need to split fonts of different widths, weights, or styles into separate font files. We can simply use CSS with a single @font-face reference to get the various font variants contained in this single file.

The concept is a bit hard to understand, so let’s explain it briefly.

The counterpart to variable fonts is the standard (static) fonts.

Standard (static) fonts are font files that represent only a particular combination of width/weight/style of a font , usually the font files we introduce on the page are of this kind, representing only a particular combination of width/weight/style of this font.

And if we want to introduce a font family (such as the Roboto font family), it may contain “Roboto Regular” (regular font weight), “Roboto Bold” (bold), or “Roboto Bold Italic” (bold + italic), and so on. This means that we might need 20 or 30 different font files to have a whole family of fonts (and several times more for large fonts with different widths).

With variable fonts – font-variation, which can be thought of as all in one – all permutations of weight, width, italics, etc. can be packed into one file. Of course, this file may be a bit larger than a regular single font file.

Limitations of static fonts

For example, in Google Font, I pick a standard static font at random and implement an animation of the font-weight change of the font.

1
2
3
4
5
6
<p>CSS font-variation</p>
<p>CSS font-variation</p>
<p>CSS font-variation</p>
<p>CSS font-variation</p>
<p>CSS font-variation</p>
<p>CSS font-variation</p>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300&display=swap');
p {
    font-family: 'Lato', sans-serif;
    font-size: 48px;
}
p:nth-child(1) {
    font-weight: 100;
}
p:nth-child(2) {
    font-weight: 200;
}
p:nth-child(3) {
    font-weight: 300;
}
p:nth-child(4) {
    font-weight: 400;
}
p:nth-child(5) {
    font-weight: 500;
}
p:nth-child(6) {
    font-weight: 600;
}

Look at the results.

css font

There is no such thing as what we think, because the font thickness is from 100 to 600, so the font becomes thicker in sequence, there are only two font-weights in total.

  • When font-weight: is at 100 - 500, it is actually font-weight: normal;
  • When font-weight: 600, it is actually hitting font-weight: bold.

This is the limitation of traditional static fonts. In a single font file, you will not actually have all the thickness and width types of that font.

Variety of variable fonts

Next, we switch to variable fonts.

The syntax for loading variable fonts is very similar to other web fonts, but there are some significant differences that are provided by upgrading the legacy @font-face syntax available in modern browsers.

The basic syntax is the same, but the font technology is different, and variable fonts can provide allowable ranges for descriptors like font-weight and font-stretch, rather than being named according to the font file loaded.

Below, we will load an AnyBody variable font that supports font thicknesses from 100 to 900 and font stretching and distortion from 10% to 400%.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@font-face {
    font-family: 'Anybody';
    src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
    font-display: block;
    font-style: normal italic;
    font-weight: 100 900;
    font-stretch: 10% 400%;
}
p {
    font-family: 'Anybody', sans-serif;
    font-size: 48px;
}
p:nth-child(1) {
    font-weight: 100;
}
// ...
p:nth-child(6) {
    font-weight: 600;
}

Again the font thickness is set from 100 - 600. the effect is as follows.

font-weight

This time, you can see that the font has a noticeable even change, supporting gradual changes from font-weight: 100 to font-weight: 600. Here is the beauty of variable fonts.

Understanding font-variation-settings

In addition to controlling the thickness of variable fonts directly via font-weight, CSS provides a new property, font-variation-settings, to control multiple properties of variable fonts simultaneously.

At the heart of the new format for variable fonts is the variation axis concept, which describes the range of variation allowed for a particular feature in a font design.

All variable fonts have at least 5 property axes that can be controlled via font-variation-settings. They are registered axes that map existing CSS properties or values.

They are

  • font-weight axis “wght”: corresponds to font-weight and controls the thickness of the font
  • width axis “wdth”: corresponds to font-stretch, which controls the stretching of the font
  • slant axis “slnt” (slant): corresponds to font-style: oblique + angle, controls the slant of the font
  • italic axis “ital”: corresponds to font-style: italic, controls the slant of the font (note that it is not the same slant as font-style: oblique)
  • optical-sizing axis “opsz”: corresponds to font-optical-sizing of the font, controls the optical size of the font

Well, it may be a little bit confusing, it’s okay, you can understand what it means immediately by an example.

Still using the above variable fonts, we use font-variation-settings to implement an animation of the change in font thickness.

1
<p>Anybody</p>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
@font-face {
    font-family: 'Anybody';
    src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
    font-display: block;
    font-style: normal italic;
    font-weight: 100 900;
    font-stretch: 10% 400%;
}
p {
    font-family: 'Anybody';
    font-size: 48px;
    animation: fontWeightChange 2s infinite alternate linear;
}
@keyframes fontWeightChange {
    0% {
        font-variation-settings: 'wght' 100;
    }
    100% {
        font-variation-settings: "wght" 600;
    }
}

The effect is as follows.

font-variation

Which, in fact, can be interpreted as an animation that takes advantage of the font-variation-settings: “wght” change, which is equivalent to the font-weight change animation.

font-weight

Using font-variation-settings to change multiple features of a font at the same time

OK, so why bother with font-variation-settings if it’s the same effect?

That’s because font-variation-settings not only supports font thickness variation, but also supports multiple style attribute variation of the above-mentioned registration axis settings, and some font style attribute variation of the custom axis.

This time, in addition to the font thickness, we add the “wdth” variation, that is, the font stretching.

1
<p>Anybody</p>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
@font-face {
    font-family: 'Anybody';
    src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
    font-display: block;
    font-style: normal italic;
    font-weight: 100 900;
    font-stretch: 10% 400%;
}
p {
    font-family: 'Anybody';
    font-size: 48px;
    animation: fontWeightChange 2s infinite alternate linear;
}
@keyframes fontWeightChange {
    0% {
        font-variation-settings: 'wght' 100, 'wdth' 60;
    }
    100% {
        font-variation-settings: "wght" 600, 'wdth' 400;
    }
}

This time, the animation effect is performed from 100 to 600 font thickness and from 60% to 400% font expansion. The effect is as follows.

font-variation

That is, font-variation-settings supports multiple font styles changing together, which is very important.

Here, we can actually use this to achieve the effect shown in the title image, we simply modify it by adding multiple lines and setting a negative animation delay for each line.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<div class="g-container">
    <ul>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
    </ul>
</div>

To simplify the code with SCSS, the core of the following code is to add an identical animation font-variation-settings animation to each li, and set the animation-delay with equal difference in turn.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
li {
    animation: change 0.8s infinite linear alternate;
}
@for $i from 1 to 9 {
    li:nth-child(#{$i}) {
        animation-delay: #{($i - 1) * -0.125}s;
    }
}
@keyframes change {
    0% {
        font-variation-settings: 'wdth' 60, 'wght' 100;
        opacity: .5;
    }
    100% {
        font-variation-settings: 'wdth' 400, 'wght' 900;
        opacity: 1;
    }
}

The effect is as follows.

font-variation

Okay, next, simply construct a 3D scene using CSS 3D, the complete CSS code is as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@font-face {
    font-family: 'Anybody';
    src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
    font-display: block;
    font-style: normal italic;
    font-weight: 100 900;
    font-stretch: 10% 400%;
}
.g-container {
    position: relative;
    margin: auto;
    display: flex;
    font-size: 48px;
    font-family: 'Anybody';
    color: #fff;
    transform-style: preserve-3d;
    perspective: 200px;
}
ul {
    background: radial-gradient(farthest-side at 110px 0px, rgba(255, 255, 255, 0.2) 0%, #171717 100%);
    padding: 5px;
    transform-style: preserve-3d;
    transform: translateZ(-60px) rotateX(30deg) translateY(-30px);
    animation: move 3s infinite alternate;
    
    &::before {
        content: "";
        position: absolute;
        left: 0;
        bottom: 0;
        right: 0;
        height: 45px;
        background: #141313;
        transform: rotateX(-230deg);
        transform-origin: 50% 100%;
    }
}
li {
    width: 410px;
    animation: change 0.8s infinite linear alternate;
}
@for $i from 1 to 9 {
    li:nth-child(#{$i}) {
        animation-delay: #{($i - 1) * -0.125}s;
    }
}
@keyframes change {
    0% {
        font-variation-settings: 'wdth' 60, 'wght' 100;
        opacity: .5;
    }
    100% {
        font-variation-settings: 'wdth' 400, 'wght' 900;
        opacity: 1;
    }
}
@keyframes move {
    100% {
        transform: translateZ(-60px) rotateX(30deg) translateY(0px);
    }
}

At this point, we have basically restored the effect of the animation in the beginning of the article.

The complete code and DEMO effect you can click here: https://codepen.io/Chokcoco/pen/YzEeBOJ

Variable axes for font-variation – registered axes and custom axes

Back to variable fonts themselves. The concept of variable axes was mentioned above, and they are divided into registered axes and custom axes, which are

  • registered axes - registered axes
  • custom axes - custom axes

The core of the new format for variable fonts is the concept of variable axes, which describes the range of variation allowed for a particular characteristic in a font design.

For example, the ‘weight axis’ describes the thickness of the font; the ‘width axis’ describes the width of the font; the ‘italic axis’ describes whether to use italic glyphs or not and can be switched on or off accordingly; etc. Note that the axes can be both range and switch selectable. The font weight may be between 1 and 999, while the italics may be a simple 0 or 1 (off or on).

As defined in the specification, two types of deformation axes exist, registered axes and custom axes.

The registered axes are the most common and are so common that the authors who developed the specification felt the need to standardize them. The five currently registered axes are word weight, width, slant, italic and optical size.

The above article actually lists the 5 registration axes and briefly describes their use. List them once more.

  1. font-weight axis “wght”: corresponds to font-weight, which controls the thickness of the font
  2. width axis “wdth”: corresponds to font-stretch, which controls the stretching of the font
  3. slant axis “slnt” (slant): corresponds to font-style: oblique + angle, controls the slant of the font
  4. italic axis “ital” (italic): corresponding to font-style: italic, controls the slant of the font (note that the slant is not the same as font-style: oblique)
  5. optical-sizing axis “opsz”: corresponds to font-optical-sizing of the font, controls the optical size of the font

Custom axes are practically unlimited: font designers can define and define any axis they like, and only need to give it a four-letter label to identify it in the font file format itself.

Let’s look at an example of a custom axis.

1
<p>Grade</p>
1
2
3
4
5
p {
    font-family: "Amstelvar VF", serif;
    font-size: 64px;
    font-variation-settings: 'GRAD' 88;
}

The above font-family: “Amstelvar VF” is a variable font, while ‘GRAD’ is one of the custom axes, meaning grade axis.

  • GRAD’: The term “grade” refers to the relative weight or density of a type design, but differs from the traditional “weight” in that the physical space occupied by the text does not change, so changing the grade of the text does not change the overall layout of the text or its surrounding elements around it. This makes the level a useful axis of change, as it can be changed or animated without causing a reflow of the text itself.

There is a DEMO on MDN about changing the value of ‘GRAD’, which corresponds to a font change. the effect is as follows:

mdn grad

It is worth noting that custom axes can be any design variation axis the type designer imagines. There may be some that will gradually become quite common, evolving into even registered axes as the specification evolves.

Where to find variable fonts?

OK, now if I want to use variable fonts in my business to implement an effect or animation, where can I look for resources for variable fonts?

Here is a great website – Variable Fonts: https://v-fonts.com/

It has a very large collection of Variable Fonts and lists the range of font properties they support on the registration axis, for example, font weights from 100 to 700, so we can freely debug the preview

v-fonts

Can i Use (2022-02-20)

Can I start using variable fonts now?

Screenshot of Can i Use as of today.

Can i Use

Compatibility has been very good, regardless of IE series then you can go on the actual production environment.

Finally

If you’re interested in CSS Font Variation, here’s a good article to read: Introduction to variable fonts on the web.