Web Performance

Variable Fonts cho Hiệu Suất: Một File Quản Lý Tất Cả

Updated Tháng 2 24, 2026
Variable fonts có thể thay thế 6+ file font tĩnh bằng một file. Hệ quả hiệu suất — kích thước file, HTTP requests và benchmark thực tế.

Variable Fonts for Performance: One File to Rule Them All

Variable fonts represent the most significant advance in web typography technology since the introduction of WOFF2 compression. Instead of loading separate font files for each weight, width, or style variant, a variable font encodes the entire design space into a single file. Bold, light, condensed, expanded — one network request.

The performance story is compelling but nuanced. Variable fonts aren't always faster than their static counterparts. The performance gains depend heavily on how many weights you use, how well you subset, and what the font's design space looks like. This guide cuts through the hype with concrete numbers and practical guidance.


Static Fonts vs Variable Fonts: File Size Comparison

Let's establish the baseline with real numbers. Inter is an excellent benchmark because it's one of the most widely used typefaces on the web and is available in both well-optimized static versions and a high-quality variable font.

Inter: Static vs Variable

Approach Files Total Transfer Size
Static: Regular only 1 WOFF2 ~65KB
Static: Regular + Bold 2 WOFF2 ~130KB
Static: Regular + Medium + SemiBold + Bold 4 WOFF2 ~260KB
Static: Regular + Medium + SemiBold + Bold + ExtraBold + Black 6 WOFF2 ~390KB
Variable (wght 100–900), full Latin 1 WOFF2 ~330KB
Variable (wght 100–900), subsetted Latin 1 WOFF2 ~75KB

The break-even point is approximately 4–5 static weights. If you use fewer weights, static files are smaller. If you use 5 or more, a subsetted variable font is smaller.

Where Variable Fonts Win

For design-heavy sites or design systems that use many weight variations — say, 7 different font-weight values across UI components — variable fonts are clearly smaller. A single 75KB variable font file versus seven static files totaling 450KB is a significant win.

Variable fonts also excel for responsive typography that uses different weights at different breakpoints, or for animations that smoothly transition between weights using CSS transitions on font-weight.

Where Static Fonts Win

For a typical marketing site or blog using only Regular (400) and Bold (700), two static WOFF2 files (~130KB) are substantially smaller than a variable font (~330KB unsubsetted, ~75KB subsetted Latin). The static approach wins on raw transfer size for lightweight use cases.


Reducing HTTP Requests: The Real Win

File size is only half the story. The other half is HTTP requests.

With HTTP/2 multiplexing, multiple small requests over a single connection are significantly less expensive than in the HTTP/1.1 era. But each request still has overhead: a minimum round-trip time for the request-response cycle, header compression costs, and browser connection management. Reducing requests from four to one is genuinely beneficial even with HTTP/2.

The Request Waterfall

When loading four static font weights, the browser initiates four separate requests. Even with HTTP/2, these requests may be partially serialized if the browser discovers them in sequence through CSS parsing. Each request competes for connection bandwidth during the critical loading window.

A variable font is one request. Once discovered, there's a single download that provides the entire weight range. No cascade, no serial discovery, no competing requests.

Cache Efficiency

Variable fonts also have better cache characteristics than multiple static files. When your font needs change — adding a new weight to a design component, for example — with static fonts you add a new file and a new cache entry. With a variable font, the existing cached file already covers the new weight. The cache hit rate for the font asset is higher because you're not fragmenting it across multiple files.

For returning users, cache efficiency matters as much as first-visit transfer size. A variable font that's already in the browser cache delivers every weight at zero cost; static fonts require individual cache entries for each weight.


