CSS Typography

Wie man Variable Fonts in CSS verwendet

Updated Februar 24, 2026
Ein praktischer Leitfaden zur Verwendung von Variable Fonts in CSS – Laden, Achsen setzen, Eigenschaften animieren und Browser-Support.

How to Use Variable Fonts in CSS

Variable fonts are one of the most significant advances in web typography in decades. A single variable font file can contain an infinite number of design variations — every weight from Thin to Black, every width from Condensed to Extended, every optical size from Caption to Display — accessible through continuous numeric axes rather than discrete files. Where a complete Inter family once required eight or more separate WOFF2 files to cover the full weight range, Inter's variable font version packages the same range into a single file that's smaller than two static files combined.

The CSS interface to variable fonts is straightforward but has several layers worth understanding thoroughly. This guide covers the complete workflow: loading the font, setting axes in CSS, using standard properties, animating axes, and handling the small remaining browser support gaps.

Loading a Variable Font with @font-face

Loading a variable font uses the same @font-face at-rule as static fonts, with two key differences: the format string includes "woff2" (variable fonts use the standard WOFF2 container), and the font-weight (or other axis) descriptor accepts a range rather than a single value.

@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

The font-weight: 100 900 range declaration tells the browser that this font file can produce any weight between 100 and 900. When your CSS later specifies font-weight: 450, the browser doesn't need to find a separate file for that value — it interpolates within the single variable font. This is the fundamental capability that makes variable fonts so powerful.

For fonts that include both regular and italic variations in a single file (not all do), you can declare both axes together:

@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal oblique 0deg 10deg;
  font-display: swap;
}

Some variable fonts separate regular and italic into two files (sometimes called "Roman" and "Italic" variable fonts). In that case, use two @font-face blocks with matching font-family names:

