CSS Variable Fonts: How to Use Flexible Font Axes

Variable fonts let one font file behave like many fonts by exposing adjustable axes such as weight, width, slant, and optical size. In CSS, that means you can fine-tune typography with fewer files, smoother transitions, and more precise control over text styling.

Quick answer: A variable font is a single font file with one or more adjustable axes. In CSS, you usually control it with font-weight, font-stretch, font-style, and sometimes font-variation-settings.

Difficulty: Intermediate

You'll understand this better if you know: basic CSS syntax, how @font-face works, and the difference between font families and font properties.

1. What Are CSS Variable Fonts?

CSS variable fonts are fonts built on OpenType variable font technology. Instead of shipping separate files for regular, bold, condensed, or italic styles, a single file can include a continuous range of design variations.

Traditional fonts often expose only a few static files, such as Regular, Bold, and Italic. Variable fonts can interpolate between those extremes and often beyond them.

2. Why Variable Fonts Matter

Variable fonts matter because they reduce file count while giving designers and developers more control. That can improve performance, simplify asset management, and create typography that adapts more smoothly to layout changes.

They are especially useful when a site needs multiple weights or widths across headings, body text, and responsive breakpoints. Instead of loading many separate font files, you may only need one or two variable files.

They are not always the best choice for every project, though. If a site only needs one static face and one bold face, a small set of traditional files may still be simpler.

3. Basic Syntax or Core Idea

The core idea is that you load a variable font with @font-face and then set the font family in normal CSS. If the font supports standard CSS properties for its axes, use those first.

Loading a variable font

This example defines a font family that points to a variable font file. Notice the font-weight range, which tells the browser the font can handle more than one weight value.

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

That rule tells the browser the file can serve a range of weights. Once the family is available, you can apply it like any other font family.

Using the font on text

body {
  font-family: "InterVariable", sans-serif;
  font-weight: 450;
}

This works because the browser maps the requested weight to the closest point supported by the variable font.

Directly setting an axis

When you need access to a custom axis or finer control, use font-variation-settings. This is the lower-level interface for variable font axes.

h1 {
  font-family: "InterVariable", sans-serif;
  font-variation-settings: "wght" 750, "wdth" 90;
}

Here, wght controls weight and wdth controls width, if the font includes those axes.

4. Step-by-Step Examples

Example 1: Regular body text and bold headings

A common use case is a single variable font that handles the whole page. The browser can render body text with a lighter weight and headings with a heavier one without switching font files.

body {
  font-family: "Source Sans Variable", sans-serif;
  font-weight: 400;
}

h1,
h2 {
  font-family: "Source Sans Variable", sans-serif;
  font-weight: 700;
}

This shows the simplest benefit of variable fonts: consistent typography from one family with a continuous weight range.

Example 2: Responsive width changes

Some variable fonts support a width axis. That allows you to make text feel wider on large screens and more compact on smaller screens.

.headline {
  font-family: "Roboto Flex", sans-serif;
  font-variation-settings: "wght" 650, "wdth" 100;
}

@media (min-width: 900px) {
  .headline {
    font-variation-settings: "wght" 650, "wdth" 85;
  }
}

The text becomes visually narrower on larger screens, which can help fit longer headlines into tighter layouts.

Example 3: Optical size for better readability

Some fonts expose an optical size axis, often written as opsz. Smaller text can use a version designed for readability, while larger headings can use a style optimized for display.

article {
  font-family: "Source Serif Variable", serif;
  font-variation-settings: "opsz" 14;
}

article h2 {
  font-variation-settings: "opsz" 36;
}

This is useful when the font is designed to look different at text sizes versus display sizes.

Example 4: Slant instead of separate italic files

Some variable fonts provide a slant or italic-related axis. That can reduce the need for separate italic files, though the exact behavior depends on the font design.

.quote {
  font-family: "Recursive", sans-serif;
  font-variation-settings: "slnt" -10;
}

That example leans the glyphs using a built-in slant axis instead of switching to a separate italic face.

5. Practical Use Cases

6. Common Mistakes

Mistake 1: Using font-variation-settings for everything

Many developers jump straight to the low-level axis syntax even for common weight changes. That works, but it is harder to read and can override more accessible CSS font properties.

Problem: Using font-variation-settings for standard axes can make your CSS harder to maintain and can conflict with font-weight, font-style, or font-stretch.

p {
  font-family: "InterVariable", sans-serif;
  font-variation-settings: "wght" 400;
}

Fix: Use font-weight for the weight axis when possible, and reserve font-variation-settings for custom or unsupported axes.

p {
  font-family: "InterVariable", sans-serif;
  font-weight: 400;
}

