DevTools Logo
All posts

Your Font Has Axes: A Field Guide to Variable Fonts and OpenType Features

August 28, 2026 · DevTools

typography
variable fonts
opentype
web fonts
css

For most of the web's history, "bold" meant a second file. Regular.woff2, Bold.woff2, Italic.woff2 — every weight and style a separate download. Variable fonts collapse that into one file with continuous, animatable axes: weight, width, slant, optical size, and however many custom axes a foundry decides to register. The catch is that understanding an axis and seeing it move are two different problems, and they're solved by two completely different pieces of code.

fvar declares, gvar interpolates

A variable font's fvar table just lists the axes and their ranges — wght from 100 to 900, say — plus named instances like "Regular" and "Black" that map to specific coordinates. That part is cheap to read: any font-parsing library can list a font's axes in a few lines. Actually interpolating glyph shapes between two points on an axis is the gvar table's job, and it's a genuinely hard problem — glyph outlines need matching point counts and correspondence across masters, delta sets have to be decoded and blended, and getting it wrong produces glyphs with self-intersecting outlines. Most JS font-parsing libraries (opentype.js included) read fvar but don't implement gvar — which means "inspect this font's axes" and "render this font's axes" have to be solved by two entirely different code paths.

The trick: let the browser do the hard part

Since re-implementing gvar in JavaScript is its own multi-week project, the practical answer is to not implement it at all — delegate to the one thing that already interpolates variable fonts correctly: the browser's own text rendering engine. Load the font via the FontFace API, apply real CSS font-variation-settings to live DOM text, and drag a slider that updates those settings on every frame. What you see is exactly what a browser would render in production, because it is the browser rendering it — not a JavaScript approximation of the same math.

Kerning is a lookup, not a rule

Ask most developers what kerning is and they'll describe a rule ("make letters closer together"). It's actually a lookup table: specific glyph pairs — AV, To, Ta, Wa — get specific adjustment values baked in by the type designer, because a general rule can't fix the visual gaps that specific letterform shapes create. A font with no kerning table simply returns 0 for every pair, which is a valid, unremarkable answer — plenty of fonts, especially monospace and display faces, ship with none.

Fallback metrics: the CLS fix nobody teaches

Every custom webfont has an invisible tax: before it loads, the browser renders with a fallback, and the swap when the real font arrives shifts layout if the two fonts don't share vertical metrics. CSS Fonts Module Level 5 added ascent-override, descent-override and line-gap-override specifically to close that gap — compute them from the webfont's own metrics and apply them to a @font-face block that maps to your system fallback, and the fallback occupies the same vertical space the real font will, so the swap doesn't reflow the page. This is a two-line CSS fix for a problem that usually gets blamed on "web fonts are just slow."

OpenType features are read, not guessed

Ligatures, contextual alternates, tabular figures — whether a font actually supports these isn't something to infer from its name or its price tier. It's a fact recorded in the font's own GSUB and GPOS feature lists, readable directly from the binary. Reading it beats guessing it: plenty of fonts marketed as having "full OpenType support" ship a much shorter feature list once you actually look.

Load a variable font, sweep an axis end to end and watch for glyphs that clip at the extremes, check whether AV actually kerns, and generate the metric-override block before you ship. Twenty minutes with the real file beats trusting the specimen page.