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 |
|
| 100–900 |
Width |
|
| 75%–125% |
Italic |
|
| 0 or 1 |
Slant |
|
| -15 to 0 |
Optical size |
|
| pt size |
Custom axes |
|
| Designer-defined |
Loading a variable font
/* 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
/* 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:
/* 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 */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:
/* 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