Disclaimer.

  • I have not been approached or employed by Hoefler&Co or cloud.typography to investigate any of the following issues.
  • I disclosed all of the following to Cloud.Typography and gave them ample opportunity to work together to address the root cause of the problem. They didn’t have the desire to do that, so I decided to give it all away for free, because faster Internet is good for everyone.
  • All the people I deal with seem like really, really nice people. The following is not a reflection on any individual. Please keep this in mind as you read.

It all began, as these things often do, with a waterfall.

There’s so much to read in just a few entries! Can you see it? Can you see it? View full size (31KB)

I’m doing some rough research and testing on a potential client’s website to get a good idea of how things look before we work together. Even in the first 10 entries in the waterfall diagram above, there are many fascinating clues and telltale signs that are immediately apparent. Early in the page load life cycle, I was struck by some interesting behavior. Let’s break it down and see…

  1. Item (9), forcloud.typography.com, has a very high connection overhead (405ms total), surprisinglyBig TTFB(210ms), and will return one anyway302. What’s going on here? Where are we being redirected to?
    • It turns out that Cloud.Typography is redirecting requests to a hosted onfonts.[client].comClient-owned CSS files on: Note oneLocation: https://fonts.[client].com/[number]/[hash].cssHead.
  2. Entry (10) lives in a different source again, so we have more connection overhead to deal with, and the file seems to take a long time to download (as evidenced by the large amount of dark green sent data). Why is Cloud.typography redirecting us to a file we own? Is there some sort of “publishing mechanism” for verification[client].comTo use font services? And why is this file so big?
    • It turns out that Cloud.Typography is a hybrid solution, somewhere between Cloud and self-hosting. Requests made from whitelisted domain names return one302, forwards the request to a self-hosted CSS file optimized for your browser, operating system, and UA (Google Fonts does a similar thing). The CSS files we host are provided by Cloud.Typography.
      • If you want to know, a non-whitelisted domain name will return one403The response.
    • This file is so large because it contains virtually all of our fonts as Base64 encoded data URIs.
  3. Finally, item (3), a large JS file, is not executed until the CSS (item (10)) is complete. From this, I can conclude that item (3) is a synchronous JS file that was defined in HTML some time after CSS.
    • How do I know that? Because synchronous JS defined anywhere after synchronous CSS is not executed while the CSS in question is in flight. JS can be blocked at CSSOM build time. The ripple effect of this long CSS request chain is huge.

So how does all this affect performance?

