Variable Fonts: One File, Every Weight

A typeface family used to mean a stack of separate files: Regular, Medium, Bold, Bold Italic, and so on, each one a fixed, finished design. A variable font collapses that whole stack into a single file that can produce any weight, width, or slant in between, calculated on the fly. This article covers how that works, what you can actually control from CSS, and includes a live slider so you can feel the difference rather than just read about it.

Fast facts

Variable fonts, in one screen

A variable font stores a range along one or more "axes," rather than a single fixed design, and lets the browser pick any point along that range.

One file, a range of designs
Axes let a single file represent a continuous range instead of separate files per weight or width.
Where axes live
The font's fvar table lists each axis and its minimum, default, and maximum value.
On the web
font-weight, font-stretch, and font-style drive the common axes. font-variation-settings reaches any axis directly.
Browser support
Universal in every modern browser. This has been safe to rely on for several years now.
1.What makes a font "variable"

A static font stores one fixed outline per glyph. A variable font instead stores a small number of extreme "master" outlines for each glyph, a Thin master and a Black master, say, plus instructions for how to interpolate smoothly between them. When a browser needs a weight in between, like 550, it doesn't pick a pre-made design; it calculates one on demand by blending the masters, and it can do that for any value the axis allows, not just round numbers.

This is still delivered as one ordinary font file, usually .woff2 on the web, just one that's typically larger than a single static weight, because it's carrying the master outlines and interpolation data for the entire range. In exchange, it's almost always smaller than shipping several static weights separately, and it offers values no static release ever did: the exact weight that looks right at a specific size, rather than whichever pre-cut weight happens to be closest.

2.Try it: the weight axis live

This is the whole idea of variable fonts in one control. The text below is one single font file (Inter, requested from Google Fonts as a variable range) rendering every value the slider passes it, not switching between a handful of pre-made weights.

Weight axis wght
Drag to any point between 100 and 900. There's no "snapping" to Regular, Medium, or Bold underneath.
Nimatype
100 900
ThinRegularBlack
font-weight: 400
3.The five registered axes

OpenType formally registers five axis tags, each with a standardized meaning and its own CSS property. A font can implement any subset of these (nothing requires all five), and most variable fonts released today implement only wght, sometimes paired with wdth or opsz.

wght: Weightfont-weight
Stroke thickness. 400 is required to mean Regular, matching the CSS scale you already know from static fonts.
11000
wdth: Widthfont-stretch
Overall glyph width, as a percentage of the design's normal width. 100% is required to mean Regular.
50%200%
ital: Italicfont-style: italic
A binary switch between upright (0) and a true italic design (1), not a continuous slant.
01
slnt: Slantfont-style: oblique Ndeg
A continuous oblique angle, in degrees, rather than a separate italic design. A font uses ital or slnt, rarely both.
−90°
opsz: Optical sizefont-optical-sizing
Adapts proportions and stroke contrast for the size the text is set at: sturdier at small sizes, more refined at large ones.
~6pt~144pt
Optical size is the quiet one
Unlike the others, opsz usually isn't something a person drags a slider on directly. The point is that browsers can drive it automatically, tied to the rendered font size, via font-optical-sizing: auto (the default in every modern browser when a font supports it). Set a heading in a large size and body copy in a small one from the same variable font, and each can quietly get proportions suited to its own size, without any extra CSS.
4.Custom axes

Beyond the five registered axes, a type designer can define entirely custom ones: a grade axis that shifts weight slightly without changing the letter widths (useful for matching perceived weight across light and dark backgrounds), or axes that control things as specific as counter size or serif length. Custom axes use uppercase four-letter tags to distinguish them from the lowercase registered ones, and, just like the stylistic sets covered in the OpenType Feature Reference, there's no universal meaning to a custom tag across different fonts. You have to check the specific font's own documentation for what it does.

Because no CSS shorthand exists for an arbitrary axis, custom axes are only reachable through font-variation-settings, using the exact tag the font defines, for example, font-variation-settings: "GRAD" 30; for a font that ships a grade axis.

5.Named instances

A variable font's axis is continuous, but most type designers still want to recommend a handful of specific points along it, the coordinates that correspond to what would have been "SemiBold" or "Condensed Bold" in a static release. These are called named instances, and they're stored right in the font's fvar table alongside the axes themselves. Design and office software can list them as a familiar style menu, and CSS's own font-weight keywords (like bold) map onto the nearest matching instance automatically. None of this limits what the axis can actually do. The values between named instances are still fully available to anyone who reaches for them directly.

6.Two ways to write it

Exactly like the OpenType feature properties covered elsewhere in this series, the five registered axes each have a familiar CSS shorthand, while anything else, including custom axes, needs the raw syntax.

Shorthand properties

  • font-weight: 550;
  • font-stretch: 85%;
  • font-style: italic;
  • font-style: oblique 12deg;
  • font-optical-sizing: auto;

Raw axis tags

  • font-variation-settings: "wght" 550;
  • font-variation-settings: "wdth" 85;
  • font-variation-settings: "opsz" 36;
  • font-variation-settings: "GRAD" 30; (custom)
  • Multiple axes: comma-separate the pairs

One rule worth remembering: wherever font-variation-settings and a shorthand property both target the same axis, the raw setting wins, regardless of the order they're written in. Mixing the two for the same axis rarely does what you'd expect, so pick one approach per axis.

7.Why bother

Beyond the obvious performance win of shipping one file instead of six, variable fonts open up a few things static fonts simply can't do. Because font-weight and font-variation-settings are ordinary, animatable CSS values, many browsers can smoothly transition or animate a variable axis with plain CSS: a heading that gently gains weight on hover, without a single extra image or font file. And because the weight or width can be tied to something like viewport size, type can respond fluidly to its container the same way layout already does, instead of jumping between a small, fixed set of pre-cut styles.

Design takeaway
Variable fonts don't replace design judgment. A font still needs well-drawn masters and sensible interpolation for the in-between weights to look right, which is real design work. What they remove is the old constraint of picking a handful of fixed cuts in advance. If a project needs a weight the type foundry didn't happen to release as a static file, a variable font may already have it.
8.Quick reference
Weight
wght: font-weight
Width
wdth: font-stretch
Italic / slant
ital / slnt: font-style
Optical size
opsz: font-optical-sizing
Custom axes
Uppercase tags, font-specific: font-variation-settings only
Presets
Named instances, stored in fvar, read by style menus and font-weight keywords