Variable Fonts: The Real Production Trade
A measured break-even point. Instancing and subsetting a real family shows that a two weight site is smaller with static cuts, which is not what the pitch says.
Date
Category
Writer

Inter's variable font, version 4.001, is 876,576 bytes as a TrueType file. I pulled it apart to see where the bytes are. The glyf table, which holds every outline in the font, is 137,900 bytes. The gvar table, which holds nothing but the deltas describing how those outlines move as you travel the axes, is 454,714 bytes. The variation data is more than three times the size of the drawings, and more than half the file.
That ratio is the whole trade, and it is why "one variable file is smaller than several static ones" is true in the general case and false in the case most studios are actually in.
What was introduced, and when
OpenType Font Variations were announced on 14 September 2016 at the ATypI conference in Warsaw, as part of OpenType 1.8. It was a four-way piece of work, and Google's announcement, written by Behdad Esfahbod and Sascha Brawer, divides the labour precisely: "Microsoft did the spec work; Apple brought their existing technology and expertise; Adobe updated their CFF format into CFF2; and we brought the tools and testing we'd been developing."
The technology is older than the announcement. The OpenType specification says so plainly: "Font variation mechanisms for fonts using TrueType outlines were first introduced by Apple in 'TrueType GX'. Some of the tables used for OpenType Font Variations have been adapted from Apple's earlier specifications with some enhancements and revisions." Google's post described it as reviving a technology "from 23 years ago".
Two tables are required in every variable font: fvar, which "is required to describe the variations supported by the font", and STAT, which establishes the relationships between members of a family. An axis of variation is defined as "A designer-determined variable in a font face design that can be used to derive multiple, variant designs within a family."
One detail matters more than it looks. A variable font has a default instance, and the specification is direct about why you should choose it carefully: "A consideration in the choice of default is desired behavior in legacy applications or platforms that do not support Font Variations: in such software, only the default instance of a variable font will be supported." Ship a font whose default is a hairline and old software will render your entire site in a hairline.
The registered axes
Five axes are registered, which means their tags have agreed meanings across fonts. The specification is explicit that this interoperability is deliberate and is the point of registration: of optical size it says the scale "is designed to provide a much stronger degree of interoperability. For instance, two different fonts with an optical-size value of 20 are assumed to be best suited to text set at 20 points, because the scale is designed that way." A designer may define further axes with their own tags, but those carry no agreed meaning outside the font that defines them.
Tag | Name | Valid range | Required default | Scale |
|---|---|---|---|---|
wght | Weight | 1 to 1000 | 400 | stroke thickness, lighter to blacker |
wdth | Width | strictly greater than zero | 100 | percentage of the designer's normal width |
opsz | Optical size | strictly greater than zero | 10 to 16 recommended | typographic points, 1/72 inch |
slnt | Slant | greater than -90, less than +90 | 0 | counter-clockwise degrees from upright |
ital | Italic | 0 to 1 | 0 | 0 is Roman, 1 is fully italic |
The slnt sign convention catches people out every time. Because the angle is counter-clockwise from upright, a normal right-leaning oblique is a negative value. This matches the italicAngle field in the post table, so it is consistent rather than perverse, but slnt: 10 will lean your text the wrong way.
And ital is rarely an axis at all. The specification notes it "can be used as a variation axis within a variable font, though this is not expected to be common". A true italic is a different set of drawings, not an interpolation, so families normally ship a second variable file. Inter does: the roman is Inter[opsz,wght].ttf and the italic is a separate Inter-Italic[opsz,wght].ttf of 906,596 bytes, with the same two axes. If your design uses italics, you are shipping two variable fonts, and every size calculation below doubles.
The CSS
The rule that saves the most trouble is MDN's own: font-variation-settings "is a low-level mechanism designed to set variable font features where no other way to enable or access those features exist. You should only use it when no basic properties exist to set those features". Use font-weight rather than "wght", font-stretch rather than "wdth", font-style rather than "ital" or "slnt", and font-optical-sizing rather than "opsz". The reason is a cascade trap: characteristics set through font-variation-settings "will always override those set using the corresponding basic font properties, e.g., font-weight, no matter where they appear in the cascade". A stray low-level declaration will beat a later, more specific font-weight, and nothing in the stylesheet looks wrong.
The file size maths, measured
The claim made at launch, in Tim Brown's announcement on the Typekit blog the same day, was "a single font file gaining an infinite flexibility of weight, width, and other attributes without also gaining file size". That is the sentence I want to test, because it is the one every subsequent article repeats.
I took the Inter variable font above and used the fontTools varLib.instancer module to produce static instances from it, then subset everything to a 179-character Latin set and compressed to WOFF2, which is roughly what a production web font pipeline does. Same source outlines, same hinting, same tooling, so the comparison is fair.
File | WOFF2, full charset | WOFF2, Latin subset |
|---|---|---|
Variable, opsz and wght | 350,460 | 84,752 |
Variable, wght only (opsz pinned) | 236,932 | 54,092 |
Static Regular 400 | 113,588 | 25,264 |
Static Medium 500 | 116,548 | 25,768 |
Static SemiBold 600 | 117,108 | 25,944 |
Static Bold 700 | 117,152 | 25,900 |
Static Light 300 | 116,036 | 25,484 |
Now do the arithmetic on the subset column, which is the column that matters for a website.
The break-even is just over two weights. At exactly two, the static pair wins, and it wins by a small enough margin that you would not notice it in a waterfall. At three it stops being close, and at five the variable file is well under half the payload.
And if you ship the variable font without pinning the axis you are not using, the picture is much worse: 84,752 bytes against 51,164 for two statics, 66% larger. Inter's opsz axis runs from 14 to 32. Pinning it to 14 removed 30,660 bytes from the subset file, 36% of it. That is an axis's worth of gvar deltas that most sites are paying for and never touching.
Instancing is the missing step
Instancing is the operation that makes all of this manageable, and it is under-used because most people think of a variable font as something you either use whole or not at all. The fontTools documentation describes both modes: "If the input location specifies all the axes, the resulting instance is no longer 'variable'," while "a 'partial' instance (i.e. a less variable font) is produced when some of the axes are omitted, or restricted with a new range". You can pin an axis to a value, or restrict it "by setting either a new minimum or maximum". Pinned axes vanish from fvar, and the remaining deltas in gvar are rewritten, "their deltas multiplied by the scalar support of the axes to be pinned".
So the practical move is not "variable or static". It is: take the family's variable font, pin the axes you will not use, clamp the ranges you will, subset the character set, and ship that. In this test that single step accounted for a larger saving than any decision about which weights to include.
Optical size, concretely
opsz is the axis that justifies variable fonts on typographic grounds rather than logistical ones, and it is the one people describe most vaguely. The specification is not vague. Values "can be interpreted as text size, in typographic points, as defined in the OpenType specification: a physical unit equal to 1/72 of a standard physical inch", and "A value in the range 10 to 16 is recommended for typical text settings."
So opsz is not an abstract slider. It is a declaration of the point size the design is cut for. A face with an opsz of 14 is drawn for text; the same family at opsz 32 is drawn for a heading, and in a well-made family that means less weight in the joins, tighter spacing, and more contrast between thick and thin.
Browsers do this for you. font-optical-sizing has an initial value of auto, meaning "The browser will modify the shape of glyphs for optimal viewing", and MDN records it as Baseline, available across browsers since March 2020. The specification gives implementers detailed rules: selection should be based on "the text size with a default or '100%' zoom level, not on a combination of text size and zoom level"; zoom that does not re-flow text should not change the selection; and if the displayed size falls outside the axis range, "Optical size axis variant selection should be clamped to the appropriate minimum or maximum axis value, not reset to the default instance".
That clamping rule is why Inter's axis running 14 to 32 behaves sensibly rather than snapping back to the default at 11px body text. And it is why the most common optical sizing bug is not a bug at all: if your headings do not look any different, check whether the font actually has an opsz axis, because most do not.
Support, and what I could not verify
Can I Use puts global support for variable fonts at 96.54%, with first full support in Chrome 66, Edge 17, Firefox 62 and Safari 11, including iOS Safari 11, and Samsung Internet 8.2. Chrome 59 to 61, Firefox 53 to 61 and Opera 49 to 52 had partial or flagged support before that.
What I could not verify in this session: the operating system requirements underneath those browser versions. It is commonly said that variable font support on macOS requires 10.13 High Sierra and that some Windows applications depend on DirectWrite versions, and I did not find a primary source confirming either, so I am not asserting them. If your audience includes long-tail desktop operating systems, test rather than trust.
The rule I use
Count the cuts before choosing the format. Two weights and no italic: ship two static WOFF2 files, because the measurement above says they are smaller and there is nothing else on the table. Three or more weights, or any intention to use intermediate values, or an opsz axis you will genuinely exercise: ship variable, but instance it first. Pin every axis you are not animating, clamp the ranges you are, subset the character set, and check the byte count rather than assuming.
The claim that started this, that a variable font buys flexibility "without also gaining file size", was written on the day of the announcement about a technology nobody had shipped yet, and it is generous rather than false. The gvar table is real and it is large. Four hundred and fifty thousand bytes of it, in a font whose outlines occupy a hundred and thirty-eight thousand. Whether that is a saving depends entirely on how many cuts it replaces, and for a great many studio projects the honest answer is two.
Sources
Microsoft, "OpenType Font Variations Overview", OpenType specification version 1.9.1. Required
fvarandSTATtables, the list of variation tables, the definition of an axis of variation, named instances, the default instance and the legacy-software caveat, and the TrueType GX lineage. https://learn.microsoft.com/en-us/typography/opentype/spec/otvaroverviewMicrosoft, "Design-Variation Axis Tag Registry", OpenType specification. The five registered axes and the statement that the optical size scale "is designed to provide a much stronger degree of interoperability". https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg
Microsoft, OpenType specification, individual axis pages for
wght,wdth,opsz,slntandital. Ranges, required defaults, scale interpretations, the counter-clockwise slant convention, the optical size units and automatic-selection and clamping rules, and the note thatitalas an axis "is not expected to be common". https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxistag_opszBehdad Esfahbod and Sascha Brawer, "Introducing OpenType Font Variations", Google Open Source Blog, 14 September 2016. OpenType 1.8, the division of work between Microsoft, Apple, Adobe and Google, and the "23 years ago" framing. https://opensource.googleblog.com/2016/09/introducing-opentype-font-variations.html
Tim Brown, "Variable fonts, a new kind of font for flexible design", Typekit Blog, 14 September 2016. The ATypI Warsaw announcement, the joint development by Apple, Google, Microsoft and Adobe, and the "without also gaining file size" claim. https://blog.typekit.com/2016/09/14/variable-fonts-a-new-kind-of-font-for-flexible-design/
fontTools documentation, "varLib.instancer". Full versus partial instancing, pinning and range limiting, and the effect on
fvar,gvarandHVAR. https://fonttools.readthedocs.io/en/latest/varLib/instancer.htmlCan I Use, "Variable fonts". Global support of 96.54% and first support in Chrome 66, Edge 17, Firefox 62, Safari 11 and iOS Safari 11. https://caniuse.com/variable-fonts
MDN Web Docs, "font-variation-settings". The low-level mechanism warning, the mapping from axes to high-level properties, and the statement that it overrides the basic font properties regardless of cascade position. https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings
MDN Web Docs, "font-optical-sizing". The
autoandnonevalues, theautoinitial value, and Baseline availability since March 2020. https://developer.mozilla.org/en-US/docs/Web/CSS/font-optical-sizingInter version 4.001 (git-66647c0bb), Rasmus Andersson, SIL Open Font License, as distributed in the Google Fonts repository. All byte counts in this article were measured by me from
Inter[opsz,wght].ttfandInter-Italic[opsz,wght].ttfusing fontTools 4.62.1. https://raw.githubusercontent.com/google/fonts/main/ofl/inter/Inter%5Bopsz%2Cwght%5D.ttf
Related reading
Latest Articles.
© Moc Mallen Studio
Thoughts, ideas, and perspectives on design, simplicity, and creative process.

