Variable Fonts - The Future of Web Typography

What You'll Learn

  • Understand what variable fonts are and how they differ from static fonts
  • Use the five standard axes: weight, width, slant, italic, and optical sizing
  • Control weight with font-weight for any value from 100-900
  • Access custom axes with font-variation-settings for unique effects
  • Load variable fonts from Google Fonts or self-host with @font-face
  • Create smooth animations between weights impossible with static fonts
  • Build responsive typography that adapts weight to viewport size
  • Understand performance benefits: one file vs multiple static weights
  • Explore popular variable fonts and where to find them

Introduction to Variable Fonts

Variable fonts are a powerful evolution in web typography. Instead of loading separate font files for each weight (regular, medium, bold, etc.), a variable font contains multiple variations along adjustable design axes in a single file. This provides infinite flexibility while often reducing total file size.

Introduced in OpenType 1.8 (2016) and supported in all modern browsers since 2017-2018, variable fonts solve the performance problem of loading multiple font weights while giving designers unprecedented control over typography. One variable font file can replace 5-10 static font files.

Traditional vs Variable Fonts

To understand the revolution variable fonts bring, compare them to traditional static fonts:

Traditional Static Fonts: • Separate file for each weight/style • Roboto Regular (28KB) + Medium (29KB) + Bold (31KB) = 88KB, 3 files • Only specific weights available (400, 500, 700) • Can't use intermediate values (no 650) • Can't animate between weights • Multiple HTTP requests slow loading Variable Fonts: • Single file with full weight range • Inter Variable (120KB) = 100-900, 1 file • Any weight value (437, 550, 680, etc.) • Smooth animations between weights • Single HTTP request, faster loading • Often smaller than loading 3-4 static weights

Standard Axes

Variable fonts adjust along "axes" - design parameters that can vary. Five standard axes have dedicated CSS properties, while custom axes use font-variation-settings.

/* Five standard (registered) axes */ font-weight: 100 to 900; /* wght - thickness */ font-stretch: 50% to 200%; /* wdth - character width */ font-style: oblique -20deg to 20deg; /* slnt - slant angle */ font-style: italic; /* ital - italic on/off (0 or 1) */ font-optical-sizing: auto; /* opsz - optical size */ /* Examples */ h1 { font-weight: 650; /* Any value 100-900! */ font-stretch: 115%; /* Slightly expanded */ font-style: oblique 12deg; /* 12-degree slant */ } /* Optical sizing (automatic in many browsers) */ .large-heading { font-size: 4rem; font-optical-sizing: auto; /* Optimizes for size */ }

Weight Axis (wght) - Infinite Flexibility

The weight axis is the most common and useful. Unlike static fonts limited to specific weights (100, 200, 300, etc.), variable fonts support ANY value in their range.

/* Static fonts: limited to specific weights */ font-weight: 400; /* Regular */ font-weight: 700; /* Bold */ font-weight: 650; /* NOT AVAILABLE - browser rounds to 700 */ /* Variable fonts: any value in range */ font-weight: 400; /* Regular */ font-weight: 700; /* Bold */ font-weight: 650; /* ✓ Exactly 650! */ font-weight: 437; /* ✓ Works! */ font-weight: 826; /* ✓ Fine-tune as needed */ /* Practical use cases */ h1 { font-weight: 750; /* Heavier than bold but not too heavy */ } .subtitle { font-weight: 450; /* Slightly heavier than regular */ } .footnote { font-weight: 350; /* Lighter than regular */ }

Custom Axes with font-variation-settings

Some variable fonts include custom axes beyond the five standard ones. These require font-variation-settings and use four-letter tags (usually uppercase for custom axes).

/* font-variation-settings syntax */ font-variation-settings: 'AXIS' value, 'AXIS' value; /* Recursive font with CASL (casual) and MONO (monospace) axes */ .linear-mono { font-family: 'Recursive', monospace; font-variation-settings: 'CASL' 0, 'MONO' 1; /* Linear style, monospace spacing */ } .casual-proportional { font-family: 'Recursive', monospace; font-variation-settings: 'CASL' 1, 'MONO' 0; /* Casual style, proportional spacing */ } /* Roboto Flex with GRAD (gradient/stroke) */ .text-gradient { font-family: 'Roboto Flex', sans-serif; font-variation-settings: 'GRAD' 150; /* Adjusts stroke thickness */ } /* Combining standard and custom axes */ .advanced { font-weight: 600; /* Standard axis */ font-variation-settings: 'GRAD' 100, 'slnt' -8; /* Custom GRAD + standard slnt */ }

Loading Variable Fonts

Variable fonts can be loaded from CDNs like Google Fonts or self-hosted with @font-face. The syntax is similar to static fonts with one key difference: specify the axis range.

<!-- Google Fonts variable font --> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <!-- Load full weight range (100-900) with .. notation --> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap" rel="stylesheet"> <!-- Use in CSS --> body { font-family: 'Inter', sans-serif; font-weight: 525; /* Any value in 100-900 range */ } /* Self-hosted variable font with @font-face */ @font-face { font-family: 'InterVariable'; src: url('../fonts/inter-variable.woff2') format('woff2-variations'), url('../fonts/inter-variable.woff2') format('woff2'); font-weight: 100 900; /* Specify range! */ font-stretch: 75% 125%; /* If font supports width axis */ font-display: swap; } body { font-family: 'InterVariable', sans-serif; font-weight: 450; /* Use any value in range */ } /* Multiple axes */ @font-face { font-family: 'RobotoFlex'; src: url('roboto-flex.woff2') format('woff2-variations'); font-weight: 100 1000; font-stretch: 25% 151%; font-display: swap; }

Animating Variable Fonts

One of the most exciting capabilities of variable fonts is smooth animation between weights, widths, or other axes. This is impossible with static fonts.

