Back in the dark ages of the web, before the introduction of CSS 3, browsers could only display fonts available on the client system. Thanks to the introduction of @font-face and its now widespread support, this is over. Now, Web designers have access to a large selection of fonts and most users will be able to appreciate them.

Typekit provides a subscription-based library of hosted, high-quality fonts to use on websites. I expose here my own, mostly uneducated1 on the typographic side, look on this service.

Fonts embedding 101

There are several ways to embed fonts for the Web. Let’s have a look at the most common ones.

Self-hosting and @font-face

A first possibility is to host fonts yourself and provide a CSS file with the appropriate @font-face declarations. There are five important things to know:

  1. There is no common format. You will need to host different versions of the same font to support most browsers.
  2. Of course, Internet Explorer comes with its own quirks that you need to work around.
  3. Most browsers enforce a same origin policy. You need to serve your fonts from the same domain as the requesting page. You can circumvent such a limitation by enabling cross-origin resource sharing: add an Access-Control-Allow-Origin header when serving fonts.
  4. Fonts are unlikely to change. To leverage caching, configure an explicit expiration date far in the future. Something like 30 days.
  5. There is no MIME types registered for fonts. Don’t bother setting one, even if Google Chrome likes to complain about them in the console.

One great resource is Font Squirrel which provides kits with fonts and the appropriate CSS declarations. You choose your font family (or upload one of your own), the formats that you want to use (check them all) and you will get a ZIP archive with all fonts and a CSS file. You may want to tweak it a bit to enable automatic use of the bold variant when needed and to use a local copy if available. For example, if you choose Crimson, an oldstyle typeface, you may want to declare the bold version with font-weight: bold and add local names:

