Typography accounts for 95% of web design. Your font choice can be critical for branding, readability, and performance.
Over time, recommendations for using web fonts have changed as browsers adopted new standards. Now in 2021, I wanted to learn the best practices for using web fonts on high-performance sites.
System Fonts
The fastest way to use a web font is none. Browsers include a set of web-safe fonts (e.g. Arial, Georgia, Times New Roman) you can use by default.
Using web-safe fonts or the system font stack will be the fastest option.
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial,
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
Why Use Web Fonts?
If system fonts are the fastest option, why do web fonts exist? Branding, improved design, and cross browser and device consistency. 82% of web pages for desktop use web fonts.
Let's cover five different areas for high-performance web fonts and conclude with a 2021 recommendation.
- Self Hosting
- Preloading
- Font Display
- Variable Fonts
- Subsetting
Self Hosting
Google Fonts is responsible for 70% of all web font usage. With over 1000 fonts, they provide easy access to quality fonts, multiple formats, and performant defaults (pre-connecting and swapping).
However, Google Fonts is no longer necessary. Since 2018, Google has advised self-hosting for optimal performance through preloading.
There aren't any caching advantages anymore, either. Let's say you're using the font "Roboto", which is a popular font on Google Fonts. Chances are you've visited another site using "Roboto" and cached the font.
Since October 2020, Chrome no longer allows a shared cache across sites. Safari has worked this way since 2013. "Roboto" will be re-downloaded for every site, regardless of it being cached.
When self-hosting, ensure you cache your font with the Cache-Control
HTTP header. immutable
tells the browser the file will never change. When a request is made within the max-age
(1 year), it avoids the roundtrip to ensure it's the latest content.
Cache-Control: public, immutable, max-age=31536000
If you need Google Fonts, use these optimizations. With the latest changes in the v2 API, you can tailor fonts to specific users and platforms (including variable fonts).
Preloading
The browser assigns loading priorities to different types of resources. By default, CSS will be loaded before scripts and images. You can influence the importance of resources by preloading critical assets.
Fonts are discovered late by the browser by default. By preloading, we fetch the font file as soon as possible. Then, the browser caches the font making it available immediately.
Preloading can improve performance metrics like Time to Interactive and First Contentful Paint. For example, Shopify saw a 50% (1.2 second) improvement in First Contentful Paint, removing their Flash of Invisible Text (FOIT).
As of 2020, 75% of web fonts use WOFF2. You likely only need this. For example:
<link
rel="preload"
href="/fonts/inter-var-latin.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
Support: All modern browsers except Firefox.
Font Display
font-display
allows you to modify the rendering behavior of web fonts with values such as auto
, swap
, block
, fallback
and optional
. When loading web fonts, we want to prevent layout shift. This occurs in two ways:
- Flash of Unstyled Text (FOUT) — The fallback font is swapped with a new font (e.g.
swap
). - Flash of Invisible Text (FOIT) — Invisible text is shown on the page until a new font is rendered (e.g.
block
).
Browsers currently have a default strategy similar to block
. The only option that eliminates layout shift is optional
. Combined with the other performance optimizations in this article, optional
is your best choice.
@font-face {
font-family: 'Inter';
font-style: normal;
font-display: optional;
src: url(/fonts/inter-var-latin.woff2) format('woff2');
}
Support: All modern browsers
Variable Fonts
Variable fonts allow us to combine multiple styles and weights (e.g. bold, italic) into a single font file.
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900; // Range of weights supported
font-display: optional;
src: url(/fonts/inter-var-latin.woff2) format('woff2');
}
You can try out different variable font options here.
Support: All modern browsers. Even Google Fonts v2 API has support for variable fonts.
Subsettings
Font files contain multiple languages and glyphs, which increase the file size. Subsetting is the removal of characters you don’t need.
For example, we might use the Inter variable font and subset to latin languages.
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900; // Range of weights supported
font-display: optional;
src: url(/fonts/inter-var-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
U+FEFF, U+FFFD;
}
Conclusion
- Use a variable font
- Preload your font file
- Self-host instead of Google Fonts
- Use
font-display: optional
to prevent layout shift
Here's an example of a site implementing all these recommendations.
Future
If you need to use font-display: swap
, future support for Font Metrics Override will reduce layout shift when swapping.
@font-face {
font-family: ...;
src: ...;
ascent-override: 80%;
descent-override: 20%;
line-gap-override: 0%;
...;
}