/* Breathing/pulsing text */ @keyframes breathe { 0%, 100% { font-weight: 300; } 50% { font-weight: 900; } } .breathing-text { font-family: 'Inter', sans-serif; animation: breathe 3s ease-in-out infinite; } /* Hover weight transition */ .button { font-weight: 400; transition: font-weight 0.3s ease; } .button:hover { font-weight: 700; /* Smoothly animates from 400 to 700 */ } /* Combining weight animation with scale */ @keyframes emphasize { 0%, 100% { font-weight: 400; transform: scale(1); } 50% { font-weight: 800; transform: scale(1.1); } } .animated-heading { animation: emphasize 2s ease-in-out infinite; }

Responsive Typography

Combine variable fonts with clamp() or media queries to create responsive typography that adapts weight to viewport size for optimal readability.

/* Responsive weight using clamp() */ h1 { font-family: 'Inter', sans-serif; font-size: clamp(2rem, 5vw, 4rem); font-weight: clamp(500, 400 + 20vw, 800); /* Mobile (~320px): weight ≈ 464 */ /* Tablet (~768px): weight ≈ 554 */ /* Desktop (~1920px): weight ≈ 784 */ } /* Media query approach */ h1 { font-weight: 500; /* Mobile: lighter weight */ } @media (min-width: 768px) { h1 { font-weight: 650; /* Tablet: medium weight */ } } @media (min-width: 1024px) { h1 { font-weight: 800; /* Desktop: heavier weight */ } } /* Adjust for dark mode */ @media (prefers-color-scheme: dark) { body { font-weight: 450; /* Slightly heavier in dark mode */ } }

Performance Benefits

Variable fonts typically offer significant performance improvements over loading multiple static font weights.

Scenario: Website needs Light (300), Regular (400), Medium (500), Semi-Bold (600), Bold (700) Static Fonts (Roboto): • roboto-light.woff2: 28KB • roboto-regular.woff2: 28KB • roboto-medium.woff2: 29KB • roboto-semibold.woff2: 30KB • roboto-bold.woff2: 31KB ---------------------------------------- Total: 146KB, 5 HTTP requests Variable Font (Inter): • inter-variable.woff2: 120KB ---------------------------------------- Total: 120KB, 1 HTTP request Savings: 26KB (18%), 4 fewer requests PLUS: Access to weights 301-699, smooth animations Tipping Point: • 1-2 weights: static fonts may be smaller • 3+ weights: variable font usually wins • 5+ weights: variable font significantly smaller

Browser Support & Fallbacks

Variable fonts have excellent support in all modern browsers (95%+ globally). Always provide fallback system fonts for the remaining 5%.

/* Progressive enhancement approach */ body { font-family: Arial, sans-serif; /* Fallback */ font-weight: 400; } /* Feature detection */ @supports (font-variation-settings: normal) { body { font-family: 'Inter var', Arial, sans-serif; font-weight: 450; /* Fine-tuned weight */ } } /* Or just use variable font directly with fallback */ body { font-family: 'Inter', Arial, sans-serif; font-weight: 450; /* Old browsers: rounds to closest static weight or uses Arial */ /* Modern browsers: exact 450 weight */ }

Popular Variable Fonts

Many high-quality variable fonts are available free or commercially. Here are some excellent starting points:

Free Variable Fonts: Inter (Sans-serif) • Axes: wght (100-900) • Best for: UI, body text, interfaces • Source: rsms.me/inter, Google Fonts Roboto Flex (Sans-serif) • Axes: wght, wdth, GRAD, slnt, opsz, and more • Best for: Material Design, Android apps • Source: Google Fonts Recursive (Mono/Sans) • Axes: wght, CASL (casual), MONO (monospace), slnt • Best for: Code editors, UI, playful designs • Source: recursive.design, Google Fonts Source Sans 3 (Sans-serif) • Axes: wght (200-900), wdth • Best for: Long-form reading, body text • Source: Adobe Fonts, GitHub Fraunces (Display Serif) • Axes: wght, opsz, SOFT, WONK (optical axis) • Best for: Headings, display, editorial • Source: fraunces.undercase.xyz, Google Fonts Amstelvar (Serif) • Axes: 14+ axes for extreme control • Best for: Experimental typography • Source: v-fonts.com Resources: • v-fonts.com - Directory with previews • fonts.google.com - Many variable fonts • axis-praxis.org - Testing playground • variablefonts.io - Guide and examples

Key Takeaways

  • Variable fonts: Single file contains multiple variations along design axes
  • Performance: One 120KB file vs five 30KB files (150KB total + 5 requests)
  • Flexibility: Any weight value (437, 550, 680) not just standard increments (100/200/300)
  • Standard axes: wght (weight), wdth (width), slnt (slant), ital (italic), opsz (optical size)
  • font-weight: Use standard property for weight axis, not font-variation-settings
  • Custom axes: Use font-variation-settings with four-letter tags (e.g., 'CASL' 1)
  • Loading: Google Fonts uses wght@100..900; self-hosted uses font-weight: 100 900;
  • Animation: Smooth transitions between weights impossible with static fonts
  • Responsive: Combine with clamp() to adapt weight to viewport size
  • Browser support: 95%+ globally since 2017-2018 (Chrome 62+, Firefox 62+, Safari 11+)
  • Tipping point: 3+ weights makes variable font smaller than static fonts
  • Use format('woff2-variations') then format('woff2') for best compatibility
  • Always provide fallback system fonts for old browsers
  • Intermediate weights (550, 650) create refined typography impossible with static fonts
  • Great for emphasis without color changes, hierarchy without size changes
  • Resources: v-fonts.com, axis-praxis.org, fonts.google.com, variablefonts.io