When Variable Fonts Are Smaller (and When They're Not)

Understanding the internal structure of variable fonts explains when they outperform static alternatives.

How Variable Fonts Store Design Variations

A variable font stores glyph outlines as a base design plus delta values — mathematical descriptions of how each point moves as you traverse the variation axes. For a weight axis, each glyph outline has a base position (the Regular weight) and delta offsets describing how each point moves to create Light, Bold, and everything in between.

This encoding is extremely efficient when the variations are smooth and mathematically describable. A simple weight axis that just changes stroke thickness compresses very well — the deltas are small and regular. A complex typeface with dramatic glyph substitutions across the weight range stores more data per glyph.

When Variable Fonts Are Larger Than Expected

Some typefaces have a large file size in variable format because their design space is complex:

  • Multiple axes: A font with weight, width, optical size, and italic axes stores delta data for all dimensions simultaneously. More axes means more delta data.
  • Glyph substitutions: If heavier weights substitute entirely different glyph shapes (not just scaled/thickened versions), the font must store both shapes as separate glyphs.
  • Large glyph sets: Variable fonts with CJK characters or extensive multilingual coverage carry all that data plus the delta variations.

Noto Sans Variable, for example, is substantially larger than Inter Variable because it covers thousands of CJK characters with full weight variation. Subsetting becomes even more critical for such fonts.

The Subsetted Variable Font Sweet Spot

A subsetted variable font — Latin-only, limited OpenType features — occupies the performance sweet spot:

pyftsubset "Inter[wght].ttf" \
  --output-file="inter-variable-latin.woff2" \
  --flavor=woff2 \
  --layout-features="kern,liga,calt" \
  --unicodes="U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122" \
  --no-hinting

This produces an Inter variable font file of approximately 75KB — smaller than four static weight files, with full weight range coverage. If you use more than three weights and serve a Latin-script audience, this is almost certainly the right choice.


Subsetting Variable Fonts

Subsetting variable fonts requires additional consideration compared to static fonts, because the delta data for removed glyphs must also be removed.

Using pyftsubset

pyftsubset handles variable fonts correctly. The same --unicodes and --text-file flags work as with static fonts:

# Subset to Basic Latin + common punctuation and symbols
pyftsubset "Inter[wght].ttf" \
  --output-file="inter-variable-subset.woff2" \
  --flavor=woff2 \
  --layout-features="kern,liga,calt,rlig" \
  --unicodes="U+0020-007E,U+00A0-00FF,U+0131,U+0152-0153,U+2013,U+2014,U+2018,U+2019,U+201C,U+201D,U+20AC,U+2122" \
  --no-hinting

The --no-hinting flag is especially valuable for variable fonts. Variable font hinting data (HVAR, VVAR, MVAR tables) can be large and is largely irrelevant for modern high-DPI displays. Removing it reduces file size with no perceptible visual quality impact at typical body text sizes.

Axis Subsetting

You can also limit which portions of the variation axes are retained. If you only use font-weight values between 400 and 700, you can subset the axes:

pyftsubset "Inter[wght].ttf" \
  --output-file="inter-400-700.woff2" \
  --flavor=woff2 \
  --unicodes="U+0000-00FF" \
  --axis-limits="wght=400:700"

Axis subsetting reduces the delta data for each glyph proportionally to the fraction of the design space retained. Cutting the weight range in half produces files roughly 30–40% smaller than the full range.

CSS for Variable Fonts

Variable font CSS differs from static font CSS in how you declare the weight range:

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-variable-latin.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* Use any weight in the range */
body {
  font-family: 'Inter', sans-serif;
  font-weight: 400;
}

h1 {
  font-family: 'Inter', sans-serif;
  font-weight: 700;
}

.caption {
  font-family: 'Inter', sans-serif;
  font-weight: 300; /* Works — within the 100-900 range */
}

The font-weight: 100 900 declaration tells the browser this single file covers the entire weight range. Any font-weight value between 100 and 900 is rendered from this file without additional network requests.


Real-World Performance Benchmarks

Testing variable fonts against static alternatives in real production scenarios reveals the nuances that synthetic benchmarks miss.

Benchmark Setup

A representative test uses a portfolio/agency-style page with: - H1, H2, H3 headings at different weights - Body text at Regular (400) - Navigation and UI at Medium (500) - Call-to-action elements at SemiBold (600) - Footer at Light (300)

This uses 5 different font weights — a realistic design system use case.

Static approach: 5 WOFF2 files for Inter (Light, Regular, Medium, SemiBold, Bold) - Total transfer: ~325KB - HTTP requests: 5 (with HTTP/2) - LCP (simulated 3G): ~2.4 seconds

Variable font approach: 1 WOFF2 file for Inter Variable (full range) - Total transfer: ~330KB (unsubsetted) - HTTP requests: 1 - LCP (simulated 3G): ~2.1 seconds

Subsetted variable font approach: 1 WOFF2 file, Latin-only - Total transfer: ~75KB - HTTP requests: 1 - LCP (simulated 3G): ~0.8 seconds

The unsubsetted variable font saves one request but has nearly identical transfer size to 5 static files. Subsetting the variable font produces a dramatic size reduction that static fonts can't match at comparable weight coverage.

First Visit vs Return Visit

On return visits, all three approaches benefit from browser caching. The static approach has 5 cache entries that can independently expire or be evicted; the variable font has 1 cache entry. In practice, this means the variable font approach has higher cache hit rates for users with memory-constrained caches (common on mobile).

On Android devices with limited browser cache sizes, a single 75KB cache entry is far less likely to be evicted than five entries totaling the same size. Variable fonts maintain their performance advantage over time on mobile, not just on first visit.

Recommendation by Use Case

Use Case Recommendation
1–2 font weights Static WOFF2, subsetted
3–4 font weights Either; measure both
5+ font weights Subsetted variable font
Weight animations/transitions Variable font only
Responsive type with many weights Subsetted variable font
Icon font Static WOFF2

Variable fonts are the right choice for most design systems and applications where typographic richness matters. They're not universally smaller than static fonts, but combined with subsetting and proper caching, they deliver the best combination of file size, request count, and flexibility for weight-rich designs.


Browser Support and Fallback Strategy

Variable font support is now universal among modern browsers. Caniuse.com shows greater than 96% global support for variable fonts, with Safari 11+, Chrome 66+, Firefox 62+, and Edge 17+ all supporting them fully.

For the small percentage of users on older browsers, you can provide a static font fallback using a CSS feature query:

/* Modern browsers: variable font */
@supports (font-variation-settings: normal) {
  @font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-variable.woff2') format('woff2-variations');
    font-weight: 100 900;
    font-display: swap;
  }
}

