Ever wondered why your layout suddenly looks like a ransom note when a custom font fails to load? We've all been there: the fallback font kicks in, but because its lowercase letters are tiny compared to your brand face, the whole hierarchy collapses and readability goes out the window.
Right, let's talk about font-size-adjust. It's one of those properties that sat in the 'experimental' bucket for years, but as of mid-2024, it's officially hit Baseline status. That means we can finally use it to build typography primitives that don't break when the network gets flaky.
The Problem with Fallback Fonts
When we set a font-size, we're technically defining the height of the em-square. However, different typefaces use that space differently. One font might have huge capital letters and tiny lowercase ones, while another is more balanced. This ratio is known as the aspect value.
MDN defines it perfectly: "font-size-adjust specifies that the font size should be chosen based on the height of lowercase letters rather than the height of capital letters." By focusing on the x-height (the height of the lowercase 'x'), we ensure that text remains legible even if the browser swaps your expensive brand sans-serif for Arial.
Implementing font-size-adjust in Primitives
In a modern design system, I've found it's best to bake this directly into your typography tokens or base components. You'll want to calculate the aspect ratio of your primary brand font and set that as your baseline.
:root {
/* Ratio tuned to your primary brand font's x-height */
--brand-x-height-ratio: 0.54;
}
.text-body {
font-family: "Brand Sans", system-ui, sans-serif;
font-size: 1rem;
font-size-adjust: var(--brand-x-height-ratio);
}
If "Brand Sans" fails, the browser will scale the system-ui font so that its x-height matches 0.54 times the 1rem font size. It's a game-changer for visual stability.
Modern Syntax: The Keyword Approach
While passing a raw number is the classic way, the modern spec allows for more granular control using keywords like ex-height or cap-height. This is particularly useful when you want the browser to do the heavy lifting for you.
.text-label {
font-family: "Brand Sans", Arial, sans-serif;
font-size: 0.875rem;
font-size-adjust: ex-height from-font;
}
Using from-font tells the browser to look at the metrics embedded within the primary font itself. It's much cleaner than manually measuring pixels in Figma and doing the maths yourself.
Common Pitfalls to Avoid
I've seen a few recurring mistakes when teams start adopting this. First off, don't confuse font-size-adjust with text-size-adjust. The latter is for mobile text inflation algorithms and won't help your x-heights at all.
- Hard-coding ratios: Don't just copy 0.58 from an example. Every typeface is different. Calibrate it for your specific brand font.
- Ignoring @font-face: You can also use the
size-adjustdescriptor inside@font-faceto normalize your brand font before it even hits your components. - Browser Support: While it's Baseline now, older Android browsers or IE (if you're still stuck there) won't support it. Always ensure your design degrades gracefully.
Wrapping Up
- Use
font-size-adjustto ensure fallback fonts maintain the same visual weight as your primary brand font. - Leverage the
from-fontkeyword for a more automated, metric-based approach. - Always test your fallbacks by temporarily disabling your web fonts to see the adjustment in action.
Typography is the foundation of any design system. By mastering these new CSS properties, you're not just making things look pretty—you're making them resilient.
If you want to go deeper and learn how to build real, production-ready CSS design systems step by step, check out my full course here: CSS Design Systems Course
I cover the full pipeline in Ship Your Design System, 200 pages on Amazon Kindle.
Feel free to reach out on Twitter or LinkedIn if you have questions about implementing this in your own projects!