Web Fonts Performance by Bram Stein
This is the third talk in a set of three talks on Visual Performance delivered on April 1, 2016 at Fronteers Spring Conference in Amsterdam
Web fonts are great. They are also be really bad for performance because they block rendering. You may have experienced this on a slow cellular network. Staring at a blank page is no fun, especially when content has already loaded.
In this talk Bram explores why browser have placed fonts on the critical path and how we can work around this while still delivering a good user experience. He also has a look at what the future will bring to web font performance: preloading hints and the new font-display property.
(male speaker) OK, so miraculously, we are still just about on time but just about on time.
So we should rattle along quickly.
Keep the questions coming in because, as I said, we're going to have a chance to do some Q&A later but we're ready for our next speaker.
So we've had a couple of talks on animation and now we're going to have a talk about a different aspect of visual performance and that's to do with web fonts, which I'm particularly keen to hear about because it's felt like a pain point for a really long time and things seem to be getting much, much better when it comes to typography on the web.
So our next speaker, Bram Stein, has been working at Typekit for some time now helping us bring typography to our websites and he's going to talk a little bit about that topic.
So please make him very welcome.
Bram Stein! [APPLAUSE] (bram stein) Thank you.
Is it on? Yeah.
So I want to start by asking you all a question.
How many of you have used web fonts before? Can you please raise your hand? OK, that's pretty much everybody.
So you're not alone.
About 60% of the top 1000 websites monitored by the HTTP Archive use web fonts and this is up from almost zero in about 2010.
So it's really an amazing increase in the last couple of years and not only are web developer using web fonts more often, they're also using more fonts per page.
About three to four fonts on average.
Now if you combine that with the average font size of about 120 kilobytes, this means that the average website is loading about four hundred kilobytes per page, which is crazy.
Now, of course you might think, well, I have images larger than that.
Why do I care? But unlike images, web fonts are used to render the main content-- the text, right? So how browsers load web fonts is very important to the perceived and actual performance of your website.
So I want to do a little experiment-- sorry about that.
I'm going to load this site in two different browsers with different font loading behavior and afterwords, I want you to tell me which one gave you the best user experience.
So very simple websites.
Just two web fonts-- one for the headline and the other one for the body of text.
So let's start with browser A. It's
going to go pretty quickly.
So I'll just type in the address and then it goes.
There we go.
And it's loaded.
So does everybody get that? Yeah, good.
So let's move on to browser B.
Type in the address and it loads.
So now my question is, who thinks browser A gave the best user experience? Please raise your hand.
That's a couple of people.
Who thinks browser B gave the best user experience? All right.
We'll work on that.
So I want to do this again but this time using a mobile device on a mobile network connection and I'm going to ask you the same question afterwards.
So this is my mobile here.
I type in the address.
It's loading some content and it loads.
That was only 10 seconds by the way.
So I want to do the same with browser B.
So I type and the address and there we go.
So same question-- who thinks browser A gave the better user experience? Who still thinks processor B gave the better user experience? You can raise your hand it's fine.
You over there.
So what I've just shown you is the difference between the flash of unstyled text and the flash of invisible text and the flash of unstyled text shows fallback fonts while webfonts are loading and a flash of invisible text hides the text while web fonts are loading.
Now most browsers use the flash of invisible text.
Chrome, Firefox, Safari, and opera all use the flash of invisible texts.
Microsoft went a different route and they use the flash of unstyled text.
Now the flash of invisible text, and I'm going to say these words a lot, has one big problem and that is if your web fonts don't load or take a very long time to load, you're just staring at this blank page.
So browsers also thought this was a problem and they started adding a timeout.
So Chrome, Firefox, and Opera use a three-second timeout.
Safari, unfortunately, doesn't use a timeout so if your webfonts fail to load or they take 30 seconds to load, you're just staring at this blank white page.
Now I think this is a really, really big problem.
Browsers should not hide text while they're downloading web fonts and definitely not for more than 3 seconds.
I think even three seconds is too much.
So I believe that the flash of unstyled text should be the default behavior in browsers.
We really need to take web fonts off the critical rendering path and turn them into a progressive enhancement because that's what the flash of unstyled text is.
It's a progressive enhancement so you show the fallback fonts first and then the experience is enhanced with the web fonts.
Now, I really dislike the name-- the flash of unstyled contents because it implies that you can't style that text, and you can.
You can set the size.
You can choose different fonts depending on the platform.
You can use different weights, et cetera.
So fallback fonts are really your baseline experience and you should style those.
So I think it should have been called the flash of fallback text but I think it's too late to change now but let's take a look and see how browsers load web fonts.
You're all familiar with the font face syntax so I'm just going to skip that but when is a website actually downloaded? So if you look at the CSS specification, and I'm kind of paraphrasing here, it's not an exact quote, but it says a web font is only downloaded when it is used in a CSS selector that matches a DOM connected node, which is kind of a mouthful but what it means is that web fonts are not downloaded unless they are actually used.
There are some exceptions for very old browsers but I'm just going to ignore those.
So for example, this paragraph that I have here with some text in it-- and in my CSS, I have a font face rule-- but this font is not used anywhere so it will not be downloaded unless I do this.
I add the web font to a CSS selector in this font stack here and then the browser will go and download that font but this is basically lazy loading, right? And this is really great because, why would you download a resource that you don't need? So I want to look at this from a network timing perspective.
So when a browser starts loading a page, it will grab the HTML file first and it will start parsing it as soon as it has it and then it will discover a reference to a CSS file and it will also start downloading that and once both of those have finished downloading, the browser can construct a DOM and a CSS object model and at that point, it will have enough information to figure out which fonts it needs because it needs to look at all the nodes and it needs to look at all the CSS.
So in this case, it will have figured out that it needs to download two fonts here and it will start downloading those in parallel.
So at some point, the font will load.
Font A will render immediately and font B is still loading and after a while, it will also load and render.
So it's a very simple site.
So we're just going to assume that I'm done loading here.
So I'm really talking about this area here.
From the moment that web fonts start loading until they render, and that's where the flash of unstyled text and the flash of invisible text happen and web developers do not have control over this but luckily, that is about to change.
And it's about to change because of a new CSS property called font-display and you add font-display to your font face rules and it will change the loading behavior for that particular web font and font-display takes five values.
It takes auto, block, swap, fallback, and optional.
Auto is the default behavior and it's one of the other four.
Block will hide the text while web fonts are loading with a timeout so it's identical to the flash of invisible text.
So it is probably going to be the default behavior in Chrome, Firefox, and Opera.
Now swap will render web fonts immediately-- sorry, no.
swap will render fallback fonts immediately and render the web fonts when they actually load.
Now, this could be a very long time.
For example, let's assume you're sitting in a train.
You're reading an article and 30 seconds later, the web fonts pop in.
That's going to be very distracting because it will cause a Reflow, right? So you don't want that to happen and that's where the fallback property comes in.
It's very similar to swap, but it has a timeout.
So if the web font loads within that timeout, it will render immediately but if it loads outside that timeout it will not render and you will continue to see the fallback fonts.
Of course, the browser will continue to download the web font and it will be available in the next request for that site or page.
Now, the last value is a bit special.
It's called optional and it will only show web fonts if they are cached but it was still load the web font in the background.
So for example, if you go to a website for the very first time and your cache is clean, then you will see fallback fonts but your browser will still download the web font in the background.
So if you go to another page on the same site, you will immediately see the web font rendered.
Now unfortunately, this very nice property is not available in browsers yet.
It's in Chrome and Opera behind a flag so I think it will go into stable very soon but it's not something you can use right now.
I've been talking about how to optimize the loading behavior in this yellow area here but you can also make this area smaller by making the fonts load faster and you can do that using compression or using subsetting.
So for example, you should always use WOFF as a font format or WOFF2 because it compresses really well and you should always subset your fonts because font contain a lot of characters that you might not need.
So you can remove those and it makes the font file much smaller and of course loads everything much faster.
So this will improve the performance somewhat but it overlooks one major area of optimization and that is this area and that is the point at which the browser decides whether or not to download a web font.
So if you recall, a browser needs both the DOM and CSS object model to even figure out what it needs to download and this is the lazy loading, right? So it's kind of working against us now and I think lazy loading is bad for performance, at least for web fonts.
It makes lot of sense for images and videos, et cetera but not so much for web fonts because web fonts are used to render the main content.
Your headlines, your body text and sometimes, if you're crazy, your icons.
So why shoot the browser lazy loading things if you already know, as a web developer, that they are going to be used, right? So what we need is a way to tell the browser that this is an important resource and you should fetch it as soon as possible.
Now we can do that with preload instructions and preload instructions are a declarative way to instruct a browser that a resource is important and should be loaded as soon as possible, or at least with high priority.
So, preload instructions can be used in two ways-- as a link element or as an HTTP header and you basically point it to your resource-- in this case, that's the font file here-- and you tell it what kind of resource it is.
So here, I've told it that it's a font and because of that, the browser can prioritize it appropriately because it will know that the font is render blocking.
And that means you can go from this, where the fonts are only downloaded after the DOM and CSS object model are constructed to this, where your fonts are downloaded in parallel with other important resources and this is a huge optimization and in this case, we almost doubled our performance.
Now I have to give you some bad news.
Preload is not supported by any browser.
I think it will ship in Chrome in mid April, maybe in Opera as well.
Mathias is nodding so yes.
Great-- but what can we do until then? Right? So what we can do is what all web developers do.
So there is a new-- also new-- API called the CSS Font Loading API, and this API provides methods to load and unload and gives you font events, et cetera.
So it's a really nice way to manipulate web fonts on your page.
So using this API, you can explicitly force the browser to load a web font.
So you can do that by creating a new font face instance.
You give it a family name.
You point it to your web font files and then you call load and load will force the browser to load it at that particular moment.
The load method returns a promise and that promise is resolved when the web font loads or it's rejected if it fails to load.
So if you in-line this snippet in your HTML page, you will force the browser to download web fonts much earlier because it will circumvent the normal lazy loading behavior.
So the results of doing this is very similar to preload instructions.
Now, you can also use this API to simulate the flash of unstyled text or the flash of invisible text in all browsers.
So you can do that by adding three classes.
Fonts loading, fonts loaded, and fonts failed.
You can name them anything you want but that's what I went with.
So you add the loading class as soon as you start loading web fonts and then, when the fonts loads, you replace that class with a loaded class or, if they fail to load, you replace it with failed.
And then you can implement the flash of invisible text or the flash of unstyled text with some very simple CSS and a trick to simulating the flash of unstyled text in all browsers is to only tell the browser about a font when it has already loaded it.
So you can do that by making the fallback font default.
So you will not get the invisible text and that's because the CSS object model doesn't know about that font until you actually add it.
So this is a really nice way of creating a consistent behavior in all browsers.
If you really like the flash of invisible text, you can do something similar here and that is by hiding your content while fonts are loading and then showing it again when they load or, also importantly, if they fail to load because otherwise you are like Safari and you just get an empty page.
So you can also add on to these things.
So it's a really nice API to have.
Now, the browser support for it is pretty decent.
Chrome, Opera, Firefox, Androids all have it.
Safari has started working on it.
I hope it will come in some sort of update in a year or two.
We don't have any signals yet from Microsoft but I think they will implement it as well but what can we do until then, right? So, I've written a small library called Font Face Observer and it polyfills the core functionality of the native API and in modern browsers, it will use the native API and on other browsers, it will fall back to some other way of detecting the font load and you can pretty much drop it into your page and use it as you would the native API.
So you create a new instance of Font Face Observer, you pass it a family name, et cetera, and then you call load.
So this is a really nice way to be able to do this in all browsers.
So to sum up, when optimizing web fonts, you should focus on two areas.
This is the moment when the font load starts and the load itself.
You should try to move to the load start as close as possible to the HTML and avoid the browser lazy loading your critical fonts.
You should also try to make the font file as small as possible by using the right font format like WOFF or WOFF2 and by subsetting your font, thus removing characters and OpenType features, sometimes, that you don't need.
Secondly, you should always use a font loading strategy.
Don't rely on the browser defaults because they are horrible and hiding content is a really bad experience for your users.
So use the Font Loading API or my polyfill to create consistent font loading behavior in all browsers and finally, remember that web fonts are an enhancement.
Your site should be usable without them.
So pick appropriate fallback fonts and style them so your site looks OK because that's your baseline experience, and then web fonts will enhance that experience.
So thank you.
If you have any questions or if you want to stay up-to-date with the latest news around web fonts, you can follow me on Twitter or ask me questions I think right now.