The corrected version is easier to understand and lets the browser apply the font in the most natural way.

Mistake 2: Forgetting to declare the supported range in @font-face

If the browser does not know the font supports a weight range, it may treat the font like a single static face. Then requests for bold or light styles may not behave as expected.

Problem: Without a range declaration, the browser may not map weight values correctly, so the variable font appears to ignore your font-weight changes.

@font-face {
  font-family: "InterVariable";
  src: url("/fonts/InterVariable.woff2") format("woff2");
}

h1 {
  font-family: "InterVariable", sans-serif;
  font-weight: 800;
}

Fix: Declare the axis range that the font supports.

@font-face {
  font-family: "InterVariable";
  src: url("/fonts/InterVariable.woff2") format("woff2");
  font-weight: 100 900;
}

Once the browser knows the range, it can choose the correct point on the axis.

Mistake 3: Assuming every variable font supports every axis

Not all variable fonts expose the same axes. A font may support weight but not width, or optical size but not slant.

Problem: If you set an axis the font does not include, the property may have no visible effect, which often looks like the font is broken.

.logo {
  font-family: "SomeFontVariable", sans-serif;
  font-variation-settings: "wdth" 75;
}

Fix: Check the font documentation or use only axes you know the font provides.

.logo {
  font-family: "SomeFontVariable", sans-serif;
  font-weight: 700;
}

The corrected version uses a supported axis, so the browser can render a visible change.

7. Best Practices

Use standard CSS properties first

When a variable font supports a common axis like weight or width, prefer the matching CSS property. This improves readability and keeps your code aligned with the browser’s typography model.

h2 {
  font-family: "InterVariable", sans-serif;
  font-weight: 650;
}

That is clearer than hiding the same idea inside low-level axis settings.

Declare axis ranges in @font-face

Explicit ranges help browsers understand what the font can do. They also make your font loading rules easier to debug later.

@font-face {
  font-family: "Acme Variable";
  src: url("/fonts/acme-variable.woff2") format("woff2");
  font-weight: 300 800;
  font-stretch: 75% 125%;
}

This gives the browser enough information to map requested values cleanly.

Test across sizes, not just weights

A variable font can look very different at small body text sizes versus large display sizes. Optical sizing, width changes, and heavier weights can all affect spacing and readability.

small {
  font-family: "Source Serif Variable", serif;
  font-size: 0.875rem;
}

h1 {
  font-family: "Source Serif Variable", serif;
  font-size: 3rem;
}

The same font can need different tuning at different sizes, so test typographic scales rather than single samples.

8. Limitations and Edge Cases

If a variable font seems not to work, the first things to check are the font file format, the declared axis range, and whether the browser actually supports the axis you are trying to use.

9. Practical Mini Project

Let’s build a small card layout that uses one variable font for both body text and headings. The goal is to use different weights and sizes while keeping the typography consistent.

@font-face {
  font-family: "Avenir Next Variable";
  src: url("/fonts/avenir-next-variable.woff2") format("woff2");
  font-weight: 300 800;
}

.card {
  font-family: "Avenir Next Variable", sans-serif;
  font-weight: 400;
  line-height: 1.5;
  padding: 1.5rem;
  border: 1px solid #ddd;
  border-radius: 0.75rem;
}

.card h2 {
  font-weight: 750;
  margin-top: 0;
}

.card p {
  font-weight: 350;
}

This mini project shows how one variable font can supply a light paragraph style and a heavier heading style without changing the font family.

10. Key Points

11. Practice Exercise

Expected output: A simple page where body text, headings, and one highlighted element all use the same variable font with visibly different axis values.

Hint: Start with font-weight, then add font-variation-settings only if the font documentation lists an extra axis.

Solution:

@font-face {
  font-family: "Practice Variable";
  src: url("/fonts/practice-variable.woff2") format("woff2");
  font-weight: 200 800;
}

body {
  font-family: "Practice Variable", sans-serif;
  font-weight: 350;
}

h1 {
  font-weight: 750;
}

.label {
  font-variation-settings: "wdth" 85;
}

12. Final Summary

CSS variable fonts give you a flexible way to control typography with fewer font files and more granular design choices. They are especially valuable when your layout needs multiple weights, widths, or optical sizes and you want those changes to feel smooth and consistent.

In most cases, start with standard CSS typography properties such as font-weight and font-stretch. Use font-variation-settings only when you need a custom axis or more direct control. That balance keeps your CSS easier to maintain while still taking advantage of what variable fonts can do.

If you are building a design system or a content-heavy site, the next step is to inspect a real variable font’s axis list and experiment with how it behaves at different sizes and weights.