Type something to search...
What Is Optical Sizing in Typography?

What Is Optical Sizing in Typography?

You pick an elegant high-contrast serif for a magazine-style site. At 64px in the hero headline it looks superb: hairline serifs, crisp thin strokes, tight spacing. Then you use the same font for 15px captions and 16px body copy, and it falls apart. The thin strokes nearly vanish on a standard-density laptop screen, the counters feel cramped, and letters crowd each other. Nothing is wrong with your CSS. You're asking one design to do two jobs that type designers have always treated as different problems.

The solution is optical sizing, and with variable fonts it's now something the browser can do for you. This article explains what optical sizing is, where it comes from, how the opsz axis works, what browsers do by default, how to control it with CSS, and which fonts support it.

What Is Optical Sizing?

Optical sizing is the practice of adjusting a typeface's design for the size at which it will be read. Small text and large text have different needs:

CharacteristicSmall sizes (captions, body)Large sizes (headlines, display)
Stroke contrastLower, so thin strokes surviveHigher, for elegance and sparkle
X-heightOften largerCan be smaller
Letter spacingLooserTighter
Counters and aperturesWider and more openCan be narrower
Fine detailsSimplified, sturdier serifsSharper, more delicate detailing

The goal is that the typeface looks consistent across sizes even though it's actually being drawn differently. A small-size design scaled up looks clunky and loose; a display design scaled down looks spindly and cramped.

A Short History

In metal type, optical sizing was automatic. Every size of a typeface was cut separately by hand, and punchcutters adjusted each size to suit it. A 6-point font and a 72-point font of the "same" typeface were different drawings.

Phototypesetting and then digital type changed that. One master outline could be scaled to any size, which was cheap and convenient, and most typefaces lost their optical adjustments. Some foundries kept the tradition alive by releasing separate families such as "Caption," "Text," "Subhead," and "Display" versions, but you had to choose and load them manually.

Variable fonts brought it back. The OpenType 1.8 specification (2016) defined a registered optical size axis, tagged opsz, that lets a single font file interpolate smoothly between small-size and large-size designs.

How the opsz Axis Works

The opsz axis is a registered variation axis whose values are meant to correspond to the text size in typographic points. A font might support an opsz range of 8 to 144, with the designer drawing master designs at each end (and sometimes in between). The font then interpolates for any value in that range.

On the web, browsers map opsz to the computed font-size in CSS pixels. If a paragraph is set at 16px, the browser sets opsz to 16. If a headline is 48px, it sets opsz to 48. Values outside the font's supported range are clamped to the nearest end.

The axis is one of five registered axes in OpenType:

TagAxisCSS property
wghtWeightfont-weight
wdthWidthfont-stretch / font-width
slntSlantfont-style: oblique
italItalicfont-style: italic
opszOptical sizefont-optical-sizing

If variable fonts are new to you, the post on what variable fonts are covers the broader concept.

What Browsers Do by Default

The CSS property that controls this is font-optical-sizing, and its initial value is auto. That means:

  • If the font has an opsz axis, all modern browsers (Chrome, Edge, Firefox, Safari) set it automatically based on font-size.
  • If the font has no opsz axis, nothing happens.

So in many cases you don't need to write any CSS. Load a font with an optical size axis, set your font sizes, and each element gets the appropriate design. A caption at 13px and a headline at 56px will look like they were drawn for their sizes, because in effect they were.

/* This is the default; you don't need to write it */
body {
  font-optical-sizing: auto;
}

Controlling Optical Sizing in CSS

Turning It Off

Occasionally you want the same design at every size, for example to match a brand mark exactly, or when you're animating font-size and don't want the letterforms shifting during the animation.

.logo-text {
  font-optical-sizing: none;
}

With none, the browser uses the axis's default value, which is defined by the font and is usually somewhere near text size.

Setting a Specific Value

There's no font-optical-sizing: 24 syntax. To set an explicit optical size, use the low-level font-variation-settings property:

/* Make 14px small print use a sturdier, smaller-size design */
.fine-print {
  font-size: 0.875rem;
  font-variation-settings: "opsz" 10;
}

/* Use the display design on a medium-size heading for more contrast */
.pull-quote {
  font-size: 1.75rem;
  font-variation-settings: "opsz" 72;
}

Two cautions apply. First, font-variation-settings overrides the automatic value, so it stays fixed even if the font size changes at another breakpoint. Second, like font-feature-settings, it's a single property: setting it again replaces all axes in the list. If you're also controlling a custom axis, include both:

.pull-quote {
  font-variation-settings: "opsz" 72, "SOFT" 50;
}

Because a fixed value can't adapt, scope these overrides to specific components rather than setting them on body or a global wrapper.

Optical Sizing with Fluid Type

If you use fluid typography with clamp(), auto handles the continuous change for you. As the computed font size moves from 32px on mobile to 56px on desktop, the browser updates opsz to match. This is one of the best arguments for leaving auto alone: a manually set value can't follow a fluid size.

Fonts That Support Optical Sizing

A growing number of fonts ship with an opsz axis. Some well-known ones available on Google Fonts:

FontClassificationopsz rangeNotes
InterSans-serif14–32Version 4 added optical sizing for display use
Roboto FlexSans-serif8–144Many axes, very flexible
LiterataSerif7–72Designed for e-reading
NewsreaderSerif6–72Editorial, strong difference across sizes
Source Serif 4Serif8–60Workhorse text serif
FrauncesSoft serif9–144Display personality, also has custom axes
Bodoni ModaDidone6–96High contrast; benefits hugely from opsz

