Polices variables : le guide CSS complet
Variable Fonts: The Complete CSS Guide
Variable fonts are one of the most significant advances in web typography in decades. Instead of loading a separate file for each weight and style — Regular, Bold, Italic, SemiBold — a single variable font file contains the entire design space. You can dial in not just predefined weights but any weight along a continuous spectrum, and do the same for width, slant, optical size, and custom axes defined by the type designer.
This guide covers everything you need to use variable fonts effectively in production: what they are, how to access their axes in CSS, the performance case for using them, and which variable fonts from Google Fonts are worth reaching for.
What Are Variable Fonts?
A traditional ("static") font file contains a single master — one specific combination of weight, width, and style. If you need four weights plus their italic counterparts, you load eight files.
A variable font contains multiple masters and a set of mathematical descriptions of how to interpolate between them. The type designer defines axes — dimensions of variation — and the extremes of each. Within those extremes, any value is valid and rendered correctly.
The OpenType specification formalizes this as OpenType Font Variations, and it's supported natively in all modern browsers.
/* With static fonts — 4 files */
@font-face { font-family: 'Roboto'; src: url('roboto-regular.woff2'); font-weight: 400; }
@font-face { font-family: 'Roboto'; src: url('roboto-medium.woff2'); font-weight: 500; }
@font-face { font-family: 'Roboto'; src: url('roboto-bold.woff2'); font-weight: 700; }
@font-face { font-family: 'Roboto'; src: url('roboto-black.woff2'); font-weight: 900; }
/* With a variable font — 1 file, full range */
@font-face {
font-family: 'Roboto';
src: url('roboto-variable.woff2') format('woff2 supports variations');
font-weight: 100 900; /* Range declaration */
font-style: normal;
font-display: swap;
}
Notice the font-weight: 100 900 in the variable font declaration. This range syntax tells the browser that this single file handles any weight from 100 to 900.
You can also use the format hint woff2 supports variations to indicate it's a variable font, though browsers today support it regardless of the hint.
Registered Axes: wght, wdth, slnt, ital, opsz
The OpenType specification defines five "registered" axes — standardized axes with defined CSS mappings. These are the common dimensions of variation that type designers opted into a standard system for.
wght — Weight
The weight axis (wght) corresponds to font-weight. When a variable font includes this axis, you can specify any numeric value within its range.
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Variable.woff2') format('woff2');
font-weight: 100 900;
font-display: swap;
}
/* Use any weight value, not just multiples of 100 */
.body-text {
font-family: 'Inter', sans-serif;
font-weight: 400;
}
.slightly-bolder {
font-weight: 450; /* Only possible with a variable font */
}
.ui-label {
font-weight: 550; /* Between medium and semibold */
}
.heading {
font-weight: 720; /* Between bold and extrabold */
}
This granularity is useful in several real-world scenarios: - Matching optical weight across different font sizes (larger text looks heavier at the same numeric weight) - Creating subtle emphasis without jumping to a full bold - Dark mode adjustments where you slightly reduce weight to account for halation (light blooming on dark backgrounds)
wdth — Width
The width axis (wdth) corresponds to font-stretch. Values represent a percentage of normal width.
@font-face {
font-family: 'Roboto Flex';
src: url('/fonts/RobotoFlex-Variable.woff2') format('woff2');
font-weight: 100 900;
font-stretch: 75% 125%;
font-display: swap;
}
/* Condensed heading */
.section-header {
font-family: 'Roboto Flex', sans-serif;
font-weight: 700;
font-stretch: 80%;
}
/* Wide display text */
.hero-word {
font-stretch: 115%;
font-weight: 900;
}
/* Normal width body */
.body {
font-stretch: 100%;
}
Width variation is excellent for responsive design: you can switch to a condensed width on small screens to fit more text without reducing font-size.
slnt — Slant
The slant axis (slnt) defines how much the letterforms are slanted. It differs from italic in that it applies a mechanical slant without changing the letterform designs.
@font-face {
font-family: 'Recursive';
src: url('/fonts/Recursive-Variable.woff2') format('woff2');
font-weight: 300 1000;
font-style: oblique -15deg 0deg;
}
/* Activate slant via font-style: oblique */
.slanted {
font-family: 'Recursive', sans-serif;
font-style: oblique -10deg;
}
/* Or via font-variation-settings directly */
.slanted-direct {
font-variation-settings: 'slnt' -10;
}
ital — Italic
The italic axis (ital) is a binary axis (0 or 1) that switches between upright and italic letterforms. Unlike slnt, switching to 1 triggers the full italic design if the font has one.
.italic {
font-style: italic;
/* Browser maps font-style: italic to ital=1 automatically */
}
/* Direct control */
.forced-italic {
font-variation-settings: 'ital' 1;
}
.forced-upright {
font-variation-settings: 'ital' 0;
}
opsz — Optical Size
Optical sizing is a feature where the font subtly adjusts its design to look best at a given size. At small sizes, letterforms are opened up, spacing is increased, and strokes are less differentiated. At display sizes, letterforms can be more refined and tightly spaced.
@font-face {
font-family: 'Source Serif 4';
src: url('/fonts/SourceSerif4-Variable.woff2') format('woff2');
font-weight: 200 900;
font-style: normal;
font-optical-sizing: auto; /* Let the browser handle it */
}
/* font-optical-sizing: auto maps the optical size axis to the font-size automatically */
body {
font-optical-sizing: auto;
}
/* Manual control via font-variation-settings */
.display-heading {
font-size: 4rem;
font-variation-settings: 'opsz' 72; /* Optimized for 72pt display size */
}
.footnote {
font-size: 0.75rem;
font-variation-settings: 'opsz' 8; /* Optimized for 8pt small size */
}
The font-optical-sizing: auto property (the default) tells the browser to automatically apply appropriate optical sizing based on the rendered font-size. This is a subtle but meaningful quality-of-life improvement that many readers notice unconsciously.
CSS: font-variation-settings vs. Standard Properties
You have two ways to set variable font axes in CSS:
- Standard CSS properties —
font-weight,font-style,font-stretch,font-optical-sizing - Low-level
font-variation-settings— direct axis control
Standard Properties (Recommended)
For registered axes, always prefer the standard CSS properties. They participate in the cascade correctly, work with inheritance, and compose with transitions and animations in a predictable way.
.component {
font-weight: 600;
font-stretch: 90%;
font-style: oblique 8deg;
font-optical-sizing: auto;
}
/* Transitions work naturally */
.button {
font-weight: 500;
transition: font-weight 200ms ease;
}
.button:hover {
font-weight: 600;
}
font-variation-settings
font-variation-settings gives you direct access to any axis, including custom ones. However, it is a lowest-level override — it does not inherit partially, it does not merge with other font-variation-settings declarations, and it requires that you specify every axis you want to control in a single declaration.
/* Direct axis control */
.text {
font-variation-settings: 'wght' 450, 'wdth' 90, 'slnt' -5;
}
/* You CANNOT do this — second declaration wins entirely, wght is lost */
.bad-example {
font-variation-settings: 'wght' 600; /* Overridden completely */
font-variation-settings: 'wdth' 90; /* Only this applies */
}
/* The correct approach — combine in one declaration */
.good-example {
font-variation-settings: 'wght' 600, 'wdth' 90;
}
The inheritance problem with font-variation-settings is the main practical footgun. If a parent sets font-variation-settings: 'wght' 700, 'wdth' 90 and a child sets font-variation-settings: 'wdth' 80, the child's wght reverts to its default — not the parent's 700. To work around this, you can use CSS custom properties:
/* Workaround using CSS custom properties */
:root {
--font-wght: 400;
--font-wdth: 100;
--font-slnt: 0;
}
.text {
font-variation-settings:
'wght' var(--font-wght),
'wdth' var(--font-wdth),
'slnt' var(--font-slnt);
}
.bold-text {
--font-wght: 700; /* Only overrides weight, others inherit via the variable */
}
This pattern lets individual components override only the axes they care about while inheriting the rest.
Custom Axes and Creative Possibilities
Beyond registered axes, type designers can define their own custom axes. These are identified by four-letter uppercase tags and can control anything — the grade of the stroke, the extension of ascenders, the casual-to-formal spectrum, and more.
Recursive is an excellent example of a font with rich custom axes:
@font-face {
font-family: 'Recursive';
src: url('/fonts/Recursive-Variable.woff2') format('woff2');
font-weight: 300 1000;
font-style: oblique -15deg 0deg;
}
/* MONO axis: 0 = proportional, 1 = monospaced */
.code-snippet {
font-variation-settings:
'MONO' 1,
'CASL' 0,
'wght' 450,
'slnt' 0;
}
/* CASL axis: 0 = Linear (formal), 1 = Casual (expressive) */
.fun-callout {
font-variation-settings:
'MONO' 0,
'CASL' 1,
'wght' 700,
'slnt' -10;
}
Fraunces has a WONK axis that controls wonky letterform alternates:
.expressive-heading {
font-family: 'Fraunces', serif;
font-variation-settings:
'opsz' 72,
'SOFT' 100,
'WONK' 1,
'wght' 900;
}
Animating Variable Font Axes
One of the most powerful (and fun) capabilities of variable fonts is animating axes. CSS transitions and animations work seamlessly:
.animated-weight {
font-weight: 100;
transition: font-weight 600ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.animated-weight:hover {
font-weight: 900;
}
/* Keyframe animation */
@keyframes pulse-weight {
0%, 100% { font-weight: 300; }
50% { font-weight: 800; }
}
.pulsing-text {
animation: pulse-weight 2s ease-in-out infinite;
}
/* Animate custom axis */
@keyframes grow-casual {
from { font-variation-settings: 'CASL' 0, 'wght' 400; }
to { font-variation-settings: 'CASL' 1, 'wght' 700; }
}
.casual-reveal {
animation: grow-casual 0.4s ease-out forwards;
}
Use these effects thoughtfully — they're powerful but can become distracting. Interactive hover states and subtle loading animations are the most justified use cases.
Performance Benefits: One File, All Weights
The performance case for variable fonts is straightforward: one file replaces many.
Consider loading four weights of a sans-serif for a typical site:
Static fonts:
roboto-300.woff2 — 16 KB
roboto-400.woff2 — 17 KB
roboto-500.woff2 — 16 KB
roboto-700.woff2 — 17 KB
Total: 66 KB (4 HTTP requests)
Variable font:
roboto-flex.woff2 — 45 KB
Total: 45 KB (1 HTTP request)
The savings grow with the number of weights you need. For a project requiring six weights, static fonts might total 100+ KB across six requests. The single variable font stays ~45-55 KB.
Beyond file count, there is a meaningful request overhead reduction. Each HTTP request has latency — the connection overhead alone adds time. On a page that loads six font requests, eliminating five of them has a measurable impact on Time to First Render.
/* Efficient: one file for the entire range */
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Variable.woff2') format('woff2');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
/* Include italic if you need it */
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Variable-Italic.woff2') format('woff2');
font-weight: 100 900;
font-style: italic;
font-display: swap;
}
/* And that's it — use any combination of weight + style */
body { font-weight: 400; }
strong { font-weight: 600; }
h1 { font-weight: 750; }
.ui-label { font-weight: 500; font-style: normal; }
.quote { font-weight: 300; font-style: italic; }
The trade-off is that a variable font with many axes may be larger than a single static weight file. If your design only ever uses one weight of a font, a static file is more efficient. Variable fonts earn their keep when you use three or more weights.
Preloading
As with any font, preload your variable font file if it's used above the fold:
<link
rel="preload"
href="/fonts/Inter-Variable.woff2"
as="font"
type="font/woff2"
crossorigin
>
Best Variable Fonts on Google Fonts
Google Fonts has an excellent and growing collection of variable fonts. The Google Fonts API automatically serves the variable version when you request a font that has one.
Inter — The UI Workhorse
Inter is a variable font with a wght axis from 100 to 900. Designed specifically for computer screens, it has excellent legibility at small sizes and a clean, neutral aesthetic.
@import url('https://fonts.googleapis.com/css2?family=Inter:[email protected]&display=swap');
body {
font-family: 'Inter', sans-serif;
font-weight: 400;
}
.ui-label {
font-weight: 550;
letter-spacing: 0.01em;
}
Roboto Flex — Google's Most Parametric Font
Roboto Flex is perhaps the most feature-rich variable font on Google Fonts, with axes for weight, width, grade, optical size, and several custom axes.
@import url('https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,[email protected],100..900&display=swap');
body {
font-family: 'Roboto Flex', sans-serif;
font-optical-sizing: auto;
}
Fraunces — Expressive Display Serif
Fraunces is a variable serif with optical size, weight, and the custom SOFT and WONK axes. It's ideal for editorial headlines that need personality.
@import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap');
h1 {
font-family: 'Fraunces', serif;
font-weight: 900;
font-optical-sizing: auto;
}
Recursive — The Coding + Casual Sans
Recursive covers a remarkable design space: it can be a proportional sans-serif, a monospaced coding font, or anything in between, with a casual/formal axis on top.
@import url('https://fonts.googleapis.com/css2?family=Recursive:slnt,wght,CASL,CRSV,[email protected],300..1000,0..1,0..1,0..1&display=swap');
code {
font-family: 'Recursive', monospace;
font-variation-settings: 'MONO' 1, 'CASL' 0;
font-weight: 400;
}
Raleway — Display Sans with Elegant Thin Weights
Raleway is a display typeface with a beautiful ultra-thin weight — perfect for large headlines where geometric elegance is the goal.
@import url('https://fonts.googleapis.com/css2?family=Raleway:[email protected]&display=swap');
.hero-headline {
font-family: 'Raleway', sans-serif;
font-weight: 100;
letter-spacing: 0.15em;
text-transform: uppercase;
}
Source Code Pro / Source Serif 4 — Adobe's Variable Releases
Adobe has released variable versions of the entire Source family through Google Fonts. Source Serif 4 in particular has excellent optical sizing and covers the full weight range.
@import url('https://fonts.googleapis.com/css2?family=Source+Serif+4:ital,opsz,wght@0,8..60,200..900;1,8..60,200..900&display=swap');
.article-body {
font-family: 'Source Serif 4', serif;
font-optical-sizing: auto;
font-weight: 400;
}
.article-body h2 {
font-weight: 700;
font-optical-sizing: auto;
}
Variable fonts represent a genuine step forward in web typography: better control, better performance, and new creative possibilities that static fonts simply cannot match. The ecosystem has matured to the point where there is no good reason not to use them — especially on Google Fonts, where the variable versions are automatically served when available.
CSS Typography Deep Dive
Typography Terms
Try These Tools
Fonts Mentioned
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.
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.
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.
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.
Originally conceived as a single-weight display face in 2010, Raleway was expanded by multiple collaborators into a full family celebrated for its elegant, slightly art-deco character. Distinctive touches — like the uppercase W formed from overlapping V shapes — give it a refined personality that suits portfolio sites, fashion brands, and high-end editorial headings. A variable weight axis and Cyrillic support round out a family that punches above its weight in visual sophistication.