@font-face {
  font-family: "Roboto";
  src: url("/fonts/roboto-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Roboto";
  src: url("/fonts/roboto-variable-italic.woff2") format("woff2");
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}

Roboto follows this two-file pattern in its variable font release. Montserrat packages both upright and italic variations in a single file. Check the font's documentation or inspect the file with a tool like Wakamai Fondue to determine which structure a given font uses.


Setting Axes with font-variation-settings

The font-variation-settings property is the low-level interface to all variable font axes. It accepts a comma-separated list of axis tag–value pairs, where each axis tag is a four-character string (either lowercase for standard axes or uppercase for custom/registered axes):

.text-custom {
  font-variation-settings: "wght" 650, "wdth" 85, "opsz" 14;
}

This sets the weight axis (wght) to 650, the width axis (wdth) to 85% of normal, and the optical size axis (opsz) to 14 — appropriate for 14px body text. The exact value ranges available depend on the specific font: a font might support wght from 100 to 900, or it might only go from 300 to 700. Values outside the supported range are clamped to the nearest endpoint.

font-variation-settings has a critical caveat: it overwrites all axes with a single property, not individual ones. If you set font-variation-settings: "wght" 700 on a parent element and then set font-variation-settings: "opsz" 14 on a child, the child inherits only "opsz" 14 — the weight axis resets to its default. This non-additive behavior is a frequent source of bugs.

The solution is to either set all axes in a single declaration or, preferably, use high-level CSS properties (covered in the next section) wherever they exist and reserve font-variation-settings for axes that don't have standard CSS equivalents.

Standard Axis Tags

Five axis tags are registered in the OpenType specification and have predefined value ranges:

Tag Axis CSS Property Equivalent
wght Weight font-weight
wdth Width font-stretch
ital Italic font-style: italic
slnt Slant font-style: oblique
opsz Optical Size font-optical-sizing

Custom axes use uppercase four-character tags and are font-specific. Oswald's grade axis might be tagged GRAD, while another font might use SOFT for a softness parameter. These custom axes have no CSS property equivalent — font-variation-settings is the only way to set them.


Using Standard CSS Properties (font-weight, font-stretch)

For the five standard axes, CSS provides dedicated properties that are both more readable and more composable than font-variation-settings. These properties map directly to the underlying axis values:

/* Instead of: font-variation-settings: "wght" 650; */
.text-semibold {
  font-weight: 650;
}

/* Instead of: font-variation-settings: "wdth" 85; */
.text-condensed {
  font-stretch: 85%;
}

/* Optical sizing */
.body-text {
  font-optical-sizing: auto; /* browser infers from font-size */
}

.display-heading {
  font-size: 4rem;
  font-optical-sizing: auto;
}

font-weight with a variable font accepts any numeric value between 1 and 1000 (constrained by the font's wght axis range), not just the traditional multiples of 100. This means you can use precise intermediate weights like 450 or 620 when a design calls for it. In practice, most designs use the standard values (400, 500, 600, 700), but the granularity is available when you need it.

font-optical-sizing: auto deserves special attention. Optical sizing refers to the design adjustments that make type more legible at different physical sizes — at small sizes, letterforms are opened up and stroke contrast is reduced; at large display sizes, more contrast and refinement are appropriate. When set to auto, the browser automatically maps the font-size value to the font's opsz axis, selecting the visually optimal variation without any manual intervention. For Oswald and other fonts with an opsz axis, font-optical-sizing: auto is a simple way to improve quality across your entire type system.

Inheritance and Composition

One of the main reasons to prefer standard CSS properties over font-variation-settings is how they compose with inheritance:

:root {
  font-weight: 400;
  font-optical-sizing: auto;
}

h1 {
  font-weight: 700; /* Changes weight without affecting opsz */
}

.caption {
  font-weight: 300; /* Changes weight without affecting opsz */
}

Each property can be set and overridden independently on descendant elements. With font-variation-settings, you'd have to repeat all the axes on every override to avoid resetting others — a maintenance burden that increases with the number of axes you use.


Animating Variable Font Axes

Variable font axes are animatable CSS properties, which opens up typographic effects that would have been impossible with static fonts. Weight transitions on hover, smooth optical size adjustments on scroll, kinetic type that responds to user interaction — all of these are achievable with CSS transitions and animations.

Basic weight transition on hover:

.logo {
  font-weight: 300;
  transition: font-weight 200ms ease-in-out;
}

.logo:hover {
  font-weight: 700;
}

This smoothly interpolates the weight axis from 300 to 700 over 200 milliseconds, creating an effect that cannot be replicated with static font files where only discrete jumps between weights are possible.

A more sophisticated animation using @keyframes:

@keyframes pulse-weight {
  0%, 100% {
    font-weight: 100;
    letter-spacing: 0.1em;
  }
  50% {
    font-weight: 900;
    letter-spacing: -0.02em;
  }
}

.animated-title {
  animation: pulse-weight 3s ease-in-out infinite;
}

This combines weight animation with letter-spacing to create a breathing effect — as the type gets heavier, spacing tightens to compensate for the increased visual mass. This kind of compensation is common in hand-crafted typography but difficult to automate in CSS without variable fonts.

Scroll-Driven Weight Variation

With CSS scroll-driven animations (supported in Chromium 115+), you can tie weight to scroll position:

@keyframes weight-on-scroll {
  from { font-weight: 100; }
  to { font-weight: 900; }
}

.scroll-headline {
  animation: weight-on-scroll linear;
  animation-timeline: scroll();
  animation-range: 0vh 50vh;
}

This makes the headline grow bolder as the user scrolls down the first half of the page. While this is more of a design effect than a usability feature, it illustrates the range of typographic expression that variable fonts enable.

Performance note: Animating font-weight and font-variation-settings triggers text relayout, which can cause layout shift and jank on complex pages. Test animated variable font usage on mid-range devices, not just high-end development hardware. Prefer animating only display text (large headings, hero sections) rather than body text, where reflow during animation would be more visually disruptive.


Browser Support and Fallbacks

Variable font support in browsers is now essentially universal. All modern browsers — Chrome 66+, Firefox 62+, Safari 11+, Edge 17+ — support variable fonts fully. The global coverage is over 97% as of 2024. The only browsers that lack support are Internet Explorer 11 (which doesn't support WOFF2 either) and very old mobile browsers.

For the small percentage of users on non-supporting browsers, the fallback strategy is straightforward. Use the CSS @supports rule to provide static font alternatives:

/* Fallback for browsers without variable font support */
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* Variable font for supporting browsers */
@supports (font-variation-settings: normal) {
  @font-face {
    font-family: "Inter";
    src: url("/fonts/inter-variable.woff2") format("woff2");
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
  }
}

The @supports (font-variation-settings: normal) block is only parsed by browsers that understand variable fonts. Browsers that don't support this feature fall back to the static @font-face declarations defined before it. The font-family name is the same in both cases, so the rest of your CSS doesn't need to change.

In practice, given the near-universal support, many teams omit the @supports fallback entirely for internal tools and modern web applications. For public-facing sites with diverse audience demographics, including the fallback is good practice without significant maintenance cost.


Practical Example: Complete Variable Font Setup

Here is a production-ready variable font configuration using Inter, combining all the techniques covered in this guide:

/* Variable font with @supports fallback */
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@supports (font-variation-settings: normal) {
  @font-face {
    font-family: "Inter";
    src: url("/fonts/inter-variable.woff2") format("woff2");
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
  }
}

/* Base typography */
body {
  font-family: "Inter", system-ui, -apple-system, sans-serif;
  font-weight: 400;
  font-optical-sizing: auto;
}

/* Headings use continuous weight scale */
h1 { font-weight: 800; }
h2 { font-weight: 700; }
h3 { font-weight: 600; }
h4 { font-weight: 550; }

/* Fine-grained label weights */
.label-primary   { font-weight: 500; }
.label-secondary { font-weight: 450; }
.caption         { font-weight: 400; font-size: 0.75rem; }

/* UI state — weight communicates emphasis */
.button         { font-weight: 500; }
.button-primary { font-weight: 600; }
.button-cta     { font-weight: 700; }

/* Smooth weight transition for interactive elements */
.nav-link {
  font-weight: 400;
  transition: font-weight 150ms ease;
}

.nav-link:hover,
.nav-link[aria-current="page"] {
  font-weight: 600;
}

This setup uses the fallback pattern for broad compatibility, maps optical sizing to auto for quality across all sizes, and demonstrates how variable fonts enable a continuous weight scale for headings and fine-grained UI states. The weight transition on navigation links creates a subtle, polished hover effect.

It is worth noting how dramatically variable fonts have changed the economics of rich typography on the web. Before variable fonts, providing five weight variants for a typeface meant five network requests, five files to cache, and five @font-face declarations to maintain. A variable font collapses all of that into one file, one declaration, and one cache entry — while actually expanding typographic capability to include weights that never existed as discrete static variants. Intermediate weights like 450 or 550 are now accessible without any additional cost, enabling more nuanced hierarchical systems than were practical before.

For the complete reference on variable font capabilities — including width, slant, grade, and custom axes — the variable fonts complete guide covers every standard and registered axis in depth. For performance measurement and file size comparisons between variable and static font approaches, see variable fonts performance. The best variable fonts article provides curated recommendations for high-quality variable fonts available in open-source and commercial releases.

Typography Terms

Try These Tools

Fonts Mentioned

Roboto Sans Serif #1

Designed by Christian Robertson for Google's Material Design ecosystem, this neo-grotesque sans-serif is the most widely used typeface on the web and Android. Its dual-nature design balances mechanical precision with natural reading rhythm, making it equally at home in UI labels and long-form text. The variable font supports width and weight axes alongside Cyrillic, Greek, and extended Latin scripts.

The quick brown fox jumps over the lazy dog
Inter Sans Serif #5

Rasmus Andersson spent years refining this neo-grotesque specifically for computer screens, optimizing letter spacing, x-height, and stroke contrast for high readability at small sizes on digital displays. An optical size axis (opsz) lets the font automatically adjust its design for captions versus headlines, while the weight axis covers the full range from thin to black. It has become the de facto choice for dashboards, documentation sites, and developer tools worldwide.

The quick brown fox jumps over the lazy dog
Montserrat Sans Serif #6

Inspired by the geometric signage and storefronts of the Montserrat neighborhood in Buenos Aires, Julieta Ulanovsky created this typeface to capture the spirit of early 20th-century urban lettering. Clean circular forms and strong geometric proportions give it an assertive presence ideal for headlines, branding, and landing pages. The variable weight axis spans a wide range, and Cyrillic and Vietnamese scripts are included.

The quick brown fox jumps over the lazy dog
Oswald Sans Serif #12

Vernon Adams reimagined the classic grotesque condensed genre for the web, taking cues from early American gothics and condensed newspaper type. Its tall, narrow proportions command attention in headlines, posters, and display contexts where vertical rhythm is tight. A variable weight axis and Cyrillic support expand its utility beyond English-language applications.

The quick brown fox jumps over the lazy dog

Related Articles