Check each font's specimen page for the exact range before relying on these numbers, since fonts get updated. On the system side, Apple's San Francisco font switches between its Text and Display designs automatically, which is part of why system-ui text looks good at both small and large sizes on Apple devices.

High-contrast serifs and Didones gain the most from optical sizing, because their thin strokes are the first thing to break at small sizes. Low-contrast sans-serifs show subtler differences, mostly in spacing and x-height.

Loading Fonts with the opsz Axis

Google Fonts CSS API

You need to request the axis explicitly, or Google Fonts may serve a file without it. Axes are listed alphabetically, with lowercase tags first:

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
  href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400..700;1,6..72,400..700&display=swap"
  rel="stylesheet"
/>

This requests upright and italic, the full optical size range, and weights from 400 to 700.

Next.js with next/font

next/font/google includes only the weight axis by default for variable fonts. Add other axes with the axes option:

import { Newsreader } from "next/font/google";

export const newsreader = Newsreader({
  subsets: ["latin"],
  style: ["normal", "italic"],
  axes: ["opsz"],
  display: "swap",
  variable: "--font-newsreader",
});

Apply newsreader.variable to the html element and reference var(--font-newsreader) in your CSS. If you're new to the API, see how to use next/font in Next.js.

Self-Hosted @font-face

If you self-host, declare the variable font normally. Optical sizing doesn't need a separate descriptor; the browser detects the axis in the file.

@font-face {
  font-family: "Newsreader";
  src: url("/fonts/newsreader-var.woff2") format("woff2") tech(variations);
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}

Watch the file size. Every extra axis adds data. A font with opsz and wght is typically noticeably larger than the same font with wght alone. If you only ever use body sizes, you may not need the axis at all.

Verifying That It Works

It's easy to assume optical sizing is active when it isn't. Check:

  1. Firefox DevTools. Select an element, open the Fonts tab in the Inspector, and look at the variable font axes. An opsz slider will show the value currently applied.
  2. Side-by-side comparison. Put the same word at 12px and at 72px, then screenshot and scale them to the same visual height. With optical sizing active, the small version should look wider-spaced and lower in contrast.
  3. Toggle the property. Temporarily set font-optical-sizing: none on a headline and watch for a change. If nothing changes, the loaded file probably lacks the opsz axis.

Optical Sizing Without a Variable Font

If your chosen font doesn't have an opsz axis but ships separate optical families, you can approximate it manually:

body {
  font-family: "Example Text", Georgia, serif;
}

h1,
h2 {
  font-family: "Example Display", "Example Text", Georgia, serif;
}

Only use the display cut above roughly 24–32px, and the text cut below that. You can also compensate for missing optical sizing with spacing: loosen letter-spacing slightly (0.01em to 0.02em) on small text and tighten it (−0.01em to −0.03em) on large headlines. It's not the same as a redrawn design, but it gets you part of the way.


Optical Sizing FAQ

Usually not. The font-optical-sizing property defaults to auto, and every modern browser applies the opsz axis automatically based on font size when the font contains one. You only need CSS to turn it off or to force a specific value.

Indirectly, and usually for the better. Small-size designs have lower contrast between thick and thin strokes and more open spacing, which makes small text easier to read. It does not change color contrast ratios measured under WCAG 1.4.3, but it improves perceived legibility.

The file you loaded may not include the opsz axis. Google Fonts and next/font only include the axes you request, so check your embed URL or axes option, and confirm in Firefox DevTools that an opsz axis is present.

The OpenType specification describes opsz in typographic points, but browsers set it from the CSS font-size in CSS pixels. In practice, the font-size value you write in CSS becomes the opsz value, clamped to the range the font supports.

Often, yes. As font size changes during an animation, the letterforms change too, which can look like wobbling. Setting font-optical-sizing to none on the animated element keeps the design stable during the transition.

Yes. Each axis adds data to a variable font, so a file with opsz and wght axes is larger than one with only wght. Weigh the visual gain against the extra bytes, especially if your design only uses a narrow range of sizes.

Conclusion

Optical sizing adjusts a typeface's contrast, spacing, and proportions to suit the size it's read at. It was standard practice in metal type, mostly disappeared with digital scaling, and has returned through the opsz axis in variable fonts. Browsers apply it automatically when font-optical-sizing is auto, which is the default.

To use it well, pick a font with an opsz axis, make sure your loading method actually includes that axis, and let auto handle the work, especially with fluid type sizes. Reach for font-variation-settings only when you have a specific reason to override the size-based value, and verify the result in DevTools rather than assuming it's working.

Here are some useful references for going deeper on optical sizing:

  1. MDN Web Docs: font-optical-sizing — syntax, default behavior, and browser support.
  2. MDN Web Docs: Variable fonts guide — registered and custom axes, with examples.
  3. Microsoft Typography: Registered axis tag: opsz — the official definition of the optical size axis.
  4. W3C: CSS Fonts Module Level 4 — the specification for font-optical-sizing and font-variation-settings.
  5. Google Fonts Knowledge: Google Fonts Knowledge — background on variable fonts and optical size in practice.
Share :

Related Posts

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

You align an icon next to a button label and it looks a pixel or two too high, no matter how you adjust vertical-align. You set overflow: hidden

Continue Reading
Are Google Fonts GDPR-Compliant?

Are Google Fonts GDPR-Compliant?

In late 2022, thousands of small business owners in Germany and Austria opened letters demanding a few hundred euros in "damages" because their websi

Continue Reading
How to Audit Web Font Performance with Lighthouse?

How to Audit Web Font Performance with Lighthouse?

A client sends you a screenshot of their PageSpeed Insights report: performance score 61, LCP 3.9 seconds, and a vague list of warnings. They want to

Continue Reading