@font-face {
    font-family: 'Crimson';
    src: url('Crimson-Bold-webfont.eot');
    src: local('Crimson Bold'), local('Crimson Text Bold'),
         url('Crimson-Bold-webfont.eot?#iefix') format('embedded-opentype'),
         url('Crimson-Bold-webfont.woff') format('woff'),
         url('Crimson-Bold-webfont.ttf') format('truetype'),
         url('Crimson-Bold-webfont.svg#CrimsonBold') format('svg');
    font-weight: bold;
    font-style: normal;

Google Web Fonts

For open-source fonts, you can also use Google Web Fonts instead of hosting the files yourself. Build a collection of fonts and choose the variants that you want to use for each of them. You will then be provided with a code snippet to paste in the <head> element of your page. For example, for Crimson:

<link href=',700,400italic'
      rel='stylesheet' type='text/css'>

Why choose this solution over self-hosting?

  1. You don’t use your own bandwith to serve fonts. This may be important if you pay for it.
  2. Google uses its own CDN to serve fonts. If you don’t use a CDN yourself, this can be a major performance improvement.
  3. Caching is more efficient since many sites will use the same font.
  4. It’s easier to do it right.

Unless you are concerned about Google tracking your users2, I think this is a better solution than self-hosting. Quick delivery of fonts is pretty important since content rendering can be delayed by them.

If you would like to use commercial fonts, like Adobe Minion Pro or Paratype Futura PT, a convenient option is to rent them. Typekit is service providing such an option. Pricing depends on volume and the number of fonts you want to have access to. There is a free plan and the first paid plan is US$24.99 per year. Other similar services exist. Feel free to compare.

Once registered, the workflow is similar to Google Web Fonts. You choose your fonts, assemble them in a “kit” and publish it. Instead of directly providing a CSS file, Typekit provides some Javascript which will in turn download a CSS file containing all fonts in the appropriate format for the browser (on most browsers, fonts are inlined).

<script type="text/javascript"
<script type="text/javascript">

Typekit: the good parts…

So, why choose Typekit? First, there is the very large choice of fonts. The first paid plan comes with more than 500 fonts from about 80 foundries. There are about a hundred serif fonts and a couple hundred sans serif ones.

Typekit provides a convenient interface to browse fonts. You can either search fonts by name or by characteristics. In the last case, you can filter on properties like the classification (serif, sans serif, decorative, etc.), weight, width, contrast, x-height, recommended use or language support. Once a font is selected and added to your kit, you can easily choose what variants you want. The kit total size is displayed.

If you happen to meet any problem or have some questions, Typekit support is very responsive. I got clear and crisp answers in less than a few hours in the week-end.

…and the bad ones

Unfortunately, Typekit has also some shortcomings. One of the most commonly cited is that Javascript is mandatory. No Javascript, no fonts. If you want to display the appropriate fonts for users without Javascript, you should just not use Typekit.

Not free, nor free

While Typekit offers some open-source fonts, most of them are not. Moreover, only a few of them are available in the free plan. While I would prefer to use open-source fonts, I don’t consider this a strong requirement: if you are dissatisfied for some reason, switching back to open-source fonts (or another service) is fast and easy.

I like serif fonts a lot and they get less attention from the FLOSS world: it is difficult to find one whose readability on screen is excellent. Droid Serif is a nice font but is a bit too black for me. Maybe Cardo would be a good match.

You may disagree and in this case, Google Web Fonts or self-hosting seem the perfect match.

Difficulty to choose and test fonts

While browsing fonts is easy and smooth, choosing and testing them is another matter. For example, Typekit is missing some interface to compare font rendering for whole paragraphs at a given size. It also misses a way to check how two fonts render together, one for headings and one for paragraphs. Here, Google Web Fonts is more versatile.

Things become really difficult when you want to test your selected fonts for real. Except if you have invested into one of the expensive plans, you can only include a limited number of fonts in your kit. Each time you want to try another set of fonts, you need to modify your kit and wait for it to be distributed across Typekit network. You may have to wait five or ten minutes between each change!

Another inconvenient thing is that once you publish your site, any change to the kit will affect the published version. Therefore, you need to use a second kit for development and play back and forth with those two kits.

What would be convenient here is a special kit with all fonts from the library with restricted access to some IP to avoid abuse. This is something done by Fontdeck, a competitor.

Poor subset support

Because you want to keep the font size small (about 30 kB for each variant), Typekit usually proposes two subsets: the default one contains only latin characters and the complete one with all glyphs. If some glyph is absent from the default subset, you need to use the complete one which is roughly two to four times the size of the default one. This is a bit huge for only one character.

What would be nice is to have subsets for each language. In my case, I would take a French subset because I need the “œ” glyph which appears in French in words like “cœur” (heart or core) or “œil” (eye). I don’t want to select the complete subset just for this character.

When a glyph is absent, the next font in the CSS stack is used. For example, if the selected font is FF Tisa Web Pro and the next font in the stack is Droid Serif, here is how the word “cœur” may be rendered. On the first line, FF Tisa Web Pro contains the appropriate glyph. On the second line, the glyph is absent and taken from Droid Serif. Since x-height and contrast are a bit different, the replaced glyph seems out of tone. At small sizes, the difference accounts for more than 10%.

Various rendering of word "cœur"
Rendering when glyph is present, substituted by a glyph from another font or emulated. Rendering at 72px and 16px (zoomed).

I have also added a third line where the ligature is built from “o” and “e” and the following style is applied to “e”:

.lf-ligature {
    margin-left: -0.172em;
    zoom: 1; /* Here to trigger hasLayout on IE7 */

As you can see, the result seems good. I have a piece of Javascript turning a ligature into the appropriate markup. It can be extended to handle more ligatures and could be adapted to handle other composed characters (like the ones in “Antonín Dvořák”).

Defective caching

UPDATED: Typekit improved the way fonts are served. It is probable that this paragraph is now void.

Because a document can only be rendered after the fonts have been loaded3, it is pretty important to deliver fonts as fast as possible. Typekit uses Edgecast as a CDN and DynECT for DNS hosting.

Two files are downloaded. Both of them are hosted on The first one is a script that will act as a loader. Based on the characteristics of your browser, it will choose how to download the requested fonts for best rendering. The second file is generally a big CSS file containing the fonts.

Here are the headers returned for the first file:

HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=300
Content-Encoding: gzip
Content-Type: text/javascript
Date: Tue, 08 Nov 2011 18:34:16 GMT
ETag: "388467610+gzip"
Expires: Tue, 08 Nov 2011 18:39:16 GMT
Last-Modified: Sat, 05 Nov 2011 09:31:57 GMT
Server: ECS (cdg/D624)
Vary: Accept-Encoding
X-Cache: HIT
Content-Length: 7440

First, notice that the content is served compressed. Good point. The next headers to look at are Cache-Control, Date, ETag and Expires: the server requests the content to be cached for 5 minutes. When the browser notices that its copy is out-of-date, it will issue a new request including a If-None-Match: 388467610+gzip header. Unless the kit has been modified, the server will answer with 304 Not Modified and no content. The copy of the browser will be valid for another 5 minutes. This means that after 5 minutes, at least 3 round-trips are needed to check if our fonts are up-to-date.

Worse, sometimes, Typekit servers may be just slow. They can wait one second before answering, just to send a 304. Since the first file is a script embedded in <head>, the whole page is blocked until you get the answer. Here is an illustrative example with Chromium 14 where a page using Typekit is loaded while all static assets are in the navigator cache and things have gone wrong (which is pretty rare):

Screenshot of Chromium Inspector
Timeline view when loading a page with Typekit embedded

Since fonts are needed, the rendering has been paused for 3.5 seconds until Typekit was fully loaded. If we look more closely, we may also notice we got a status 200 from the server instead of 304. Here is an excerpt of the browser request:

GET /tyt0atd.js HTTP/1.1
Connection: keep-alive
Accept-Encoding: gzip,deflate,sdch
If-None-Match: "2200251225+gzip"
If-Modified-Since: Thu, 10 Nov 2011 11:38:11 GMT

And here is an excerpt of the answer headers:

HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=300
Content-Type: text/javascript
Date: Fri, 11 Nov 2011 17:10:03 GMT
ETag: "2200251225"
Expires: Fri, 11 Nov 2011 17:15:03 GMT
Last-Modified: Thu, 10 Nov 2011 11:38:11 GMT
Server: EOS (lax001/54E5)
Content-Length: 25755

What happened? For some unknown reason, the server denied the access to the compressed version of the resource: there is no Content-Encoding: gzip while the browser did send Accept-Encoding: gzip. The tag provided by the browser (2200251225+gzip) came obviously from the compressed resource and therefore does not match. RFC 2616 requests If-Modified-Since header to be ignored if the tag does not match. Consequently, the server answers with 200 OK and resend the whole file. The same problem happened with the CSS file. Let me stress again how important the problem is: we had to download again the two files but we also downloaded the non-compressed version.

Now, let’s suppose that we did not run into this problem and the server answered with 304 Not Modified. The result is still not satisfactory: we would have waited 1.3 seconds to get both answers from the server. Keep in mind that most of the time, Typekit is pretty fast.

Here is how I think this should have been done: each time you modify your font kit, you get a new URL to use (containing the version of the font kit for example). This resource and the associated fonts (whose URL is also versioned) are requested to be cached for several days. As a sidenote, this also enables to modify a kit for your development environment without modifying the kit for your production site.

I suspect that Typekit did not go this way to track kit usage more accurately and enforce the appropriate pricing. Having multiple versions of the same kit would also allow us to use more fonts that we are paying for.

UPDATED: Typekit support knows the problem and is working on it:

You’ve identified a small bug in our CDN provider that we’re working with them to fix. Hopefully I can give an explanation of what’s happening.

The CDN edge servers have a cache of recently served kits on them. Any request that is served from that cache will be correctly Gzipped and handle ETags. Unfortunately, if the content isn’t already in cache and needs to be fetched from the master origin server then the request won’t be gzipped correctly which has the side effect of breaking ETags.


We are working to on getting this bug fixed, but any change to a globally distributed CDN will take some time. We’re also working on having our content cached longer than 5 minutes whilst still allowing people to quickly update their site.

UPDATED: To avoid to block page loading if Typekit is slow, you can load it asynchronously with yepnope.js. You’ll get some FOUT instead.


I have mixed feelings about Typekit. While the difficulty to test fonts and the lack of language-related subsets are annoying, I have been able to work around them. However, the caching problem is more annoying. I am still considering going back to Google Web Fonts just because of this.

  1. While I have a high interest in typography, have my own copy of “The Elements of Typographic Style” by Robert Bringhurst and have throughly read “The Elements of Typographic Style Applied to the Web”, I am still fairly novice on this matter. 

  2. Google serves fonts from a cookie-less domain. Technically, they could still do some kind of tracking but I believe they don’t. 

  3. In fact, browsers have different strategies on how to render before fonts availability. Paul Irish wrote a comprehensive article on FOUT. One common strategy is to render a blank space until downloaded fonts are available. If this takes too much time, fallback fonts may be used.