one font file instead of ten styles
Variable fonts bundle a complete type family with every style into a single file and can be interpolated seamlessly across weight, width and optical size purely through CSS. Once you understand the axes and load them correctly, you save load time while gaining design control that static font styles never offered.
Table of Contents
- 1. What variable fonts really are
- 2. Understanding axes: wght, wdth, slnt, ital, opsz
- 3. font-variation-settings versus registered properties
- 4. Named instances and robust fallbacks
- 5. Performance: one file instead of ten styles
- 6. Animating variable fonts
- 7. Coupling optical sizing automatically
- 8. Using custom axes
- 9. Common mistakes and browser support compared
- 10. Summary
- 11. FAQ
1. What variable fonts really are
A variable font is a single font file that contains a complete design space of axes, instead of requiring one separate file per style. Where Regular, Medium, Bold, Light and Italic used to be five separate files, a variable font delivers the same range, often with far more intermediate values, in a single file that is usually smaller than two or three static styles combined. The format is based on the OpenType Font Variations specification and is natively supported by every current browser.
The decisive difference from classic web fonts lies in continuity. A static style only knows Bold or Regular, a variable font knows every value in between, for example weight 550 or 612. This flexibility opens design options that simply did not exist before, such as a heading that smoothly transitions from thin to bold while scrolling, without the browser having to load a second file. For teams that think of typography as a design system, variable fonts are therefore not a niche feature but the logical continuation of what CSS already hinted at with font-weight.
In practice this means: instead of five @font-face blocks each with its own .woff2 file, you load a single file and control every intermediate value through CSS. That reduces HTTP requests, simplifies caching, and makes design iterations cheaper, because no new style needs to be loaded when a designer wants a weight of 600 instead of 700.
2. Understanding axes: wght, wdth, slnt, ital, opsz
Every variable font defines its own set of axes, but five registered axes are standardized through the OpenType specification and appear most often. wght controls weight analogous to font-weight, typically in the range 100 to 900. wdth controls the width from narrow to wide as a percentage of the normal width. slnt controls the slant in degrees for a genuine oblique without artificial skewing, ital switches between upright and italic, usually as a binary value of 0 or 1. opsz controls the optical size and automatically adjusts stroke weight and proportions to the font size in use.
These registered axes are written in lowercase because lowercase tags are reserved for standardized axes. A type designer can additionally define arbitrary custom axes, which are then written in uppercase, such as GRAD for optical correction or brand specific axes like SERF for the amount of serifing. Not every variable font supports all five registered axes, many are limited to wght alone or to the combination of wght and ital.
The value range of each axis is stored inside the font itself and can be read directly with the CSS font inspector in modern developer tools. Anyone who sets a value outside the supported range risks the browser clamping it to the nearest valid value, which can produce unexpected results if the range was not checked beforehand.
/* Register the variable font with its supported axis ranges */
@font-face {
font-family: "Inter Variable";
src: url("/fonts/InterVariable.woff2") format("woff2-variations");
font-weight: 100 900; /* supported wght range */
font-stretch: 75% 125%; /* maps to wdth axis */
font-style: normal;
font-display: swap;
}
body {
font-family: "Inter Variable", system-ui, sans-serif;
}
/* Standard axes can be set via ordinary properties */
h1 {
font-weight: 650; /* fractional weight, not possible with static fonts */
font-stretch: 105%;
}
A practical side effect of this axis based architecture is that a single variable font can serve body text, headings and UI elements at once, where three separate type families with different licensing used to be required.
3. font-variation-settings versus registered properties
For the five registered axes you should use the ordinary CSS properties wherever possible, meaning font-weight for wght, font-stretch for wdth, font-style: oblique Ng for slnt and font-style: italic for ital. These properties have long existed in CSS, are well documented, and continue to work sensibly even if the browser does not support variable fonts, because they fall back to static styles.
The generic property font-variation-settings is needed mainly for custom axes that have no registered CSS property, or when several axes need to be set at once in a single declaration. The syntax requires a comma separated list of four letter tags in quotes and the corresponding numeric value. Important: font-variation-settings does not automatically stay in sync with registered shorthands, if both target the same axis in the same selector at once, conflicts can arise that are hard to debug.
/* Prefer standard properties for registered axes */
.button {
font-weight: 550;
}
/* font-variation-settings needed for combining multiple axes at once,
or for custom axes without a dedicated CSS property */
.headline-hover {
font-variation-settings: "wght" 700, "wdth" 110, "GRAD" 40;
transition: font-variation-settings 0.3s ease;
}
.headline-hover:hover {
font-variation-settings: "wght" 850, "wdth" 100, "GRAD" 0;
}
4. Named instances and robust fallbacks
Type designers frequently define what are called named instances, fixed curated combinations of axis values with their own name, for example "SemiBold Condensed" as a combination of a specific wght and wdth value. These named instances can be addressed via @font-face using the font-named-instance descriptor, which shows up in operating systems and some applications as its own font in the selection list. For controlling a website through CSS it is usually simpler to set the underlying axis values directly rather than relying on named instances, since their support is less consistent.
A robust fallback stack remains mandatory regardless. If loading the variable font fails for any reason, such as a network error or an outdated browser, the font-family declaration must contain a sensible system font as a second value. In addition you should check with @supports (font-variation-settings: normal) whether the browser supports variable axes at all before deploying elaborate interpolations that should fall back to static but functional results on older browsers.
5. Performance: one file instead of ten styles
The performance advantage of variable fonts shows most clearly when a website uses several styles at once, for example Regular, Bold and Italic for body copy plus a separate display weight for headings. Instead of four separate .woff2 files each with its own HTTP request, the browser loads a single file that covers all four states. Even though that one file is larger than a single static style, it is in total almost always smaller than the sum of all the static styles that would be needed, because glyph data is shared and interpolated between states rather than duplicated.
A second, often underestimated advantage is caching behavior. A single font file is loaded once and then reused from the browser cache for every weight variation, without a new request being necessary for new intermediate values. For international projects with subsetting it is also worth looking at unicode-range, to load only the character sets actually needed, subsetting remains an effective tool against unnecessarily large files even with variable fonts.
/* Subsetting still matters for variable fonts */
@font-face {
font-family: "Inter Variable";
src: url("/fonts/InterVariable-subset-latin.woff2") format("woff2-variations");
font-weight: 100 900;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC;
font-display: swap;
}
/* Preload the single variable font file for the critical path */
In addition, a <link rel="preload"> for the critical variable font belongs in the document head, so the browser loads the file in parallel with the HTML instead of only discovering it after parsing the CSS. Combined with font-display: swap, this prevents visible layout shift while the font is still loading.
For content management systems with many templates it is also worth centralizing the font file URL in a CSS custom property, so switching the variable font happens in a single place in the theme rather than being repeated across dozens of templates. This reduces maintenance effort and prevents outdated font references from lingering in forgotten templates.
6. Animating variable fonts
Because axis values are continuous numbers, variable fonts can be animated fluidly, for example on hover states, while scrolling, or as a loading animation. Modern browsers interpolate font-variation-settings as an animatable property, so a transition from weight 400 to weight 700 renders not abruptly but as a smooth motion through every intermediate value. For performance critical cases it is advisable to limit the animation to a few axes, since every change can trigger a layout reflow if character widths shift due to wdth or wght.
A proven technique is coupling scroll position with font weight via CSS scroll driven animations or, as a more robust fallback, through an IntersectionObserver in JavaScript that updates a CSS custom property. This lets a landing page heading fade from thin to bold as it enters the viewport, without needing multiple images or SVGs, the whole effect remains pure text rendering with full accessibility for screen readers.
/* Animate weight smoothly on hover */
.card-title {
font-weight: 450;
transition: font-weight 0.25s ease-out;
}
.card:hover .card-title {
font-weight: 700;
}
/* Reduced motion users get an instant, non-animated change */
@media (prefers-reduced-motion: reduce) {
.card-title {
transition: none;
}
}
7. Coupling optical sizing automatically
The opsz axis is one of the most underrated features of variable fonts, because it solves a problem designers used to have to fix manually with separate display and text styles. Small font sizes need thicker strokes and larger apertures to remain legible at low resolution, while large headings can carry finer, more elegant proportions. Without optical size adjustment, one and the same style looks too thin at 12px and too clunky at 72px.
Many browsers automatically couple opsz to font-size when the font supports it and no explicit font-variation-settings declaration overrides that coupling. For full control you can also bind opsz manually to a custom property derived with calc() from the current font size, which is especially useful in fluid type scales built with clamp(), since stroke weight and size then move in sync using the same formula.
8. Using custom axes
Beyond the five registered axes, some type families define their own custom axes, recognizable by uppercase tags like GRAD for grade, a fine optical correction of weight without changing character width, useful for dark mode adjustments where light text on a dark background appears optically thinner than dark text on a light background. Some foundries also offer axes for contrast, x-height, or even the degree of rounding in letterforms.
These custom axes can only be controlled through font-variation-settings, since CSS has no registered shorthand for them. Important in practice: which custom axes a variable font supports and in what value range is not part of the CSS specification, it must be read from the type foundry's documentation or directly from the font file using tools such as Wakamai Fondue.
/* Custom axis GRAD for optical correction in dark mode */
.dark body {
font-variation-settings: "wght" 400, "GRAD" -25;
}
body {
font-variation-settings: "wght" 400, "GRAD" 0;
}
For daily work with custom axes it is also worth checking modern browser developer tools, since Chrome and Firefox now ship their own font inspector that displays every axis of a loaded variable font along with its value range as an interactive slider. This makes coordination between development and design considerably easier, because design values can be tried directly in the browser and then carried over into CSS without the detour through separate font editing software.
9. Common mistakes and browser support compared
The most common mistake is using font-variation-settings for registered axes even though a dedicated, semantically clearer property such as font-weight exists. A second mistake is missing the range descriptor in @font-face, which means the browser only knows the default value instead of the actually supported span, rendering intermediate font-weight values ineffective. A third mistake is missing subsetting in multilingual projects, causing users to load unnecessarily large files even though they only need Latin characters.
| Task | Fragile / cumbersome | Recommended pattern | Benefit |
|---|---|---|---|
| Loading five styles | 5 x @font-face | 1 x variable font | Fewer requests, often smaller total size |
| Setting weight | font-variation-settings: "wght" 600 |
font-weight: 600 |
Falls back to static styles |
| Declaring the range | No range descriptor | font-weight: 100 900 |
Intermediate values are honored |
| Optical size | Maintained manually for each size | Couple opsz automatically |
Consistent stroke weight across all sizes |
| Multilingual projects | Loading the full font file | unicode-range subsetting |
Smaller file, targeted at the alphabet |
Browser support for variable fonts has been present for several years across all current versions of Chrome, Firefox, Safari and Edge, both on desktop and mobile. Missing support practically only exists in very old browser versions, for which a static fallback style in the font-family chain is sufficient. The real challenge therefore is not support but the correct use of axes and careful performance optimization.
10. Summary
Variable fonts solve a fundamental problem in web typography: instead of loading a separate file per style, a single file delivers the entire design space of weight, width, slant and optical size. Registered axes such as wght and wdth should be set through the matching CSS shorthands, font-variation-settings remains reserved for custom axes and combined declarations. A clean range descriptor in @font-face, subsetting via unicode-range and a solid fallback stack make the deployment production ready.
The biggest design win lies in continuity: animations, responsive weight adjustments and optical size coupling all become pure CSS with variable fonts, without needing extra images, SVGs or JavaScript libraries. Anyone already loading several static styles of a family typically saves both load time and maintenance effort by switching to a variable font.
Variable Fonts in Practice — the essentials at a glance
Axes
wght, wdth, slnt, ital, opsz are registered, custom axes use uppercase tags.
CSS usage
Set registered axes via shorthands like font-weight, custom axes via font-variation-settings.
Performance
One file instead of several styles, do not forget the range descriptor in @font-face, subset with unicode-range.
Animation
Axis values are continuously animatable, respect prefers-reduced-motion.