Zach’s ugly mug (his face)

Zach Leatherman

“The Compromise”—a Modern but Compatible Font Loading Strategy

April 05, 2018

Let’s be frank: the next evolution in advanced web font loading is overdue. I’m not talking about font-display, which is great—a quick and easy win for introductory font loading. I’m talking about evolving our multi-stage font loading approaches to squeeze every last drop of performance out of our web font rendering.

The next evolution will come when we retire our font loading polyfills.

Retirement Benefits

Polyfill retirement is the next step and is especially important as these JavaScripts are usually inlined in the critical path. The time spent parsing and executing polyfill JavaScript is essentially wasted on browsers that support the native CSS Font Loading API. For example, take the following two examples from my web-font-loading-recipes repository:

No… Please—Don’t go?

Can we just remove the polyfills wholesale and switch our code to use the CSS Font Loading API? Well, not quite yet. The browser-provided CSS Font Loading API has pretty broad support and has been around for a long time but is confoundedly still missing from all available versions of Microsoft Edge.

Web BrowserCSS Font Loading
Support Added
Chrome v35May 19, 2014
Opera v22June 2, 2014
Firefox v41September 21, 2015
Samsung Internet v4April 18, 2016
UC Browser v11.8August 16, 2016
Mobile Safari v10September 12, 2016
Safari v10September 19, 2016
Internet Explorer🚫 Not supported
Microsoft Edge🚫 Not supported

Microsoft Platform status has given no signals of intent to implement. At time of writing, the CSS Font Loading API UserVoice entry (a voting platform for developers to help Microsoft prioritize features) has 796 votes, which is #49 on the list (ordered by total votes). That’s 350 votes behind the Web MIDI API and 553 votes behind Scrollbar Styling 🙄.

Ahem, I digress.

Fonts for Auction

How do we go polyfill-less in modern browsers without completely sacrificing our web fonts on IE and Edge? This blog post would be pretty silly if it ended without answering that question…

In September of 2017, eBay engineers Senthil Padmanabhan (@senthil_hi) and Raja Ramu (@rajaramu) wrote a blog post titled eBay’s Font Loading Strategy. It may have been one of the most underrated font loading blog posts of 2017.

The eBay strategy was novel for two reasons, the first being that it emulated font-display: optional using JavaScript. JavaScript was important because at that time only Chrome supported the font-display descriptor. Practically speaking, font-display: optional means that an empty-cache view kicks off requests for web fonts but doesn’t render them. Web fonts are only rendered when they are already in available in cache. This minimizes both FOIT and FOUT, as well as nasty text reflows.

The second novelty was that it took steps to partially eliminate polyfill usage! Namely it did not inline a font loading polyfill! Instead it used the CSS Font Loading API if it was available and if not, asynchronously loaded the font loading polyfill to load the web fonts (again, only on IE and Edge).

I’ve also added the eBay Method to web-font-loading-recipes if you want to check out the code.

The Compromise

Senthil and Raja’s eBay method is great! However, I would make one small change to their approach (and this is also why I don’t prefer font-display: optional)—I like rendering web fonts on an empty cache load! Thus, the Compromise is born, borrowing heavily from eBay’s approach.

If you’ve been following along at The Comprehensive Guide to Font Loading Strategies, you may be aware that my personal favorite method is currently transitioning to Critical FOFT with preload, given that preload support is on the cusp of widespread support.

Curious what FOFT is? Check the Web Font Loading Glossary.

Let’s Frankenstein the eBay method and the Critical FOFT method together:

You can review the HTML and Lazy loaded JavaScript (only used if the CSS Font Loading API is not supported). The Compromise is also on web-font-loading-recipes.

In this example, we used a traditional 4+1 Lato setup: Roman (plus a super subset A-Za-z Roman), Italic, Bold, Italic Bold.

  1. Preload the super subset Roman (WOFF2)
  2. If sessionStorage key exists for repeat views, add the fonts-loaded-2 class and stop here!
  3. If CSS Font Loading API is supported, load the fonts:
    1. Use CSS Font Loading API to load Stage 1: super subset Roman
    2. Render super subset Roman (add fonts-loaded-1 class)
    3. Use CSS Font Loading API to load Stage 2: Roman, Italic, Bold, and Italic Bold.
    4. Render Roman, Italic, Bold, and Italic Bold versions (add fonts-loaded-2 class)
    5. Skip to step 5.
  4. If CSS Font Loading API is not supported, load the fonts:
    1. Asynchronously lazy load the polyfill (in this example critical-foft-preload-fallback-optional.js)
    2. Use the polyfill to load only Stage 2: Roman, Italic, Bold, and Italic Bold.
    3. Do not render these fonts (no classes added)
    4. Skip to step 5.
  5. All the fonts have confirmed to be loaded, add the sessionStorage key for repeat views.

In the wild

It’s frustrating that Microsoft browsers don’t support the CSS Font Loading API. But this is where The Compromise shines—it allows us to serve and render web fonts using the CSS Font Loading API (no polyfills) on an empty-cache page loads. If a browser doesn’t support the CSS Font Loading API (uh, IE and Edge) we take a more relaxed font-display: optional-ish repeat-view approach.

The Compromise has been battle tested. It has been in production on this web site for quite some time ( and is the strategy we used for the font loading on the lovely redesign of Smashing Magazine (although with FOFT instead of Critical FOFT).

Keep those web fonts frosty, y’all 👍.


If some of the terms in the above article were new to you, you may want to look them up on the Web Font Loading Glossary.

Zach’s ugly mug (his face)

Zach is a Web Craftsperson with the award winning Filament Group. He’s currently fixated on web fonts and static site generators. His public speaking résumé includes talks in eight different countries at events like Beyond Tellerrand, Smashing Conference, CSSConf, and The White House. He also helps herd NEJS CONF and the NebraskaJS meetup. Read more about Zach »


jalbertbowdeniiChris Ferdinandi ⚓️nils binder 🏳️‍🌈
4 Replies
  1. Chris Ferdinandi ⚓️

    Chris Ferdinandi ⚓️ @ChrisFerdinandi #

    Thank you!!

  2. Zach Leatherman

    Zach Leatherman @zachleat #

    Make sure you use `preconnect`! You should check out this talk about the wins you can get with google fonts: The preconnect stuff starts at Slide 50

  3. Paauł Kruczynski

    Paauł Kruczynski @paulkruczynski #

    Thank you for responding. It’s appreciated!

  4. Paauł Kruczynski

    Paauł Kruczynski @paulkruczynski #

    It may be. Reading these again in a new context, I might be using the wrong method on this project. I don’t have much control over the fonts. Just trying to get them to not use google fonts, but I may have to let it go