Variable Typography: Responsive Fonts, Dynamic Type and Modern Web Design

The word Variable rendered in five weights from thin to black on one typeface
Published

2026-09-15

Author

Nural Choudhury

A variable font is a single file that stores a continuous range of a typeface’s design, so weight, width, slant and other properties become numbers you set precisely rather than named styles you pick between.

This page covers using variable font technology to create responsive, efficient and animated letterforms that adapt to context. Typography basics covers the fundamentals underneath it: type anatomy, legibility, hierarchy and spacing, so it is not repeated here.

What this gets you:

One font file that replaces a shelf of static weights and widths, adapts continuously to viewport and reading conditions, and animates between states without a jump cut between files.

Where it applies:

Responsive headlines and body text, brand systems that need many weights without many files, and interface feedback where the type itself signals a state change.

What to get right first:

Choose axis values for what they mean to the reader, not because the font exposes them. Optical size is the axis people skip, and skipping it is why display type looks thin and loose at caption size.

Where it comes from

Type designers had interpolated between master drawings for years before the practice had a shared file format. The OpenType 1.8 specification, published in 2016, defined the variation tables that let one file hold that continuous design space instead of one file per style.

Adobe, Apple, Google, and Microsoft developed it jointly, then committed their platforms to it, which is why the format spread so quickly. Support has kept broadening across browsers, operating systems, and design tools, though it isn’t universal in every context, so it’s still worth planning a fallback.

Specimen grid showing weight, width, optical size and slant axis variation
The five registered variation axes rendered in Roboto Flex, per the OpenType 1.8 specification.

How variable fonts work

A type designer draws master designs at the extremes of each axis: the lightest weight and the heaviest, the narrowest width and the widest. The file stores those masters plus the mathematics to interpolate between them, so a request for weight 450 with masters at 400 and 500 produces a calculated in-between letterform rather than a stored one. Every value between the extremes is generated on request, not drawn in advance, which makes the range continuous.

Variation happens along axes, and the specification registers five with standard behaviour.

AxisTagWhat it variesWhat to watch
WeightwghtStroke thickness, from hairline near 100 through regular at 400 to black near 900Interpolation quality can drop near the extremes; test the values the design uses, not just 400
WidthwdthHorizontal proportion; 100 is normal, below is condensed, above is expandedExtreme condensed settings can close counters and hurt legibility at small sizes
SlantslntOblique angle in degrees, applied to the upright designSlant tilts the existing letterforms; it is not a true italic and reads differently from one
ItalicitalA switch, sometimes continuous, between the upright design and a separate italic drawingA continuous italic axis left mid-range can strand text in a half-italic state
Optical sizeopszThe drawing itself, redrawn for the size it renders at rather than scaledThe axis people skip; skip it and an 8-pixel caption inherits the drawing meant for an 80-pixel headline

Type designers can add custom axes beyond the five registered ones. Grade, tagged GRAD is the most common: it shifts visual weight without changing character widths, so a layout built on one grade value keeps its line breaks at another. A custom axis has no shared registry defining what it does, so check the specific font’s documentation before you rely on one.

A single variable file often weighs less than the static weights it replaces, because interpolation stores the range mathematically instead of storing every style as a separate drawing. Loading one file instead of four or five also cuts the requests a page has to make, which matters most on a slow connection. That saving isn’t automatic: a variable font carries data for every axis it defines whether you use it or not, so a font with axes a project ignores can lose the size advantage the format promises.

Two lines of identical text at different grade values but matching line length
Grade axis in Roboto Flex shifting visual weight while line length stays fixed.

How to apply it

Set an axis with font-variation-settings, which takes any axis tag and a numeric value: font-variation-settings: ‘wght’ 500, ‘wdth’ 85;. For the five registered axes, use the matching standard CSS property where you can instead; font-weight: 450; sets the weight axis without touching font-variation-settings directly, and it degrades more gracefully on a font that lacks the axis. Reach for font-variation-settings for slant, optical size or any custom axis, since none of those has a dedicated property.

Store the values you use as CSS custom properties rather than as literals scattered through a stylesheet. That gives you one place to change a value and one place to read it back when you check what a component is doing at a given breakpoint.

