New package to allow locally hosting webfonts

The current guidelines for themes prohibit the use of CDNs to load assets due to privacy/tracking concerns. The only exception to that rule is Google-Fonts.

Historically, google-fonts have been used as a way for themes to provide more appealing typography options. They are widely used and though there were always privacy & tracking concerns, we could not remove the exception until we had a viable alternative for theme-authors.

To that end, the Themes Team has built an easy-to-use implementation that themes can start using and you can get it on our GitHub repository.

The intention is to remove the Google-Fonts exception soon from our guidelines. When that happens, themes that use Google-Fonts will need to implement a way to locally host the fonts.

Our hope is to see a similar method implemented in WordPress Core, as there is a discussion on Trac. This is just the first step to what we hope will be a more unified approach.

Usage

A WordPress theme will typically enqueue assets using the wp_enqueue_style function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function my_theme_enqueue_assets() {
 
    // Load the theme stylesheet.
    wp_enqueue_style(
        'my-theme',
        get_stylesheet_directory_uri() . '/style.css',
        array(),
        '1.0'
    );
 
    // Load the webfont.
    wp_enqueue_style(
        'literata',
        'http://wayback.fauppsala.se:80/wayback/20201129181013/https://fonts.googleapis.com/css2?family=Literata&display=swap',
        array(),
        '1.0'
    );
}
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_assets' );

To locally host the webfonts, you will first need to download the wptt-webfont-loader.php file from the repository and copy it in your theme. Once you do that, the above code can be converted to this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function my_theme_enqueue_assets() {
 
    // Include the file.
    require_once get_theme_file_path( 'inc/wptt-webfont-loader.php' );
 
    // Load the theme stylesheet.
    wp_enqueue_style(
        'my-theme',
        get_stylesheet_directory_uri() . '/style.css',
        array(),
        '1.0'
    );
 
    // Load the webfont.
    wp_add_inline_style(
        'my-theme',
        wptt_get_webfont_styles( 'http://wayback.fauppsala.se:80/wayback/20201129181013/https://fonts.googleapis.com/css2?family=Literata&display=swap' )
    );
}
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_assets' );

Supporting IE

The wptt_get_webfont_styles will – by default – download .woff2 files. However, if you need to support IE you will need to use .woff files instead. To do that, you can pass woff as the 2nd argument in the wptt_get_webfont_styles function:

1
wptt_get_webfont_styles( 'http://wayback.fauppsala.se:80/wayback/20201129181013/https://fonts.googleapis.com/css2?family=Literata&display=swap', 'woff' );

Licensing

We believe that locally hosting webfonts will benefit the whole ecosystem by improving performance for visitors and increasing privacy online.

WordPress themes hosted in the official repository are required to be GPL2.0-compatible, but there is a wider community of WordPress themes out there, and they are not always GPL2.0-compatible.

We chose to license this script under the MIT license, which is GPL2.0-compatible. Every theme, plugin, or other script can use the code without any restrictions as to the license they use.

Very excited to see the work continue on 46370.

It seems like using one option called ‘downloaded_font_files’ breaks a requirement for themes to prefix their global names, and for their options to be all in one array.
Also, there is no provision made for cleaning up the fonts which could accumulate by switching themes a lot.
Also, I think WP has some functions to use for finding the files. It would reduce the code.

I take issue with the statement “We believe that locally hosting webfonts will benefit the whole ecosystem by improving performance for visitors and increasing privacy online.”
The whole point of Google Fonts is that the font has one address across the web, and it’s more likely to be cached already on the visitor’s computer. By making it local to the site, you have defeated that purpose, and it will be slower (Google has fast servers and a backbone connection and you only download it once, instead of again for each site using the same font).
The privacy aspect is a specious concern.

I am opposed to removing the Google Fonts exception to external resources.

When I mentioned “switching themes a lot”, it was only one case of when fonts could pile up. When a Customizer control allows you to select the Google font to use, the code would be filling up the font folder.
My theme uses Google’s partial text feature to request only the characters that are in the name of the font, so I can show the font in the list correctly without huge downloads. Your code doesn’t account for that.

It seems like using one option called ‘downloaded_font_files’ breaks a requirement for themes to prefix their global names, and for their options to be all in one array.

True, but it also makes the option persist across all themes that use the library – which will avoid running the same processes again and will also avoid littering the database with multiple options – one per theme.

Also, there is no provision made for cleaning up the fonts which could accumulate by switching themes a lot.

Yes, we could add a periodical cleanup routine. In its initial implementation only the transient is refreshed once/week. Future iterations may go through the fonts and delete ones that are no longer used.
Though if properly used I don’t think there will be issues… The scenario where this library would be used is the same scenario twentytwenty had – where it was shipping a specific font. Or another scenario would be serving a font-family the author has picked to visitors (not selecting the font, but serving it).

Also, I think WP has some functions to use for finding the files. It would reduce the code.

I know it has functions to find files inside themes etc, but I am not aware of a function that “finds files” in general. Or are you referring to the functions inside the `WP_Filesystem` class?

By making it local to the site, you have defeated that purpose, and it will be slower (Google has fast servers and a backbone connection and you only download it once, instead of again for each site using the same font)

There are more things to performance than just caching. As a small example, when a google font gets loaded from the Google Fonts CDN, the browser has to perform 4 additional requests to Google’s servers for the TLS handshakes (or at least I think it’s 4, correct me if I’m wrong).
If the files are hosted locally, it doesn’t need to do that since the files are coming from the same domain. That saves 4 roundtrips for the browser.
As for the performance of Google’s servers, it is not all that better from a round of the mill modern host with Cloudflare (or another similar service) in front of it.

When I mentioned “switching themes a lot”, it was only one case of when fonts could pile up. When a Customizer control allows you to select the Google font to use, the code would be filling up the font folder.

This is not intended for customizer controls or deciding which font one wants to use. For these things a more suitable solution would be something purely JS-driven like for example Typescript’s webfontloader, or even directly injecting the link elements.
It is not a cure-all. It is a solution to a problem, and it is not the same problem as the one you are describing.

My theme uses Google’s partial text feature to request only the characters that are in the name of the font, so I can show the font in the list correctly without huge downloads. Your code doesn’t account for that.

That is a perfectly good solution to the problem you are describing – which is previewing a font so you can select one to use. Not the same as serving the font you have selected to visitors… These are different problems which require different solutions.

If this is good enough for the theme team, it should be good enough for WordPress core.

Having an API like this in core would (mostly) guarantee everyone who needs this functionality uses it. It would also help avoid authors including different versions of this file in the future across multiple parent & child themes, which could cause breakage if the internals ever change, or as methods come and go. Lastly, it also avoids themes from not updating their bundled versions of this file.

I’m know code-related feedback is best provided on GitHub, but while I’m here (and before too many people use this approach) I noticed a lack of multisite support in this file, even though fonts are stored globally in the content directory. I think this means that each site in the installation ends up doing the same loop to discover and cache fonts in their own options and transients, which leaves open the possibility of them being out of sync across sites, which may be a weird experience for visitors.

I am, in general, strongly in favor of serving properly licensed fonts locally, and in a universally acceptable way. Good progress so far!

I noticed a lack of multisite support in this file, even though fonts are stored globally in the content directory. I think this means that each site in the installation ends up doing the same loop to discover and cache fonts in their own options and transients

Good point @johnjamesjacoby! I changed the option and transient functions to their multisite counterparts, so this will now work properly both on single-site and multisite installations.

What if an Author wants to use 2 or 3 fonts for a theme?

There’s nothing stopping them from doing that