CSSVariable Fonts

Variable Fonts

Variable fonts are a single font file that contains the entire design space of a typeface — all weights, widths, slants, and optical sizes. Instead of loading separate files for regular, bold, italic, and condensed, you load one file and use CSS to access any point in the design space. This reduces HTTP requests, enables true intermediate weights like 650, and unlocks creative possibilities that static fonts simply can't offer.

How variable fonts work

A variable font contains a set of axes — dimensions of variation defined by the type designer. Standard registered axes have four-letter lowercase tags; custom axes use uppercase tags. You control axes with CSS properties or with the low-level font-variation-settings.

Axis

Tag

CSS property

Range (typical)

Weight

wght

font-weight

100–900

Width

wdth

font-stretch

75%–125%

Italic

ital

font-style: italic

0 or 1

Slant

slnt

font-style: oblique Ndeg

-15 to 0

Optical size

opsz

font-optical-sizing

pt size

Custom axes

GRAD, XHGT...

font-variation-settings

Designer-defined

Loading a variable font

CSS
/* Declare the variable font with a weight range */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter-Variable.woff2') format('woff2 supports variations'),
       url('/fonts/Inter-Variable.woff2') format('woff2');
  font-weight: 100 900;    /* declares the full range */
  font-style: normal;
  font-display: swap;
}

/* For a font with an italic variable axis */
@font-face {
  font-family: 'Recursive';
  src: url('/fonts/Recursive.woff2') format('woff2');
  font-weight: 300 1000;
  font-style: normal;      /* even italic values use normal here */
  font-display: swap;
}
Using standard axes via CSS properties

CSS
/* Weight axis — any value between the declared range */
.thin     { font-weight: 100; }
.regular  { font-weight: 400; }
.medium   { font-weight: 500; }
.semibold { font-weight: 600; }
.bold     { font-weight: 700; }
.black    { font-weight: 900; }

/* In-between values — only possible with variable fonts */
.hero-title    { font-weight: 750; }
.subhead       { font-weight: 525; }

/* Width axis */
.condensed  { font-stretch: 75%; }
.normal     { font-stretch: 100%; }
.expanded   { font-stretch: 125%; }

/* Optical sizing — font adjusts its design for the current size */
body h1 {
  font-size: 3rem;
  font-optical-sizing: auto; /* font uses its 'display' optical size */
}

body p {
  font-size: 1rem;
  font-optical-sizing: auto; /* font uses its 'text' optical size — subtly different */
}
font-variation-settings — low-level axis control

For custom axes or when you need precise control, use font-variation-settings to set axes directly:

CSS
/* Standard axes via font-variation-settings */
.custom {
  font-variation-settings:
    'wght' 650,    /* weight: 650 */
    'wdth' 85,     /* width: 85% */
    'slnt' -10;    /* slant: -10 degrees */
}

/* Recursive font — custom axes */
.recursive {
  font-variation-settings:
    'MONO' 1,      /* monospace: 0=proportional, 1=monospace */
    'CASL' 0.5,    /* casual: 0=linear, 1=casual */
    'wght' 400,
    'slnt' 0,
    'ital' 0.5;    /* semi-italic */
}

/* CAUTION: font-variation-settings resets all axes not listed
   Use the high-level properties when you only need one axis */
font-variation-settings resets all axes not explicitly listed to their default values — if you set wght via font-variation-settings, also include all other axes you care about
This is the main gotcha with `font-variation-settings`. If a parent sets `font-variation-settings: 'CASL' 1` for a casual style, and a child sets `font-variation-settings: 'wght' 700`, the child loses the casual style — `CASL` is reset. The solution: use the high-level properties (`font-weight: 700`) when you can, and only use `font-variation-settings` for custom axes. High-level properties and `font-variation-settings` can be combined.
Animating variable font axes

One of the most exciting features of variable fonts is that axes can be animated — but only if you register the property with @property:

CSS
/* Animate font-weight on hover */
.animated-heading {
  font-weight: 400;
  transition: font-weight 0.3s ease;
}

.animated-heading:hover {
  font-weight: 800; /* animates smoothly with variable font */
}

/* Animate a custom axis — requires @property */
@property --grad {
  syntax: '<number>';
  inherits: false;
  initial-value: 0;
}

.grade-text {
  font-variation-settings: 'GRAD' var(--grad);
  transition: --grad 0.3s ease;
}

.grade-text:hover {
  --grad: 150; /* grade axis — affects visual weight without changing spacing */
}
Finding variable fonts
  • Google Fonts: Filter by "Variable" — Inter, Roboto Flex, Source Serif, and many more

  • v-fonts.com: A showcase of variable fonts with interactive axis controls

  • axis-praxis.org: Playground for variable font axes

  • fonts.google.com/variablefonts: Google's dedicated variable font library

Next
The font shorthand — and how to use system font stacks efficiently: [font shorthand & system fonts](/css/font-shorthand).