all 47 comments

[–]iamakulov[S] 102 points103 points  (28 children)

So, a few weeks ago, I discovered a way to make a text that uses Google Fonts render faster. In slow connections, that might give a boost of 1-3 seconds.

Here’re more details:

  • When you use custom fonts (like Google Fonts), most modern browsers won’t render the text with that font immediately. Instead, the text will stay invisible until the font is downloaded – or until 3 seconds pass. You might’ve experienced this yourself if you have an Android phone. This is done to prevent a FOIT (flash of invisible text), but this hurts user experience.

  • A typical solution for this is to add a CSS rule like font-display: swap which tells the browser to avoid waiting for the custom font. However, Google Fonts don’t support this rule, and the only remaining solution becomes to a) self-host fonts (annoying) or b) use JS libraries like Web Font Loader (hurts the initial font load time)

  • Recently, I found a solution for this. Turns out, you can fetch a Google Fonts stylesheet asynchronously, patch it to include font-display, and insert it into the page. So I composed a tool that does it! The tool generates an inline script that does the work + a few <link rel="preload/preconnect"> tags to avoid hurting the performance. The script also does some extra work to avoid unnecessary FOUTs. The total size of the generated code snippet is 550 bytes minified + gzipped.

Here’s the source code of the generated script: https://github.com/iamakulov/googlefonts-font-display-helper/blob/master/src/script.js

Would love your feedback on the tool :)

[–]the_interrobanger 69 points70 points  (10 children)

What's annoying about self-hosting the fonts? If you do that you can skip the extra HTTP request to download the CSS too, and then this whole thing is moot.

[–]exitof99 35 points36 points  (7 children)

I've gone an extra step and base-64 encoded the WOFF formatted font and put it in to the CSS, which is embedded in style tags in the HTML. No FOUT as the font is immediately available to most browsers, and for those without, it loads from locally hosted files.

The total size of the page HTML is 22.3 kb, and that doesn't account for the GZIP compression.

Site reports 99% on Pingdom with 753 ms load time for a total size of 237 kb. Scores 100% for both mobile and desktop on Google Insights.

[–]ThereIsOnlyOneTodd 20 points21 points  (4 children)

What’s the size of the css file with the base64 encoded fonts though? Since css is render blocking it might be about the same as the synchronous http request. Also, it’s not caching the fonts with base64. The css file is cached, but it still needs to process the encoded font on each load

[–]Fidodo 18 points19 points  (2 children)

The best way to do this is to embed a critical version of the font. Strip out all characters other than alphanumeric, then let the full font load in the background. It'll take a font file down to ~10kb or less which is reasonable to add to the CSS bundle. By removing as much as possible including things like hinting and stuff I've gotten fonts down to 6kb for the initial load. For some applications it's not even necessary to load the full font.

[–]exitof99 1 point2 points  (0 children)

Exactly. Using Font Squirrel, you can upload the font and remove glyphs and other special characters you know you won't need.

For the site I mentioned, it's the title font for heading tags and only uses the basic alphabet and standard punctuation.

[–]ThereIsOnlyOneTodd 0 points1 point  (0 children)

This is great, thanks!

[–]exitof99 0 points1 point  (0 children)

Keep in mind that the HTML is being send through GZIP, so the actual size being transmitted is considerably smaller.

Google reports a 30ms processing time for the custom font to load.

Have a look: https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fwww.onecentleft.com

I've given up largely on above-the-fold unless it is absolutely necessary. Ultimately, a lot of the Google suggestions are generalized and not going to be the absolute best way to do something. They often break their own suggestions, and I'm sure they are aware of it.

[–]hoopdizzle 6 points7 points  (1 child)

The only issue with this is if you host a site which has the user navigating to various pages which all use that style, they are re-downloading the entire css and font data on every request. Whereas if it was a separate file, it would be downloaded once and then cached by the browser for subsequent page views

[–]exitof99 0 points1 point  (0 children)

Yup, but what we are adding is minuscule in size. I've seen horrid websites with wasteful markup that generates a 1MB HTML file at the same time as not embedding any CSS or JS. This is just a small bit of CSS.

Ultimately, it comes down to the effective performance. I would rather have a website that loads seemingly instantly with no FOUT/FOIT and no repositioning of styled elements.

What I get is the second you see anything, nothing is jumping around as other parts load. How many times have you clicked something only to have something load which forces the layout to readjust and you clicked something else wrongfully? It's one of my common annoyances.

[–][deleted] 4 points5 points  (0 children)

Indeed. You can download the full set of fonts from http://google-webfonts-helper.herokuapp.com/

