Hey! This whole article is about a time before May 2019 in which Google Fonts didn’t offer a way to use font-display without self-hosting the fonts.
To use font-display with Google Fonts, you include a URL parameter like &display=swap
in the URL, like https://fonts.googleapis.com/css?family=Open+Sans&display=swap. If you’re copying code from Google Fonts now, it’s the default, so you get it automatically, but you might want to add it if you have existing URLs to Google fonts lingering around, or you want to change it to something like optional
if you prefer that.
Zach Leatherman notes there are still some things to wish for, like stable URL’s for the fonts so we could link up the fonts in our own CSS, preventing the double-hop needed right now.
This is the original article:
The font-display
descriptor in @font-face
blocks is really great. It goes a long way, all by itself, for improving the perceived performance of web font loading. Loading web fonts is tricky stuff and having a tool like this that works as well as it does is a big deal for the web.
It’s such a big deal that Google’s own Pagespeed Insights / Lighthouse will ding you for not using it. A cruel irony, as their own Google Fonts (easily the most-used repository of custom fonts on the web) don’t offer any way to use font-display
.
Summarized by Daniel Dudas here:
Google Developers suggests using Lighthouse -> Lighthouse warns about not using
font-display
on loading fonts -> Web page uses Google Fonts the way it’s suggested on Google Fonts -> Google Fonts doesn’t supportsfont-display
-> Facepalm.
Essentially, we developers would love a way to get font-display
in that @font-face
block that Google serves up, like this:
@font-face {
font-family: "Open Sans Regular";
src: url("...");
font-display: swap;
}
Or, some kind of alternative that is just as easy and just as effective.
Seems like query params is a possibility
When you use a Google Font, they give you a URL that coughs up a stylesheet and makes the font work. Like this:
https://fonts.googleapis.com/css?family=Roboto
They also support URL params for a variety of things, like weights:
https://fonts.googleapis.com/css?family=Open+Sans:400,700
And subsets:
http://fonts.googleapis.com/css?family=Creepster&text=TRICKS
https://fonts.googleapis.com/css?family=Open+Sans:400,700&subset=cyrillic
So, why not…
http://fonts.googleapis.com/css?family=Creepster&display=swap
The lead on the project says that caching is an issue with that (although it’s been refuted by some since they already support arbitrary text params).
Adding query params reduces x-site cache hits. If we end up with something for font-display plus a bunch of params for variable fonts that could present us with problems.
They say that again later in the thread, so it sounds unlikely that we’re going to get query params any time soon, but I’d love to be wrong.
Option: Download & Self-Host Them
All Google Fonts are open source, so we can snag a copy of them to use for whatever we want.
Once the font files are self-hosted and served, we’re essentially writing @font-face
blocks to link them up ourselves and we’re free to include whatever font-display
we want.
Option: Fetch & Alter
Robin Richtsfeld posted an idea to run an Ajax call from JavaScript for the font, then alter the response to include font-display
and inject it.
const loadFont = (url) => {
// the 'fetch' equivalent has caching issues
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
let css = xhr.responseText;
css = css.replace(/}/g, 'font-display: swap; }');
const head = document.getElementsByTagName('head')[0];
const style = document.createElement('style');
style.appendChild(document.createTextNode(css));
head.appendChild(style);
}
};
xhr.send();
}
loadFont('https://fonts.googleapis.com/css?family=Rammetto+One');
Clever! Although, I’m not entirely sure how this fits into the world of font loading. Since we’re now handling loading this font in JavaScript, the loading performance is tied to when and how we’re loading the script that runs this. If we’re going to do that, maybe we ought to look into using the official webfontloader?
Option: Service Workers
Similar to above, we can fetch the font and alter it, but do it at the Service Worker level so we can cache it (perhaps more efficiently). Adam Lane wrote this:
self.addEventListener("fetch", event => {
event.respondWith(handleRequest(event))
});
async function handleRequest(event) {
const response = await fetch(event.request);
if (event.request.url.indexOf("https://fonts.googleapis.com/css") === 0 && response.status < 400) {
// Assuming you have a specific cache name setup
const cache = await caches.open("google-fonts-stylesheets");
const cacheResponse = await cache.match(event.request);
if (cacheResponse) {
return cacheResponse;
}
const css = await response.text();
const patched = css.replace(/}/g, "font-display: swap; }");
const newResponse = new Response(patched, {headers: response.headers});
cache.put(event.request, newResponse.clone());
return newResponse;
}
return response;
}
Even Google agrees that using Service Workers to help Google Fonts is a good idea. Workbox, their library for abstracting service worker management, uses Google Fonts as the first demo on the homepage:
// Cache the Google Fonts stylesheets with a stale while revalidate strategy.
workbox.routing.registerRoute(
/^https:\/\/fonts\.googleapis\.com/,
workbox.strategies.staleWhileRevalidate({
cacheName: 'google-fonts-stylesheets',
}),
);
// Cache the Google Fonts webfont files with a cache first strategy for 1 year.
workbox.routing.registerRoute(
/^https:\/\/fonts\.gstatic\.com/,
workbox.strategies.cacheFirst({
cacheName: 'google-fonts-webfonts',
plugins: [
new workbox.cacheableResponse.Plugin({
statuses: [0, 200],
}),
new workbox.expiration.Plugin({
maxAgeSeconds: 60 * 60 * 24 * 365,
}),
],
}),
);
Option: Cloudflare Workers
Pier-Luc Gendreau looked into using Cloudflare workers to handle this, but then followed up with Supercharge Google Fonts with Cloudflare and Service Workers, apparently for even better perf.
@font-feature-values
Option: Wait for One of the reasons Google might be dragging its heels on this (they’ve said the same), is that there is a new CSS @rule
called @font-feature-values
that is designed just for this situation. Here’s the spec:
This mechanism can be used to set a default display policy for an entire font-family, and enables developers to set a display policy for @font-face rules that are not directly under their control. For example, when a font is served by a third-party font foundry, the developer does not control the @font-face rules but is still able to set a default font-display policy for the provided font-family. The ability to set a default policy for an entire font-family is also useful to avoid the ransom note effect (i.e. mismatched font faces) because the display policy is then applied to the entire font family.
There doesn’t seem to be much movement at all on this (just a little), but it doesn’t seem pretty awesome to wait on it.
Super interesting as this is something I’ve been trying to resolve as of late!!!!
I landed on FontFaceObserver (https://github.com/bramstein/fontfaceobserver) which has worked well for me so far, especially when adding a cheeky little cookie to check whether the user is a previous visitor or not (if yes, apply body class of “fonts-loaded and use cached font – if no, don’t apply class, use fallback font and FFO then waits for GFonts to download then change (so FOUT)). Similar to: https://www.filamentgroup.com/lab/font-events.html
I use a service worker so I’d be really interested to see the performance benefits and ease of use vs something like FFO (potentially a few KBs saving/a couple of requests?)
Do you have any experience with FFO?
I almost cried when I got a 96(or 97) score on performance and best practises and the only thing left to do was something I couldn’t do. I’ll probably go for SW caching, hoping to get that awesome 100/100.
This will help: https://google-webfonts-helper.herokuapp.com/fonts
Excellent information for anyone looking to implement font-display. Hopefully Google will hurry on this since insights dings as you mention, that way it should be backwards-compatible. Cheers!
Everybody Here’s a more (hopefully) convenient way to deal with this. You have to just download the CSS file from, for example, https://fonts.googleapis.com/css?family=Creepster and then add your own rule in it. Just because you will take control with the @font-face option and at the same time, load fonts from Google’s fast server. Hopefully this helps!
Fonts API serves a stylesheet generated for the specific user agent making the request.
If you download the CSS file you’ll get a single version of the stylesheet for whatever user agent you used to download and then serve that stylesheet to all user agents.
See here:
https://developers.google.com/fonts/docs/technical_considerations
Is is me or everyone that service worker does not work? Service worker cannot read the content of the request (text() return empty string). I guess it is blocked for security that prevent service worker from reading response from different domains.
http://fonts.googleapis.com/css?family=Creepster&font-display=swap
this doesn’t work anymore
Hey Lukas! That’s what the post is pointing out — it would be great to have that sort of parameter, but we don’t have it yet though we hope to some day. :)
oww, I thought it used to work, sorry :)
My solution is to make a simple php file which downloads the required css from
fonts.googleapis.com
into a variable, inserts thefont-display: ...
line after each@font-face {
lines, and returns the contents. The exact google fonts url to grab can be passed as a query parameter. Than replace allhttps://fonts.googleapis.com/css?family=...
references withhtt*://yourdomain/google-font.php?family=...
. Done. No javascript delays, no more libraries or magic. And as the references inside the modified css still point to the google server, the site can benefit from fonts being cached from before, even other sites that use the same, which is better than self hosting the font files too.