Scale weight or width with the viewport by writing the axis value as a clamp() against the same custom property that drives your fluid type size, so the two move together instead of independently. You must check the calculated value at both ends of its range, because a formula that looks right at the midpoint can push an axis past the point where the font holds its quality. Where a variable font ships an automatic optical size, let the browser apply it by rendered size rather than fighting it with a fixed value.

Animate an axis the way you animate any other CSS property: name font-variation-settings in a transition or an animation, and the browser interpolates the frames in between. Pick the timing the way you pick timing for any motion; animation principles have their own easing curves and duration bands, so use their guidance rather than a value chosen by feel. Respect prefers-reduced-motion for an axis animation exactly as you would for a transform, because a shifting typeface reads as motion to a user who asked for less of it.

Animating a variable axis costs more per frame than animating opacity or a transform, because the browser recalculates glyph outlines instead of compositing an existing layer. Test an animated axis on the slowest device your audience uses, not the machine on your desk, before you ship it as a default rather than a light enhancement.

A heading rendered at two font weights labelled for narrow and wide viewports
A heading's weight axis set through a CSS clamp, lighter on a narrow viewport, heavier on a wide one.

A worked example

Take a page heading that needs more visual weight on a wide desktop viewport than it needs on a phone, without loading a second font file for the difference. Set the weight axis through a custom property, --heading-weight and drive it with a clamp() between 500 at the narrowest supported viewport and 650 at the widest, tied to the same breakpoint logic as the heading’s font size. Apply the property through font-variation-settings: 'wght' var(--heading-weight); on the heading rule alone, so the rest of the page keeps one predictable weight.

Check the result at three widths, not two: the narrowest, the widest and a point in between, because a linear clamp() can land on a mid-range value the font renders less cleanly than either endpoint. If it does, adjust the clamp’s bounds instead of accepting the dip. The axis exists so you choose the value; it should not choose one for you by accident.

Where it goes wrong

The mistake I see most often is loading the full variable file when a project only ever calls two weights. A variable font’s size scales with the range it stores, so if a brand only ever uses regular and semibold, two static files are usually smaller, and always simpler, than one variable file carrying axes nobody touches.

The second is animating an axis because it can move, not because the movement says anything. A weight that pulses on hover for no reason reads as decoration; a weight that shifts because a button just became active reads as feedback. If I cannot name the state change an animation communicates, I cut the animation.

The third is treating a variable font as one drawing at every size and leaving optical size unset. Set opsz low for an 8-pixel caption and high for an 80-pixel headline and let the font redraw itself for each. Leave it unset, and the caption inherits the headline’s proportions: heavier strokes and wider spacing than a small size needs.

Common questions

What is a variable font, in one sentence:

A single font file that stores a continuous range of a typeface’s design, so weight, width and other properties are numbers you set rather than named styles you pick between.

Do I need a design tool that supports variable fonts to use one:

No. You can set every axis value from CSS alone; a design tool only matters if you are drawing or editing the masters yourself, and that is the type designer’s job, not the implementer’s.

Does a variable font always load faster than static weights? Only if the axes it carries are the ones the project uses. A file with five axes when a project needs one is not smaller than two well-chosen static weights, so check the file size rather than assume the format wins by default.

Can I animate a custom axis the same way as a registered one? Yes. font-variation-settings accepts any axis tag the font defines, registered or custom, and the browser interpolates it the same way regardless of type.

What happens on a browser that does not support variable fonts:

The standard CSS properties, such as font-weight, still resolve to the nearest fixed value the browser can render, so plan a sensible static fallback rather than assume universal support.

Key facts, current as of September 2026

FactDetail
Governing specificationOpenType 1.8, published 2016
Joint developersAdobe, Apple, Google, and Microsoft
Registered axesFive: weight (wght), width (wdth), slant (slnt), italic (ital), optical size (opsz)
Common custom axisGrade (GRAD): shifts visual weight without changing character widths
Primary CSS propertyfont-variation-settings, for any axis tag
Standard CSS shorthandfont-weight maps directly to the wght axis on a variable font