/* Fallback: static fonts for older browsers */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-bold.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}

Browsers that support @supports and variable fonts use the variable font. Browsers that don't (IE 11, very old Safari) fall back to static fonts for the two most critical weights.

In practice, given the browser support landscape in 2024 and beyond, this fallback strategy is optional for most consumer-facing applications. Enterprise applications with known requirements for IE 11 support are the primary use case for maintaining static font fallbacks.


Variable Font Axes Beyond Weight

The performance discussion so far has focused on the weight axis (wght) because it's the most commonly used. Variable fonts can expose many other axes, some of which have performance implications of their own.

Registered Axes

The OpenType specification defines several "registered" axes with standardized four-character tags:

  • wght — Weight (100 to 900 for most typefaces)
  • wdth — Width (condensed to expanded)
  • ital — Italic (0 = upright, 1 = italic)
  • slnt — Slant (degree of slant in degrees)
  • opsz — Optical size (optimized for specific body text sizes)

The optical size axis (opsz) is particularly interesting for performance-conscious typography. A font with an opsz axis stores separate optimized designs for small text (8pt) and large text (72pt) within a single file. Without a variable font, you'd need separate optical size families — potentially two font files with separate weights — to achieve this typographic quality.

CSS for Multiple Axes

@font-face {
  font-family: 'Source Serif 4';
  src: url('/fonts/source-serif-4-variable.woff2') format('woff2-variations');
  font-weight: 200 900;
  font-style: oblique 0deg 15deg;
  font-display: swap;
}

/* Use weight and optical size together */
h1 {
  font-family: 'Source Serif 4', Georgia, serif;
  font-weight: 700;
  font-variation-settings: 'opsz' 48;  /* Optimized for large display */
}

body {
  font-family: 'Source Serif 4', Georgia, serif;
  font-weight: 400;
  font-variation-settings: 'opsz' 14;  /* Optimized for body text */
}

Using font-variation-settings to set optical size gives you display-quality typography at large sizes and high legibility at small sizes — typographic features that previously required multiple font files, now delivered in one.


Integration with Design Tokens

Variable fonts integrate naturally with CSS custom properties, enabling design token systems that provide precise typographic control with smooth interpolation:

:root {
  --font-weight-body: 400;
  --font-weight-semibold: 550;
  --font-weight-bold: 700;
  --font-weight-display: 800;
}

/* Fluid weight that responds to viewport */
@media (min-width: 768px) {
  :root {
    --font-weight-display: 900;
  }
}

h1 {
  font-weight: var(--font-weight-display);
}

/* Dark mode can use different weights for the same visual weight */
@media (prefers-color-scheme: dark) {
  :root {
    --font-weight-body: 350;      /* Lighter weight reads better on dark backgrounds */
    --font-weight-bold: 650;      /* Slightly lighter bold to reduce halation */
  }
}

This pattern is impossible with static fonts — changing the CSS custom property would require a different font file to be loaded, with all the associated network overhead. With variable fonts, adjusting font weight in response to viewport size, color scheme preferences, or user settings is purely a CSS operation with no additional network cost.

Typography Terms

Try These Tools

Fonts Mentioned

Roboto Sans Serif #1

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.

The quick brown fox jumps over the lazy dog
Inter Sans Serif #5

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.

The quick brown fox jumps over the lazy dog
Montserrat Sans Serif #6

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.

The quick brown fox jumps over the lazy dog

Related Articles