August 11, 2011
Typekit is based on the standard CSS
@font-face property. But using fonts on the web is about more than just the code we use. Just like video or audio, fonts pose challenges when you need them to work consistently and reliably across the entire ecosystem of browsers and devices. Fonts can also take time to download, and — unlike other kinds of rich media — browsers don’t provide any built-in way to detect when a font has loaded or, more importantly, when it has failed to load. That’s important to know so designers can plan for and apply fallback styles when fonts are unavailable. If a web font fails to load and there’s no fallback, web pages designed around the font can look different, even ugly, and sophisticated web app interfaces can break entirely.
- It identifies the user’s browser, using fast, accurate, client-side detection methods.
- It loads the correct
@font-facerules for that browser by injecting a link to it into the DOM.
- Finally, it observes the browser as it loads the fonts themselves, firing font events to provide better control over fallbacks and to help prevent problems like the dreaded flash of unstyled text (or FOUT).
In this post, I’d like to give you a brief introduction to Kit JS, explaining why it’s there and how it works. Let’s get started by first recapping what it is we’re talking about when we talk about “kits.”
Anatomy of a kit
<head> of your HTML document:
As I mentioned, a kit is comprised of fonts and stylesheets. Specifically, it consists of:
- Font files for each variation (i.e. weight or style) in your kit, such as Nimbus Sans Extended Light Italic, in every format we support: EOT (for Internet Explorer 8 or earlier), raw TrueType or PostScript fonts (for Safari, iOS, and Android), and WOFF (for everyone else).
@font-facestylesheet covering all of the variations in your kit, for each of the OS/browser combinations we support. For browsers that support it, we embed the actual raw font data directly in the CSS, which helps performance by reducing the number of external requests needed to render the page.
- The kit JS file itself, which ties everything together by handling the actual font loading process.
When you publish a kit, all of these items are copied from our library or generated from your kit settings and published as static files to our content delivery network (CDN). A CDN is a network of web servers placed in several regions around the world, each containing a local, cached copy of the kit so users in that region can download kits more quickly.
Fast, scalable browser detection
Using just CSS
@font-face technique uses one stylesheet to link to every available font format, but is written in such a way that each browser will only load the formats it can use. As Chris Coyier wrote back in 2009, “the whole reason that web standards exist is so that we don’t have to write specific code for specific environments.” The bulletproof method is viewed as a web standards-friendly compromise: while it does incorporate some browser-specific hacks, it saves you from having to write, and serve, a different stylesheet for each browser.
The biggest drawback to this method is that it can be brittle: the very same hacks that make it possible to target specific browsers using just CSS may stop working as the browser landscape changes, forcing developers to continually monitor and update their code for regressions that could prevent fonts from loading. This has already happened several times since 2009, and likely will continue to happen for as long as new browsers are being developed.
Using this method you also have less control over which browsers you’re targeting. In one sense this is a feature, not a bug. The bulletproof method isn’t about targeting specific browsers, but rather providing browsers with content in a variety of formats from which they can use whichever is most appropriate. However, when it comes to fonts, the file format is just one part of the story. As we’ve written in the past, every font file contains vector outlines in either TrueType or PostScript format. TrueType outlines are more broadly supported, but some fonts render dramatically better on certain platforms with PostScript outlines. The bulletproof method is effective at delivering fonts in the right file format, but it has no reliable way of delivering different outline formats. For that, you have to use browser detection.
Using server-side browser detection
As the name would imply, browser detection is a kind of content negotiation where different content is served to different users based on information about the browser software they’re using — such as its name, version number, and operating system. It’s frequently executed on a web server, by using the user agent string sent along with every request to identify the browser, then running a simple algorithm to identify and send back the appropriate content. For example, upon receiving a request from someone using Internet Explorer 8, a server set up for this kind of content negotiation would see IE8’s user agent string and decide to send back a stylesheet with fonts in EOT format.
All hosted web font services, including us, use some form of browser detection because it’s more reliable, especially for supporting legacy browsers whose requirements are unlikely to change.
Server-side browser detection has been done on the web for years — in fact, the ability to serve different content to different applications is one reason why the web even has user agent strings. But for our purposes it has several drawbacks, perhaps the biggest of which is that it’s relatively slow. Our font-serving network currently handles about 2,000 requests per second, and running an identifying algorithm on each one of those is very hard to do without affecting performance.
Any time a web browser requests content from a server there is always some delay — or latency — between the moment when a browser submits the request and when the server actually starts sending back a response (that is, the file your browser asked for). During that period browsers are just waiting, and users are left with either a half-rendered page or nothing at all. For web sites to feel fast, latency needs to be as small as possible.
With regard to serving fonts, there are three main sources of latency:
- Waiting for requests to travel over the network. Servers that are further away will take longer to receive or respond to requests.
- Waiting for a server to perform any tasks it needs to perform before sending the response. This can include authentication, referrer checking, server-side browser detection, or setting up an encrypted connection for SSL-enabled sites.
- Waiting for other users’ requests to finish. If a server is under heavy load, new incoming requests can be put on hold until older requests have been processed.
If you can, imagine a web server as a coffee shop where none of the customers know what they want and the barista has to ask a bunch of questions to help them decide. That’s what server-side detection is like. Browsers request something very generic, like
coffee.css, and it’s up to the server to figure out what kind of coffee they need. This is fine as long as the shop isn’t crowded. But when there’s a line, having to go through this negotiation process for every customer will slow things down and frustrate everyone.
But if every customer in line can come in already knowing exactly what they want — not just
Document mode targeting for IE users
A great example of this is document mode targeting for newer versions of Internet Explorer. To maintain backward compatibility with older websites that may depend on rendering quirks specific to older versions of IE, newer IEs have the ability to switch into different “document modes” which mimic those older versions’ rendering behavior. In other words, if IE9 detects a web site that’s not compatible with its regular “standards” mode, it will enter a “quirks” mode where it will still identify itself as IE9 in its user agent string but behave as if it were IE7.
This is relevant because older versions of Internet Explorer have several quirks that directly impact font loading and rendering, such as the inability to recognize more than four variations at a time of a single
@font-face family. Or worse: if you inadvertently send a WOFF file to IE9 in quirks mode, the font will fail to render at all. In order to best serve fonts to IE9 in quirks mode, we need to be able to detect it and enable features like variation-specific family names. But at the same time, we also want to be able to identify when IE9 is in standards mode, so users can take advantage of its better standards compliance and WOFF support.
Font loading, with font events
Having figured out which fonts to ask for, it’s time for Kit JS to load those fonts. The process for this is pretty simple.
At this point, your browser has requested and started to load the CSS stylesheet containing your
@font-face declarations. The next step is to begin loading the fonts themselves, which ordinarily will happen after the stylesheet has finished loading, as soon as the browser tries to render text that’s been styled with a web font.
- First, the loader fires the “loading” font event, to indicate the process has started. Each time an event is “fired,” the loader sets or changes a CSS class name on the
<html>element corresponding to the name of event. So for the “loading” event, if you inspected your page in Firebug or the Safari/Chrome Web Inspector, you’d see the
<html>tag has the class name
- Next, the loader inserts an invisible HTML
<span>element into the page for each font variation being loaded. Each
- Finally, once the loader has tested every variation in the kit, and as long as at least one variation has loaded, the “active” font event is fired for the whole kit. At this point the process is complete, and all fonts that have loaded should now be displayed on your page.