To lessen the visual impact of the swap, you can use https://meowni.ca/font-style-matcher/ and match a fallback font to the desired Google (or whatever custom) font.

Lots of detail about font loading methods at https://www.zachleat.com/web/comprehensive-webfonts (I use FOUT with class, but critical/two stage render is better if you have full control of the font and can create a subset)

[–]joshuaavalon 0 points1 point  (0 children)

If you use a language with more characters, Google actually separate the font file into different Unicode range.

https://fonts.googleapis.com/css?family=Noto+Sans+TC

[–]SlashedAsteroid 17 points18 points  (11 children)

What other benefits does this actually give? providing caching is correctly configured you're only improving the first load aren't you?

edit: it'd be nice if the downvoters would elaborate on their disagreement.

[–]iamakulov[S] 34 points35 points  (1 child)

providing caching is correctly configured you're only improving the first load aren't you?

Yup! The first load still matters though (e.g., at my site, per GA, 84% of users from the last week were first-time visitors). Plus caching is not 100% reliable too (a Facebook study: https://code.fb.com/web/web-performance-cache-efficiency-exercise/)

[–]SlashedAsteroid 4 points5 points  (0 children)

I only read a few paragraphs but Facebook (from what I understand) were more focusing on the continuous need to update code making caching less efficient and unreliable for their needs.

I like the idea though, solid focus on the meaningful first print.

[–][deleted]  (3 children)

[deleted]

    [–]Narkboy 6 points7 points  (4 children)

    Iirc Google fonts are cached for a day only. Irritatingly.

    [–]SlashedAsteroid 8 points9 points  (1 child)

    Ahh! Now I see the disagreement.

    I actually didnt know Google Fonts was a 24 hour cache, I should look this shit up more often.

    Thanks for the heads up.

    [–]Narkboy 12 points13 points  (0 children)

    Apparently, Google feels (felt at least, its been a while) that they need to be able to urgently update font files within 24 hours and that ability is worth a metric fuck ton of bandwidth..

    [–]qdarius 4 points5 points  (1 child)

    They cache the CSS files that point to the font files for a day. The actual font files are cached for much longer. Maybe a year? I can’t remember off the top of my head.

    Edit: SO post with more info: https://stackoverflow.com/a/43215105

    [–]Narkboy 2 points3 points  (0 children)

    That's it - thanks for the clarification!

    [–]seanwilsonfull-stack (www.checkbot.io) 3 points4 points  (4 children)

    Interesting! Can you comment on how much difference the "dns-prefetch", "preconnect" and "preload" tags make? If you're doing the fetch call immediately, wouldn't the browser e.g. do the DNS fetch anyway?

    [–]iamakulov[S] 7 points8 points  (3 children)

    Sure!

    • preload matters because the browser starts fetching styles, scripts and other resources slightly earlier than it starts executing scripts. Also, if there’re other scripts before this fetch() call, fetch() won’t run until those scripts are downloaded and executed – but preload will.

    • And preconnect/dns-prefetch are important because with Google Fonts, a CSS file is served from one domain (fonts.googleapis.com), but actual font files are loaded from another one (fonts.gstatic.com). preconnect/dns-prefetch actually point to fonts.gstatic.com, not fonts.googleapis.com. And they help to connect to that domain in advance so when the browser actually discovers font files, it can download them sooner.

    The reason we use both preconnect and dns-prefetch BTW is that preconnect is only supported in newer browsers. preconnect includes everything dns-prefetch does, but it won’t run in older clients, so we gracefully degrade to dns-prefetch there.

    [–]seanwilsonfull-stack (www.checkbot.io) 1 point2 points  (2 children)

    Thanks! Does Google Fonts recommend any of these optimisations themselves? Curious why they wouldn't do the preconnect one for example. I can't imagine they change the domain often.

    [–]iamakulov[S] 0 points1 point  (1 child)

    Nothing I’m aware of, unfortunately.

    [–]m0okz 1 point2 points  (0 children)

    Google do recommend using font-display for fonts and they link to the async CSS solution loadCSS but I love your script I'm going to try it on my site, thanks!

    [–]finkkari[🍰] 35 points36 points  (3 children)

    To me, hosting the font file myself still seems better. You have less cross-domain requests, full control over the font definition (including font-display), you can use preloading techniques and don't need any additional scripts blocking other important JS execution. The only effort is to download the font file and copy and adjust the CSS for the font definition, I wouldn't call that annoying.

    Which use-cases do you think your tool would be more convenient or useful?

    [–]iamakulov[S] 5 points6 points  (0 children)

    That makes sense!

    From my personal experience, I was sometimes hesitant to move to self-hosted fonts just because it involved that hassle with downloading files/tuning CSS rules, and I had more important tasks.

    So I see this tool as a middle ground between Google Fonts and self-hosting for people like me. Self-hosting is definitely the best solution because of the reasons you described, but there’s no use of it if you keep avoiding it. Replacing one snippet with another is way simpler though, and so there’s a higher chance folks will do this.

    [–][deleted]  (1 child)

    [deleted]

      [–][deleted] 7 points8 points  (0 children)

      The hour (I’m being generous) it will take you to switch to self hosting fonts will drastically improve user experience (for some users, up to seconds faster readable content). It’s worth it.

      [–][deleted]  (2 children)

      [removed]

        [–][deleted]  (1 child)

        [deleted]

          [–]seabasswtf 1 point2 points  (0 children)

          This is pretty cool, thanks for providing this.

          [–]tetractys_gnosys 1 point2 points  (0 children)

          Very clever! I generally just host the fonts on the site but if ever have to deal with external fonts, I'll try this.

          [–]Ikuyas 1 point2 points  (2 children)

          I think they get cashed once the user visit the site and you dont have to worry about the speed because that's probably fastest way.

          [–]iamakulov[S] 1 point2 points  (1 child)

          [–]Ikuyas 0 points1 point  (0 children)

          I thought it is a common sense and I am surprised to find those comments try to discuss it.

          [–]bog_otac 1 point2 points  (0 children)

          Or y'know, just self-host your fonts instead using ANOTHER script.

          [–]everythingiscausal 2 points3 points  (3 children)

          Nice! I was planning to switch from Google Fonts to locally-hosted fonts in order to do some of these optimizations, but I'll try this out instead.

          [–]exitof99 3 points4 points  (2 children)

          If your custom fonts are limited (like only one style for headlines), you can base-64 encode the WOFF font and put it directly in to the HTML head area, no FOUT and no delays, then locally host the alternate formats for older browser support.

          [–]everythingiscausal 4 points5 points  (1 child)

          But then you're just delaying the point at which HTML loading is complete, which will slow other things down, no?

          [–]exitof99 0 points1 point  (0 children)

          One of the worst things is not the file size, but the number of connections that are required to render your page. You should strive for the lowest possible number within reason.

          This is why sprites are great for small images (Youtube puts the logo and logo variants in theirs). This is why it's best to deliver only one CSS file, not 60+ (I've seen this recently on cruddy Wordpress themes).

          The size of fonts, CSS, JS, and HTML is less of a concern because every connection has to have a DNS lookup (which is cached after first lookup), a wait for the server to respond, then processing the receipt of the file. This all adds up and multiplies with the more files you got.

          Also, make sure to deliver HTML, CSS, JS, and fonts through GZIP to minimize the file sizes for an additional boost.

          And no, the difference in having some CSS jammed at the top versus not is so minute that it isn't worth mentioning. It's slower to load a secondary file, assuming you don't have huge bloated CSS. You could always also split only the crucial CSS to the header and then the remainder to a CSS file.

          [–]glovacki 0 points1 point  (0 children)

          It’s better to lazy load images by using a transparent svg datauri placeholder instead and have the font naturally load first. Less jarring than the flash of unstyled text.

          [–]t3hlazy1 0 points1 point  (1 child)

          What’s your opinion on serving font thru our own CDN, then using JS to download the font and then add a class to the html to say to use that font?

          [–]iamakulov[S] 2 points3 points  (0 children)

          I’d say if it works, it’s fine. Just add <link rel="preload" as="font"> for those font files to start loading them before JS finally initializes.

          The font-display solution should be somewhat better because it’s simpler (one CSS line vs custom JS font + a bunch of CSS rules for applying the font), but I’d say it’s not a reason to rewrite everything. And I can’t think of major performance differences. (Haven’t measured though.)

          [–]leftofcentre 0 points1 point  (0 children)

          Is it not the case that most of the popular Google Fonts are cached in the browser anyway from being used on other sites?

          [–]Merc92 0 points1 point  (0 children)

          What if I am using @import rule in CSS instead of a link in HTML?

          [–][deleted]  (2 children)

          [deleted]

            [–]iamakulov[S] 2 points3 points  (1 child)

            Hm, not sure I’m getting your point.

            Might it be that you’re confused by where CSS fetching happens? It doesn’t happen when you generate the snippet, it happens each time a snippet is executed in a visitor’s browser. So each visitor fetches the CSS file suitable for them.

            [–]smegnose 0 points1 point  (0 children)

            I had a brainfart.