Flash of Faux Text—still more on Font Loading
Summary: After we’ve defeated the Flash of Invisible Text, we also want to decrease the amount of reflow a user sees when the font switches from fallback to
@font-face. We already split our CSS into critical and deferred, why not fonts? Prioritize your fonts by loading only your roman (or plain/normal) font first, which will cause the browser to use font synthesis to create faux-bold/italic versions automatically. Next, trigger requests for the real bold and italic fonts. This will minimize the amount of reflow while the user is reading the content.
By now you’re no doubt tired of hearing my endless blathering about the perils of using the default font loading behavior. The phenomenon known as the Flash of Invisible Text, or hiding the content while the Web font loads, is terrible for the perceived performance of the web.
One technique to solve this problem uses the CSS Font Loading API to add classes signifying that our fonts have successfully loaded. We can then use those classes in our CSS selectors to kill the FOIT altogether. Usually this involves a class (and a repaint/reflow) for each individual font that loads. You can read more about this technique at Dev.Opera.
Another technique to solve this problem involves embedding fonts as Data URIs inside of a single CSS file that we load asynchronously. This method groups our fonts into one large request that only triggers a single combined repaint/reflow for your font bundle. You can read more about this technique in the Filament Group Lab.
Of course, the amount of repaint/reflow is really up to you and is not tied to either method. You could use a single class addition (one repaint/reflow) when all of the fonts have successfully loaded using the CSS Font Loading API or you could load individual CSS files with Data URIs (multiple repaints/reflows). If you’d like code snippets showing how to achieve this, please ask me on twitter.
An important piece of this puzzle is that the fonts that we choose to load are sometimes different weights and styles of the same typeface. For example, this site uses Lato (with a roman font-weight, 400, and normal font-style), but also Lato (bold), Lato (italic), and Lato (bold italic). This is four different font files.
|Lato Font Format||Combined Size of Four Variants|
Remember that after we’ve defeated the Flash of Invisible Text, we’ve decreased the delay in which a user must suffer before they can become a reader of our content. So what happens when the web fonts finish loading while the user is in the middle of reading a particularly juicy paragraph on our page? If your fallback font has a different
line-height than your web font, it could reflow your user’s viewport to a completely different location in the text.
Of course, one way to attempt to solve this problem is to fiddle with the
letter-spacing of your fallback font. Jason Pamental has covered this topic extensively in his Fonts.com post: Web Font Tune-up Time: A Fun Font Fallback Event.
Prioritize Your Fonts
However, here I’m proposing that we split our font bundles into two separate packages. First, we would just load the Roman weight (
font-weight: normal; and
font-style: normal;). This would cause the biggest reflow to our content. This will also trigger the browser to use
font-synthesis to create faux-bold and faux-italic versions of our web font. We’ll use these fake variants while we trigger the second request for our real Bold, Italic, and Bold Italic variants.
|Lato Font Format||Stage 1:|
Combined Size of Bold, Italic, Bold Italic Variants
|TrueType||63 KB||191 KB|
|WOFF||31 KB||97 KB|
|WOFF2||25 KB||77 KB|
On slower connections, this purposefully moves from a single delayed reflow to a two-stage reflow, the first of which should be the most jarring to our reader. We move that first reflow up the waterfall to happen as soon as possible and the likely more minimal faux-bold/italic to real-bold/italic reflow would happen later.
Try it out on this page, I’ve implemented this approach on my site. Open up the Chrome DevTools with device mode enabled and throttle your Network connection down to EDGE. Try reloading this page and watch the three stages:
Important Note: you have to use different aliased
font-family names for each
@font-face block used here. If not, your second stage (the Flash of Faux-Text) will suffer the same Flash of Invisible Text problem that we’re trying to avoid with font loading.
This does make one wonder if it would be useful to have Browsers use this internally by making bold and italic variants a lower priority than roman. Blink already uses a similar approach with non-applicable
media queries on
<link> elements for CSS, why not fonts?