Variable Fonts: One File, Infinite Typographic Control
A variable font is a single font file that contains an entire type family — all weights, widths, optical sizes, and other axes in one file. Instead of loading Inter-Regular.woff2, Inter-Bold.woff2, and Inter-SemiBold.woff2 separately, you load one Inter[wght].woff2 and access any weight from 100 to 900 continuously.
1. Why Variable Fonts Matter
Traditional fonts (one file per style):
Inter-Thin.woff2 (30KB)
Inter-Regular.woff2 (32KB)
Inter-Medium.woff2 (32KB)
Inter-SemiBold.woff2 (33KB)
Inter-Bold.woff2 (34KB)
Inter-ExtraBold.woff2 (34KB)
─────────────────────────────
Total: ~195KB (6 files, 6 requests)
Variable font:
Inter[wght].woff2 (95KB)
─────────────────────────────
Total: ~95KB (1 file, 1 request)
+ Access to EVERY weight continuously (100–900)
2. The Variation Axes
Variable fonts expose axes that you control with CSS font-variation-settings:
Registered axes (standardized — have dedicated CSS properties):
wght → font-weight: 100 to 900
wdth → font-stretch: 75% to 125%
ital → font-style: 0 (normal) or 1 (italic)
slnt → font-style: oblique -15deg to 15deg
opsz → font-optical-sizing: auto
Custom axes (font-specific — 4 uppercase letters):
GRAD → Grade (slight weight change without layout shift)
MONO → Monospace (variable font that morphs between proportional and mono)
CASL → Casualness (Recursive font — straight to cursive)
CRSV → Cursive (Recursive font)
3. Loading Variable Fonts
Google Fonts Variable Fonts
<!-- The modern way: include variable font from Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Inter: variable weight 100-900 -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap" rel="stylesheet">
<!-- Multiple variable fonts -->
<link href="https://fonts.googleapis.com/css2?
family=Inter:wght@100..900&
family=Playfair+Display:ital,wght@0,400..900;1,400..900&
display=swap" rel="stylesheet">
Note the syntax difference:
Traditional: family=Inter:wght@400;700 (two separate weights)
Variable: family=Inter:wght@100..900 (continuous range with ..)
Self-Hosted Variable Fonts
@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; /* Range this font covers */
font-style: normal;
font-display: swap;
}
/* Italic axis (if separate italic variable font file) */
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Italic-Variable.woff2') format('woff2 supports variations');
font-weight: 100 900;
font-style: italic;
font-display: swap;
}
4. Using Variable Fonts with Standard CSS Properties
The simplest way — use the standard CSS properties. The browser maps them to variation axes:
/* font-weight maps to the wght axis */
.c-text-thin { font-weight: 100; }
.c-text-normal { font-weight: 400; }
.c-text-medium { font-weight: 500; } /* ← Only possible with variable fonts */
.c-text-semibold { font-weight: 600; } /* ← Same */
.c-text-bold { font-weight: 700; }
.c-text-black { font-weight: 900; }
/* Any value from 1 to 1000 — not just 100-step increments */
.c-heading-precise { font-weight: 650; } /* ← Fine-tuned weight */
/* font-stretch maps to the wdth axis */
.c-text-condensed { font-stretch: 75%; }
.c-text-expanded { font-stretch: 125%; }
/* Optical sizing */
.c-small-caption { font-size: 11px; font-optical-sizing: auto; }
/* Browser automatically adjusts letterforms for legibility at small sizes */
5. The font-variation-settings Property
For custom axes or fine control:
/* font-variation-settings: 'axis' value, 'axis' value */
.c-text-custom {
font-family: 'Inter', sans-serif;
font-variation-settings: 'wght' 450, 'wdth' 90;
}
/* Animating font weight with variable fonts */
.c-heading {
font-weight: 300;
transition: font-weight 0.3s ease;
}
.c-heading:hover {
font-weight: 800; /* Only works with variable fonts — instant snap otherwise */
}
/* Or animate with font-variation-settings (needed for custom axes) */
.c-logo {
font-variation-settings: 'wght' 300;
transition: font-variation-settings 0.4s ease;
}
.c-logo:hover {
font-variation-settings: 'wght' 800;
}
6. Popular Variable Fonts for Web Design
| Font | Axes | Best For | Google Fonts |
|---|---|---|---|
| Inter | wght 100–900 | UI, body text, SaaS | ✅ family=Inter:wght@100..900 |
| Roboto Flex | wght, wdth, opsz, GRAD + more | Android-style UI | ✅ |
| Playfair Display | wght 400–900, ital | Premium headings, editorial | ✅ family=Playfair+Display:ital,wght@0,400..900;1,400..900 |
| Fraunces | wght, opsz, WONK, SOFT | Display, branding | ✅ |
| Recursive | wght, MONO, CASL, CRSV, slnt | Unique — morphs serif↔sans | ✅ |
| DM Sans | wght 100–900, opsz | Clean, modern sans-serif | ✅ |
| Manrope | wght 200–800 | Geometric, premium feel | ✅ |
7. Design Token Integration
/* Token system using variable font weights */
:root {
/* Font weight tokens — meaningful semantic names */
--font-weight-light: 300;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--font-weight-black: 900;
/* These only produce intermediate values with variable fonts */
/* With static fonts, browser rounds to nearest available weight */
}
h1 { font-weight: var(--font-weight-black); }
h2 { font-weight: var(--font-weight-bold); }
p { font-weight: var(--font-weight-normal); }
.c-label { font-weight: var(--font-weight-medium); }
8. Browser Support and Fallbacks
/* Feature detection — all major browsers support variable fonts */
@supports (font-variation-settings: 'wght' 400) {
/* Variable font-specific CSS */
.c-heading { font-weight: 650; }
}
/* Fallback: browsers without variable support get the closest static weight */
h1 { font-weight: bold; } /* All browsers: 700 */
@supports (font-variation-settings: 'wght' 800) {
h1 { font-weight: 800; } /* Variable font: 800 */
}
[!NOTE] Variable font browser support is 98%+. You do not need fallbacks for modern projects. Only legacy IE11 (dead) would need them.