{"id":305626,"date":"2020-03-25T06:33:56","date_gmt":"2020-03-25T13:33:56","guid":{"rendered":"https:\/\/css-tricks.com\/?post_type=chapters&p=305626"},"modified":"2021-10-06T07:39:09","modified_gmt":"2021-10-06T14:39:09","slug":"perfect-font-fallbacks","status":"publish","type":"chapters","link":"https:\/\/css-tricks.com\/books\/greatest-css-tricks\/perfect-font-fallbacks\/","title":{"rendered":"Perfect Font Fallbacks"},"content":{"rendered":"\n

When you load custom fonts on the web, a responsible way to do that is to make sure the @font-face<\/a><\/code> declaration uses the property font-display<\/a><\/code> set to a value like swap<\/code> or optional<\/code>. <\/p>\n\n\n\n

@font-face {\n  font-family: 'MyWebFont'; \/* Define the custom font name *\/\n  src:  url('myfont.woff2') format('woff2'); \/* Define where the font can be downloaded *\/\n  font-display: swap; \/* Define how the browser behaves during download *\/\n}<\/code><\/pre>\n\n\n\n

With that in place, there will be no delay<\/em> for a user loading your page before they see text. That’s great for performance. But it comes with a design tradeoff, the user will see FOUT or “Flash of Unstyled Text”. Meaning they’ll see the page load with one font, the font will load, then the page will flip out that font for the new one, causing a bit of visual disruption and likely a bit of reflow.<\/p>\n\n\n\n

This trick is about minimizing<\/em> that disruption and reflow!<\/p>\n\n\n\n

This trick comes by way of Glen Maddern<\/a> who published a screencast about this at Front End Center<\/a> who uses Monica Dinculescu’s Font style matcher<\/a> combined with Bram Stein’s Font Face Observer<\/a> library. <\/p>\n\n\n\n

Let’s say you load up a font from Google Fonts. Here I’ll use Rubik<\/a> in two weights:<\/p>\n\n\n\n

@import url(\"https:\/\/fonts.googleapis.com\/css2?family=Rubik:wght@400;900&display=swap\");<\/code><\/pre>\n\n\n\n

At the end of that URL, by default, you’ll see &display=swap<\/code> which is how they make sure font-display: swap;<\/code> is in the @font-face<\/code> declaration.<\/p>\n\n\n\n

On a slow connection, this is how a simple page with text will load:<\/p>\n\n\n\n

\n
First you’ll see the fallback typography, then the custom fonts will load and you’ll see the typography swap to use those.<\/figcaption><\/figure>\n\n\n\n

See the swap<\/strong>? Remember that’s good<\/em>, in a way, because at least the text is visible to begin with. But the swap is pretty heavy-handed. It feels visually disruptive.<\/p>\n<\/div><\/div>\n\n\n\n

\n
\n
\n
\"\"<\/figure>\n<\/div>\n\n\n\n
\n

Before the page’s first paint. <\/p>\n<\/div>\n<\/div>\n<\/div><\/div>\n\n\n\n

\n
\n
\n
\"\"<\/figure>\n<\/div>\n\n\n\n
\n

Page will paint with fallback fonts while the custom font loads. font-display: swap<\/code> has an “extremely small block period”, so unless the font is in the browser cache, you’ll almost certainly see the swap happen (FOUT). <\/p>\n<\/div>\n<\/div>\n<\/div><\/div>\n\n\n\n

\n
\n
\n
\"\"<\/figure>\n<\/div>\n\n\n\n
\n

Once the custom font loads, the page will repaint, causing reflow and visually jerkiness. Unless we fix it! <\/p>\n<\/div>\n<\/div>\n<\/div><\/div>\n\n\n\n

Let’s fix that.<\/p>\n\n\n\n

Using Font style matcher<\/a> tool, we can lay the two fonts on top of each other and see how different Rubik and the fallback font are. <\/p>\n\n\n\n

\"\"<\/figure><\/div>\n\n\n\n

Note I’m using system-ui<\/code> as the fallback font here. You’ll want to use a classic “web-safe” font for a fallback, like Georgia, Times New Roman, Arial, Tahoma, Verdana, etc. The vast majority of computers have those installed by default so they are safe fallbacks.<\/p>\n\n\n\n

In our case, these two fonts have a pretty much identical “x-height” already (note the height of the red and black lowercase letters above). If they didn’t, we’d end up having to tweak the font-size and line-height to match. But thankfully for us, just a tweak to letter-spacing will get them very close. <\/p>\n\n\n\n

Adjusting the callback to using letter-spacing: 0.55px;<\/code> gets them sizing very close! <\/p>\n\n\n\n

\"\"<\/figure>\n\n\n\n

Now the trick is to give ourselves the ability apply this styling only before the font loads<\/em>. So let’s make it the default style, then have a body class that tells us the font is loaded and remove the alterations:<\/p>\n\n\n\n

body {\n  font-family: \"Rubik\", system-ui, sans-serif;\n  letter-spacing: 0.55px;\n}\nbody.font-loaded {\n  letter-spacing: 0px;\n}<\/code><\/pre>\n\n\n\n

But how do you get that font-loaded<\/code> class? The Font Face Observer library<\/a> makes it very easy and cross-browser friendly. With that library in place, it’s a few lines of JavaScript to adjust the class:<\/p>\n\n\n\n

const font = new FontFaceObserver(\"Rubik\", {\n  weight: 400\n});\n\nfont.load().then(function() {\n  document.body.classList.add(\"font-loaded\");\n});<\/code><\/pre>\n\n\n\n

Now see how much smoother and less disruptive the font loading experience is:<\/p>\n\n\n\n

\n