First, although a request to cloud.typography.com returns a 302 response of text/ HTML MIME type, it sends a request for CSS. This makes perfect sense because the request is made by . We can do this by noting that Accept: text/ CSS,*/*; The presence of the q=0.1 request header further verifies this. In short, although it is not CSS, this request is on our “critical path” and therefore prevents rendering.

To further exacerbate this problem, the 302 response has a cache-Control: must-revalidate, private header, which means that we will make a request for this resource whether we are accessing the site through a cold Cache or a hot Cache. Although the response was 0B in file size, we were always hit with latency on every page view (this response was basically 100% latency). On mobile connections, this can be equivalent to a full few seconds of delay, all on the critical path.

Next, we are sent to fonts.[client].com, which adds more delay to the connection setup. Note that this phenomenon is unique to the way the company implements its assets and has nothing to do with Cloud.typography). Because the response is CSS, our chain of critical requests remains uninterrupted-this is all the work that goes on along the critical path.

Once we had taken care of the connection overhead, we started downloading a huge CSS file (271.3KB) that contained all the fonts encoded in Base64 data URIs in the project. Base64 is absolutely terrible for performance, especially when it comes to fonts, and it has the following problems.

  • Base64-encoded assets into CSS would have shifted more weight (and therefore delay) to the critical path, hindering our initial rendering.
  • Base64 has a high compression rate, which means we can’t recover excessive, increased file sizes.
  • Fonts are usually not downloaded until the Render Tree is built and the browser knows that the current page actually needs them. This browser defense ensures that only fonts that are really needed are downloaded. If you encode a font into CSS, then all the font data will be downloaded regardless of whether the current page needs it or not: it’s just too wasteful.
  • By Base64 encoding fonts, we end up discovering that technically we don’t have any fonts at all: we only have CSS. The implication here is that we have now completely disabled any font loading strategy: if there is no font,font-display; Without fonts, the font loading API is useless. Technically, what we have here is a CSS problem, not a font problem.

The practical result of the above situation is that we have 1,363ms of render blocking CSS on our Critical Path for first access on wired connections. Repeat browsing still cost 280ms.

Because the “CSS file “in Cloud.typography has a must-revalidate header, we still suffer from delays when browsing repeatedly. View full size (17KB)

The final nail in the coffin is that without font files, the browser can’t take any decisions about what to do with missing fonts. Most browsers display invisible text for three seconds, and if the Web font doesn’t reach the device within that time, backup text is displayed. This means that once the page is rendered, the user can’t read anything for a maximum of three seconds.

All this only works if there are actual font files to negotiate. Because cloud.typography’s fonts are really just more CSS, the browser can’t tell the difference between the two and therefore can’t provide a three-second grace period. This means that, in theory, there is an indefinite amount of time for us to stop rendering not just the text, but the entire page.

If you’re interested in seeing a real-world example of this, consider the movie clip below for comparison.

Post-it was a client of Cloud.typography, and the entire page remained blank until the CSS files in cloud.typography entered the device. Grid By Example, on the other hand, uses Google fonts (without any font-display configuration), which allows the browser to start rendering the page without a Web font.

** Note: ** These two test cases are very, very different sites, so I don’t want to directly compare any time – that would be unfair – I just want to highlight this phenomenon.

Post-it starts rendering in 16.8 seconds on 3G connections. This time was increased by more than 48% to 8.7 seconds if cloud.typography was completely removed.

All of the above issues add up to mean that, unfortunately, Cloud.typography is slow by default. While I’m a huge fan of this font library and some of the amazing fonts they produce, I can’t in good conscience recommend Cloud.Typography as a reliable font provider, which is how they do it. It is detrimental to performance.

Solve problems for customers

The solutions I implemented for my client were mitigation measures at best — the issues of Cloud.Typography still exist, but I was able to do something to eliminate the manifestations of these problems.

First, I want to unblock JS execution: there is no need to keep the main app.js bundle for font sake. How to solve it? It’s simple: swap the and

Before.

<link rel="stylesheet" href="https://cloud.typography.com/[number]/[number]/css/fonts.css" />
<script src="https://www.[client].com/packs/application-[hash].js"></script>
Copy the code

After.

<script src="https://www.[client].com/packs/application-[hash].js"></script>
<link rel="stylesheet" href="https://cloud.typography.com/[number]/[number]/css/fonts.css" />
Copy the code

Yes. It’s that simple. Now notice that item (3) is executed before the fonts.[client].com file starts to download.

By loading CSS after our JS, JS can run earlier. View full size (29KB)

My second strategy is to prepay the connection cost of fonts.[client].com by simply preconnecting to the origin. In the early days of head.

<link rel="preconnect" href="https://fonts.[client].com" />
Copy the code

Note entry (11) where we are dealing with up to 397ms of connection overhead outside the critical path.

These two small changes resulted in a 300ms improvement at the start of rendering, and a stunning 1504ms improvement in TTI at 50 quartiles.

** Note: ** Please remember that none of these changes address any issues inherent in Cloud.typography. What I’m doing here is doing my best to mitigate these issues on the client side.

Let’s talk about

Need something the same?

I can help you with workshops, consulting, advice and development.

Solve problems for everyone

We’ve written over 1,300 words, and I haven’t even nailed down the key to this problem yet. Cloud.typography didn’t give us a font performance problem, it gave us a CSS performance problem. Because fonts are Base64 encoded, there are no fonts. This means that no amount of font-display, font loading apis, Font Face Observer, etc., can help us. How do we solve this problem at source? I got in touch with Cloud. Typography.

Lend a helping hand

To be honest, I was a little confused at first. There are non-cacheable, cross-source redirects on Critical Path and Base64 encoded fonts, which seems like a very slow way to deliver assets. So, naturally, I wanted to check if we were doing anything wrong. I also want to understand more thoroughly how the solution works end-to-end, so I can better advise.

I got the details of the Hoefler&Co team members and asked if I could send them some questions about their cloud service. If you are interested, you can read the full unedited email.

The answers I got shed some light on a few things. In a nutshell.

  • The customer’s implementation is correct.
  • The redirection does not “free” the font for use, but actually collects some information about your browser and operating system and forwards the request to the correct one of the many CSS files that Cloud.typography provides for you to host yourself.
    • The redirection ofLocationThis largely depends on the user agent making the request, so you can’t circumvent cloud.typography’s itinerary.
    • (Author’s note: But make no mistake, redirects exist mainly to keep track of usage, otherwise they would cache it.)
  • Cloud services are targeted at developers and organizations that may need access to a larger library to complete many different projects.
  • The self-hosting option is targeted at companies with a limited, strictly defined font palette (consider more corporate customers).
  • We encourage anyone concerned about performance to upgrade to a self-hosted plan.

I’m very grateful to this person for their time and patience, I just popped up on their radar and had quite a few questions and, I admit, criticism of them. Their answers were insightful and timely.

However, here I ended up a little frustrated: despite a clear overview of the specific impact of Cloud.typography on performance, there was no interest in studying how to remedy these problems. No one was willing to provide or even document an alternative (i.e., not _ substituted _– the current approach is still perfectly valid) non-obstructive loading strategy.

I came up with a proof-of-concept alternative loading strategy that would not block rendering, opting instead to load assets asynchronously in exchange for a flashback text (FOFT), similar to implementing font display: swap; , I’ve been told that clients overwhelmingly prefer not to have their pages load with no font and then “pop” the correct font… Of course, I disagree with every asynchronous font loading policy and the entire font-display/ font loading specification.

I learned that there are currently around 13,100 sites in the HTTP archive that use Hoefler&Co’s cloud-.typography service. That means about $15.5 million worth of customers a year are tied to a slow design, slow default web font solution when we could be helping them out! It’s a shame.

It’s a real shame because the solution is trivial and I’ve done all the legal work, but that’s life.

The solution

To reiterate: we don’t have a web font problem, we have a CSS problem. With this in mind, the solution to this problem becomes very simple: we just need to lazily load our CSS. By lazily loading our font style sheet, we remove it from the Critical Path and unblock rendering.

While lazy load stylesheets have become increasingly common since the advent of Critical CSS, the Filament Group’s latest approach is the simplest and most widely supported yet.

<link rel="stylesheet" href="https://cloud.typography.com/[number]/[number]/css/fonts.css"
      media="print" onload="this.media='all'" />
Copy the code

The magic is in the second row. By setting a mismatched media type, the browser automatically loads the stylesheet from the critical path with low priority. Once the file is loaded, the embedded onload event handler switches to a matching media type, and this change applies the style sheet to the document, replacing the font.

One caveat to this approach is that we do now have a flashback text (FOFT). This method of loading the stylesheet effectively synthesizes font display: swap; , displays a fallback font immediately and replaces the web font of our choice when it becomes available. This means that while we’re waiting for the right font to arrive, you have to design a very powerful, accurate back-up style for the user to experience. Thankfully, Monica made this a lot easier by providing us with a font style matcher.

What I particularly like about this approach is that we’ve gone from being arguably the slowest way to load assets to perhaps the least intrusive. It’s a paradigm turned upside down.

Here’s a before and after comparison over a 3G connection. The original implementation is on the left, and my fixed version is on the right.

Didn’t see anything? Please click here!

Yes, we do have a FOFT, but we put the text in front of readers earlier, which is a huge improvement.

conclusion

I was very frustrated with Cloud. Typography’s lack of interest in these issues, especially considering that the “fix” only required updating the document. They don’t need to make any platform or infrastructure changes. In addition, they are still able to maintain the same level of control and completely track font usage, but completely asynchronously. It was also good news for them to announce the release of a faster way to include equally beautiful fonts.

That said, if you are a client of Cloud.Typography, don’t panic. I suggest you explore the asynchronous approach I outlined above. The implementation itself should be trivial, but you need to invest some time in designing a proper fallback style while your CSS is loading.

Leaving aside specific web font vendors, I think the whole situation is a fascinating case study of how things can start to slide if you allow enough little mis-mergers. It was interesting for me to peel back each layer and see how it affected the next part of the problem, and I hope my elaboration taught people a thing or two in the process.

If I had to sum up the whole article with one takeaway, it would be that CSS performance is absolutely critical, and with the current tendency to ignore and dismiss CSS as its own discipline, cases like this are becoming very common.

CSS is important. Very important.