The Publishing Project

CSS Variables For Handling Fonts

Thanks to Jason Pamental for feedback on this post.

Variable fonts have a problem as currently implemented. If you use font-variation-settings to control the different axes of the font then every time one changes you must change the other ones or they will reset back to their default values.

We’ll use Recursive as the font for this post.

Recursive has two custom axes: Monospace and Csual and three default axes: Weight, Slant and Italics. The axes information is shown in the table below.

Axis Designation Axis Name Min Value Max Value Default
MONO Monospace 0 1 0
CASL Casual 0 1 0
wght Weight 300 1000 300
slnt Slant -15 0 0
ital Italic 0 1 0.5

We define the following variables defining the default values of the Variable Font axes to the :root element of the style sheet. We use :root rather than html because :root has a higher specificity.

root: {
  --vf-mono: 0;
  --vf-casl: 0;
  --vf-slnt: 0;
  --vf-ital: 0.5;
  --vf-wght: 300;
}

Using these variables we can then use them to update font-variation-settings when we make changes.

In the first rule of the example below, we change the value of --vf-mono and when we update font-variation-settings, it’ll take the value for --vf-mono we just defined and all the other values from :root so we don’t have to define all the axes values every time we make a change to one of them.

The different examples show different combinations of variable changes from a single value to changing multiple values for more complex forms.

.mono-linear-light {
  --vf-mono: 1;
  font-variation-settings:  "MONO" var(--vf-mono),
                            "CASL" var(--vf-casl),
                            "slnt" var(--vf-slnt),
                            "ital" var(--vf-ital);
  font-weight: var(--vf-weight);
}

.mono-linear-light-italic {
  --vf-mono: 1;
  --vf-slnt: -15;
  --vf-ital: 1;
  font-variation-settings:  "MONO" var(--vf-mono),
                            "CASL" var(--vf-casl),
                            "slnt" var(--vf-slnt),
                            "ital" var(--vf-ital);
  font-weight: var(--vf-weight);
}

.mono-casual-light {
  --vf-mono: 1;
  --vf-casl: 1;
  font-variation-settings:  "MONO" var(--vf-mono),
                            "CASL" var(--vf-casl),
                            "slnt" var(--vf-slnt),
                            "ital" var(--vf-ital);
  font-weight: var(--vf-weight);
}

.mono-casual-light-italic {
  --vf-mono: 1;
  --vf-casl: 1;
  --vf-slnt: -15;
  --vf-ital: 1;
  font-variation-settings:  "MONO" var(--vf-mono),
                            "CASL" var(--vf-casl),
                            "slnt" var(--vf-slnt),
                            "ital" var(--vf-ital);
  font-weight: var(--vf-weight);
}

.mono-casual-regular {
  --vf-mono: 1;
  --vf-casl: 1;
  --vf-wght: 400;
  font-variation-settings:  "MONO" var(--vf-mono),
                            "CASL" var(--vf-casl),
                            "slnt" var(--vf-slnt),
                            "ital" var(--vf-ital);
  font-weight: var(--vf-weight);
}

.mono-casual-italic {
  --vf-mono: 1;
  --vf-casl: 1;
  --vf-wght: 400;
  --vf-slnt: -15;
  --vf-ital: 1;
  font-variation-settings:  "MONO" var(--vf-mono),
                            "CASL" var(--vf-casl),
                            "slnt" var(--vf-slnt),
                            "ital" var(--vf-ital);
  font-weight: var(--vf-weight);
}

There is another use for Variable Fonts variables. The predefined axes change the way that we use existing attributes like font-weight, font-stretch and font-styles. Instead of preset values from 100 to 900 in increments of 100 units we now use values that are dependent on the font and can be used in as small increments as we want to use.

Recursive’s weight range is from 300 (light) to 1000 (black). Let’s say for example that we want to use a value that is halfway between semi-bold (600) and bold (700). We could define a variable with the value we want to use, like this:

:root {
  --vf-bold-weight: 650;
}

We could then use the variable wherever we want to use boldfaced text.

strong, b {
  font-weight: var(--vf-bold-weight);
}

If we want to change the value of our bold font, we need to change it in one place at the top and it will change it everywhere we use it.