And here is our Fontdeck Fontstack
This paragraph should be displayed in Proxima Nova Regular.
This paragraph should be displayed in Proxima Nova Bold.
This paragraph should be displayed in Proxima Nova Italic.
This paragraph should be displayed in Skolar Regular.
This paragraph should be displayed in Skolar Bold.
This paragraph should be displayed in Skolar Italic.
Test? What test? How test? Why test?
This is a test-case for seeing how fonts are being handled by different browsers when downloaded in the background with FontFaceObserver (by Bram Stein) and switched in after all downloads succeeded. I also want to proof to myself that there will be no flash of unstyled or invisible or fallback or whatever unwanted text on subsequent visits when adding sessionStorage to this technique.
I chose Bram's script over Web Font Loader (by Typekit), FontFaceOnload (by Zach Leatherman) and Zenfonts (by Gabor Lenard) because it was the first to draw my attention to it. It also comes very close to the upcoming Font Loading API (on a syntactical level that is), hence until support for the API broadens I'll go with the Observer script.
What's all in my
Building this little test-suite I tried to make the whole thing as performant as possible. I prefetch the DNS of the Fontdeck CDN with
rel="dns-prefetch" on the first
<link> element and for supporting browsers (currently only Firefox) I do the complete handshake (DNS, TCP, TLS) with
rel="preconnect" on the second one. The third measure to improve the whole experience is hinting the browser to download the Fontdeck stylesheet itself asynchronously to the HTML by putting
rel="subresource" on a third
<link> element. All of this happens just after I've set the charset and the title for this little page – just after the opening
I would have loved to prefetch specific font-files but Fontdeck won't let me without requesting the stylesheet containing those – which then results in double-downloads for all font-files – which means:"Nope!". I also would have liked to leave the stylesheet and take it's containing
@font-face rules but again to no avail. So, while I can't work around downloading the Fontdeck stylesheet, I can only do my best to ensure it's downloaded as soon as possible.
Those pre-thingies are then followed by a very short script. This script checks if there's the keyword "fontsloaded" in the visitor's sessionStorage. If it's there, I'll instantly add the class
fonts-loaded on the
<html> element and there are your custom web fonts without a flash of invisible or fallback text.
Wondering how that keyword got into the visitor's sessionStorage? Follow me inconspiciuously!
FontFaceObserver & sessionStorage to prevent FO*T
The FontFaceObserver script will monitor when a web font is applied to the page and notify me. So, as soon as those custom-fonts are completely downloaded by the user on his first visit (empty cache and sessionStorage) a few things are happening:
- The class "fonts-loaded" is added on the
- the custom fonts are instantly switched in
- and (when supported) the keyword "fontsloaded" is written into the visitor's sessionStorage.
- If the fonts (for whatever reason) fail to load, nothing happens, nothing fails.
The result: on first view (empty cache) we do not have renderblocking fonts or scripts or stylesheets just like before. We are loading the font-files in the back. We have a Flash of Fallback Text until the fonts are completely downloaded.
… and on subsequent visits …
A visitor reloading the page or jumping to other pages on my website already has the keyword "fontsloaded" in his sessionStorage and as soon as my check-script (see above) sees that, it adds the "fonts-loaded" class on the
Why sessionStorage? Why not localStorage or Server-Side-Injects?
localStorage – the pickpocket trick
There's something unsettling to me about localStorage being so persistent. It's a little like evercookie. If you're unfamiliar with localStorage let me explain it to you the way I see it:
Suppose you have a friend coming over to your house and you need him or her or it to carry something around for you. While he's not looking, you secretly sneak something into his pocket without him noticing. In the future, whenever he'll return to your house, he'll already have it with him – until he'll be changeing his pants I suppose, which results in you again secretly sneaking this thing into his pocket.
The problems start when his pocket is already full as you won't be able to put additional stuff in there – no luck this time. Another issue arises if your friend changed his pants every time he visited you because the effect of him having this something with him already is nullified by his cleanliness.
The biggest problem to me though is this: what if a lot of your friend's friends decided to do the same thing as you do? Right, at some point your friend will find himself on the street with his pants dropping because of the weight of all the things you and his other "friends" put into his pockets.
Back to the nerdcave this means: if the localStorage of your page's visitor was full, you can't write something into it. If a user was aware of his Storage filling up, he'll empty it occasionally or in a set timeframe thus you will have to put your files there again and again and again. And then think for yourself: how'd you like it, if people stuffed your pants with their stuff and you only realize when your pants drop in the streets because the weight is tearing them down?
While I'm at it: at Smashing we are putting six base64-encoded fonts (otherwise the whole thing won't work) into our user's localStorage. That's 600kbs. As this technique is used across the board, we do the same in our shop and on SmashingConference websites, resulting in huge numbers of data being pushed into our user's pockets without them realizing only to prevent a Flash of text.
Server Side Injects - Yes but No but Yes but No
Much better than localStorage and yet. What if the cookie is still in the user's cache but the fonts are gone? Sure, fallback text is no broken text but with SSI the class would still be set in the HTML and the CSS rules would be applied and . I don't know if that's 100% reliable.
Also a cookie is a cookie is a cookie. It's data being put into the user's cache, which originally shouldn't be there.
sessionStorage to the rescue
The main advantage of sessionStorage are all advantages localStorage comes with. The only thing sessionStorage won't do is leave the data I put there for good. As soon as a user decides to close his session by shutting down the browser or his computer or whatever closes sessions… the sessionStorage will be cleared.
The Nay-Sayers among you will now say:"Oh, wait a minute! localStorage ensured that even when someone came back after the session expired …" Yes! I know! But here we are again back at the point where we use our users to carry stuff around they don't need – but we might.
Can I use it now?
This technique is built with progressive enhancement in mind, thus you can use it right away.
Looking at this caniuse table you can see in which browsers this technique works to its full extent.
That means currently (15.10.2015) in Firefox there will be no FOUT or FOIT on subsequent visits or reloading the same page as long as the session won't expire.