How to Use Variable Fonts in CSS
Embed This Widget
Add the script tag and a data attribute to embed this widget.
Embed via iframe for maximum compatibility.
<iframe src="https://fontfyi.com/iframe/entity//" width="420" height="400" frameborder="0" style="border:0;border-radius:10px;max-width:100%" loading="lazy"></iframe>
Paste this URL in WordPress, Medium, or any oEmbed-compatible platform.
https://fontfyi.com/entity//
Add a dynamic SVG badge to your README or docs.
[](https://fontfyi.com/entity//)
Use the native HTML custom element.
How to Use Variable Fonts in CSS
Variable fonts are one of the most significant advances in web typography in decades. A single variable font file can contain an infinite number of design variations — every weight from Thin to Black, every width from Condensed to Extended, every optical size from Caption to Display — accessible through continuous numeric axes rather than discrete files. Where a complete Inter family once required eight or more separate WOFF2 files to cover the full weight range, Inter's variable font version packages the same range into a single file that's smaller than two static files combined.
The CSS interface to variable fonts is straightforward but has several layers worth understanding thoroughly. This guide covers the complete workflow: loading the font, setting axes in CSS, using standard properties, animating axes, and handling the small remaining browser support gaps.
Loading a Variable Font with @font-face
Loading a variable font uses the same @font-face at-rule as static fonts, with two key differences: the format string includes "woff2" (variable fonts use the standard WOFF2 container), and the font-weight (or other axis) descriptor accepts a range rather than a single value.
@font-face {
font-family: "Inter";
src: url("/fonts/inter-variable.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
The font-weight: 100 900 range declaration tells the browser that this font file can produce any weight between 100 and 900. When your CSS later specifies font-weight: 450, the browser doesn't need to find a separate file for that value — it interpolates within the single variable font. This is the fundamental capability that makes variable fonts so powerful.
For fonts that include both regular and italic variations in a single file (not all do), you can declare both axes together:
@font-face {
font-family: "Inter";
src: url("/fonts/inter-variable.woff2") format("woff2");
font-weight: 100 900;
font-style: normal oblique 0deg 10deg;
font-display: swap;
}
Some variable fonts separate regular and italic into two files (sometimes called "Roman" and "Italic" variable fonts). In that case, use two @font-face blocks with matching font-family names:
@font-face {
font-family: "Roboto";
src: url("/fonts/roboto-variable.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Roboto";
src: url("/fonts/roboto-variable-italic.woff2") format("woff2");
font-weight: 100 900;
font-style: italic;
font-display: swap;
}
Roboto follows this two-file pattern in its variable font release. Montserrat packages both upright and italic variations in a single file. Check the font's documentation or inspect the file with a tool like Wakamai Fondue to determine which structure a given font uses.
Setting Axes with font-variation-settings
The font-variation-settings property is the low-level interface to all variable font axes. It accepts a comma-separated list of axis tag–value pairs, where each axis tag is a four-character string (either lowercase for standard axes or uppercase for custom/registered axes):
.text-custom {
font-variation-settings: "wght" 650, "wdth" 85, "opsz" 14;
}
This sets the weight axis (wght) to 650, the width axis (wdth) to 85% of normal, and the optical size axis (opsz) to 14 — appropriate for 14px body text. The exact value ranges available depend on the specific font: a font might support wght from 100 to 900, or it might only go from 300 to 700. Values outside the supported range are clamped to the nearest endpoint.
font-variation-settings has a critical caveat: it overwrites all axes with a single property, not individual ones. If you set font-variation-settings: "wght" 700 on a parent element and then set font-variation-settings: "opsz" 14 on a child, the child inherits only "opsz" 14 — the weight axis resets to its default. This non-additive behavior is a frequent source of bugs.
The solution is to either set all axes in a single declaration or, preferably, use high-level CSS properties (covered in the next section) wherever they exist and reserve font-variation-settings for axes that don't have standard CSS equivalents.
Standard Axis Tags
Five axis tags are registered in the OpenType specification and have predefined value ranges:
| Tag | Axis | CSS Property Equivalent |
|---|---|---|
wght |
Weight | font-weight |
wdth |
Width | font-stretch |
ital |
Italic | font-style: italic |
slnt |
Slant | font-style: oblique |
opsz |
Optical Size | font-optical-sizing |
Custom axes use uppercase four-character tags and are font-specific. Oswald's grade axis might be tagged GRAD, while another font might use SOFT for a softness parameter. These custom axes have no CSS property equivalent — font-variation-settings is the only way to set them.
Using Standard CSS Properties (font-weight, font-stretch)
For the five standard axes, CSS provides dedicated properties that are both more readable and more composable than font-variation-settings. These properties map directly to the underlying axis values:
/* Instead of: font-variation-settings: "wght" 650; */
.text-semibold {
font-weight: 650;
}
/* Instead of: font-variation-settings: "wdth" 85; */
.text-condensed {
font-stretch: 85%;
}
/* Optical sizing */
.body-text {
font-optical-sizing: auto; /* browser infers from font-size */
}
.display-heading {
font-size: 4rem;
font-optical-sizing: auto;
}
font-weight with a variable font accepts any numeric value between 1 and 1000 (constrained by the font's wght axis range), not just the traditional multiples of 100. This means you can use precise intermediate weights like 450 or 620 when a design calls for it. In practice, most designs use the standard values (400, 500, 600, 700), but the granularity is available when you need it.
font-optical-sizing: auto deserves special attention. Optical sizing refers to the design adjustments that make type more legible at different physical sizes — at small sizes, letterforms are opened up and stroke contrast is reduced; at large display sizes, more contrast and refinement are appropriate. When set to auto, the browser automatically maps the font-size value to the font's opsz axis, selecting the visually optimal variation without any manual intervention. For Oswald and other fonts with an opsz axis, font-optical-sizing: auto is a simple way to improve quality across your entire type system.
Inheritance and Composition
One of the main reasons to prefer standard CSS properties over font-variation-settings is how they compose with inheritance:
:root {
font-weight: 400;
font-optical-sizing: auto;
}
h1 {
font-weight: 700; /* Changes weight without affecting opsz */
}
.caption {
font-weight: 300; /* Changes weight without affecting opsz */
}
Each property can be set and overridden independently on descendant elements. With font-variation-settings, you'd have to repeat all the axes on every override to avoid resetting others — a maintenance burden that increases with the number of axes you use.
Animating Variable Font Axes
Variable font axes are animatable CSS properties, which opens up typographic effects that would have been impossible with static fonts. Weight transitions on hover, smooth optical size adjustments on scroll, kinetic type that responds to user interaction — all of these are achievable with CSS transitions and animations.
Basic weight transition on hover:
.logo {
font-weight: 300;
transition: font-weight 200ms ease-in-out;
}
.logo:hover {
font-weight: 700;
}
This smoothly interpolates the weight axis from 300 to 700 over 200 milliseconds, creating an effect that cannot be replicated with static font files where only discrete jumps between weights are possible.
A more sophisticated animation using @keyframes:
@keyframes pulse-weight {
0%, 100% {
font-weight: 100;
letter-spacing: 0.1em;
}
50% {
font-weight: 900;
letter-spacing: -0.02em;
}
}
.animated-title {
animation: pulse-weight 3s ease-in-out infinite;
}
This combines weight animation with letter-spacing to create a breathing effect — as the type gets heavier, spacing tightens to compensate for the increased visual mass. This kind of compensation is common in hand-crafted typography but difficult to automate in CSS without variable fonts.
Scroll-Driven Weight Variation
With CSS scroll-driven animations (supported in Chromium 115+), you can tie weight to scroll position:
@keyframes weight-on-scroll {
from { font-weight: 100; }
to { font-weight: 900; }
}
.scroll-headline {
animation: weight-on-scroll linear;
animation-timeline: scroll();
animation-range: 0vh 50vh;
}
This makes the headline grow bolder as the user scrolls down the first half of the page. While this is more of a design effect than a usability feature, it illustrates the range of typographic expression that variable fonts enable.
Performance note: Animating font-weight and font-variation-settings triggers text relayout, which can cause layout shift and jank on complex pages. Test animated variable font usage on mid-range devices, not just high-end development hardware. Prefer animating only display text (large headings, hero sections) rather than body text, where reflow during animation would be more visually disruptive.
Browser Support and Fallbacks
Variable font support in browsers is now essentially universal. All modern browsers — Chrome 66+, Firefox 62+, Safari 11+, Edge 17+ — support variable fonts fully. The global coverage is over 97% as of 2024. The only browsers that lack support are Internet Explorer 11 (which doesn't support WOFF2 either) and very old mobile browsers.
For the small percentage of users on non-supporting browsers, the fallback strategy is straightforward. Use the CSS @supports rule to provide static font alternatives:
/* Fallback for browsers without variable font support */
@font-face {
font-family: "Inter";
src: url("/fonts/inter-regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter";
src: url("/fonts/inter-bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* Variable font for supporting browsers */
@supports (font-variation-settings: normal) {
@font-face {
font-family: "Inter";
src: url("/fonts/inter-variable.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
}
The @supports (font-variation-settings: normal) block is only parsed by browsers that understand variable fonts. Browsers that don't support this feature fall back to the static @font-face declarations defined before it. The font-family name is the same in both cases, so the rest of your CSS doesn't need to change.
In practice, given the near-universal support, many teams omit the @supports fallback entirely for internal tools and modern web applications. For public-facing sites with diverse audience demographics, including the fallback is good practice without significant maintenance cost.
Practical Example: Complete Variable Font Setup
Here is a production-ready variable font configuration using Inter, combining all the techniques covered in this guide:
/* Variable font with @supports fallback */
@font-face {
font-family: "Inter";
src: url("/fonts/inter-regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter";
src: url("/fonts/inter-bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@supports (font-variation-settings: normal) {
@font-face {
font-family: "Inter";
src: url("/fonts/inter-variable.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
}
/* Base typography */
body {
font-family: "Inter", system-ui, -apple-system, sans-serif;
font-weight: 400;
font-optical-sizing: auto;
}
/* Headings use continuous weight scale */
h1 { font-weight: 800; }
h2 { font-weight: 700; }
h3 { font-weight: 600; }
h4 { font-weight: 550; }
/* Fine-grained label weights */
.label-primary { font-weight: 500; }
.label-secondary { font-weight: 450; }
.caption { font-weight: 400; font-size: 0.75rem; }
/* UI state — weight communicates emphasis */
.button { font-weight: 500; }
.button-primary { font-weight: 600; }
.button-cta { font-weight: 700; }
/* Smooth weight transition for interactive elements */
.nav-link {
font-weight: 400;
transition: font-weight 150ms ease;
}
.nav-link:hover,
.nav-link[aria-current="page"] {
font-weight: 600;
}
This setup uses the fallback pattern for broad compatibility, maps optical sizing to auto for quality across all sizes, and demonstrates how variable fonts enable a continuous weight scale for headings and fine-grained UI states. The weight transition on navigation links creates a subtle, polished hover effect.
It is worth noting how dramatically variable fonts have changed the economics of rich typography on the web. Before variable fonts, providing five weight variants for a typeface meant five network requests, five files to cache, and five @font-face declarations to maintain. A variable font collapses all of that into one file, one declaration, and one cache entry — while actually expanding typographic capability to include weights that never existed as discrete static variants. Intermediate weights like 450 or 550 are now accessible without any additional cost, enabling more nuanced hierarchical systems than were practical before.
For the complete reference on variable font capabilities — including width, slant, grade, and custom axes — the variable fonts complete guide covers every standard and registered axis in depth. For performance measurement and file size comparisons between variable and static font approaches, see variable fonts performance. The best variable fonts article provides curated recommendations for high-quality variable fonts available in open-source and commercial releases.
Thuật ngữ typography
Thử các công cụ này
Font được đề cập
Được Christian Robertson thiết kế cho hệ sinh thái Material Design của Google, đây là phông chữ sans-serif neo-grotesque được sử dụng rộng rãi nhất trên web và Android. Thiết kế mang tính kép cân bằng giữa độ chính xác cơ học và nhịp đọc tự nhiên, phù hợp cho cả nhãn giao diện lẫn văn bản dài. Phông chữ biến thể hỗ trợ các trục chiều rộng và trọng lượng cùng các chữ viết Cyrillic, Hy Lạp và Latin mở rộng.
Rasmus Andersson đã dành nhiều năm tinh chỉnh phông chữ neo-grotesque này đặc biệt cho màn hình máy tính, tối ưu hóa khoảng cách chữ, chiều cao x và độ tương phản nét vẽ để đạt khả năng đọc cao ở kích thước nhỏ trên màn hình kỹ thuật số. Trục kích thước quang học (opsz) cho phép phông chữ tự động điều chỉnh thiết kế cho chú thích và tiêu đề, trong khi trục trọng lượng bao gồm toàn bộ phạm vi từ thin đến black. Nó đã trở thành lựa chọn tiêu chuẩn thực tế cho bảng điều khiển, trang tài liệu và công cụ dành cho nhà phát triển trên toàn thế giới.
Được truyền cảm hứng từ biển hiệu hình học và các cửa hàng trong khu phố Montserrat ở Buenos Aires, Julieta Ulanovsky đã tạo ra phông chữ này để nắm bắt tinh thần của nghệ thuật chữ viết đô thị đầu thế kỷ 20. Các hình tròn gọn gàng và tỷ lệ hình học mạnh mẽ tạo ra sự hiện diện quyết đoán, lý tưởng cho tiêu đề, thương hiệu và trang đích. Trục trọng lượng biến thể bao gồm phạm vi rộng, với các chữ viết Cyrillic và tiếng Việt được bao gồm.
Vernon Adams đã tái tưởng tượng thể loại grotesque condensed cổ điển cho web, lấy cảm hứng từ các kiểu chữ Gothic Mỹ đầu thế kỷ và chữ in báo condensed. Tỷ lệ cao và hẹp của nó thu hút sự chú ý trong tiêu đề, áp phích và bối cảnh hiển thị nơi nhịp điệu dọc chặt chẽ. Trục trọng lượng biến đổi và hỗ trợ chữ Kirin mở rộng tính hữu dụng của phông vượt ra ngoài các ứng dụng tiếng Anh.