Cache, Cookies and render blocking webfonts

Hi fellow Kirbyists,

I want to speed up web fonts by loading them, so they’re not blocking the rendering of the page, and basically turn a multisecond loading-experience into a subsecond one.

So I load the fonts async through js and throws a class with the webfont fam, on the doc - now, in js I’m also dropping a cookie so that return-visits (with the webfont in browser cache) can have the font class applied instantly to prevent a FOUT (flash of unstyled text), for this I’ve tried the cookie helper or API that Kirby ships with, but it couldn’t read the cookie I made from the js, so I then used a regular PHP request reading for the cookie, which works fine, except when I’m using kirby to cache all pages, as the class will then be written to the cache and end up making a render blocking load of the font for everyone - does the same thing happen if I got the built in Kirby cookie handling working? :grin:

Using the HTML cache together with user-specific output (like classes) generally does not work. The page is cached once – and on the next visit, the template isn’t even called. That’s basically the purpose of the cache. :wink:

You can however set the class in the frontend using JS or disable the HTML cache (content cache is no problem).

Thanks @lukasbestle :smirk:

What do you mean by

If I set the class with JS, it will still result in a FOUT for each page load though, due to the async script and font loading.

I’ve actually hijacked the HTML cache, to send it through https://github.com/drdk/dr-css-inliner server side, to automagically inline Above The Fold styles, which sends mobile performance through the roof :grin:

I’m just experimenting to find the best way to load web fonts in a non render blocking manor, and thought that the Kirby cookie functionality maybe magically got excluded from the HTML cache.

Anyway, thanks for your input.

No, cache is cache. No custom code (except plugins) will run.

Hm. I was referring to the separate HTML cache in Kirby 1 (you could disable that so only the site’s content would be cached in a single file but no HTML would be cached). It seems like this feature does not exist anymore.

It looks like you will need to completely disable cache for your code to work. Maybe Kirby will support partial renders in the future (so that cached HTML can still contain a bit of PHP).

I created an issue over on GitHub.

1 Like

I’m curious as to what your setup looks like… Do you use https://github.com/typekit/webfontloader to load your fonts? If you do, your fonts should already be cached on subsequent visits. There are callbacks you can hook into to apply custom classes to your html.

In my setup, I’m also using “critical css” for above the fold styles which I’m inlining and then loading the actual stylesheet asynchronously. This does indeed help with very fast initial rendering of the page.

My setup looks like this:

  • Using

in the head to load my fonts from google for all pages except the home page

  • Inlining above the fold styles on the home page (only)

  • Load fonts through a seperate stylesheet which is loading asynchronously (there’s a small “loadCSS” function in the head

  • The seperate fonts.css stylesheet looks something like this: (it’s what google returns from the above link)

    @font-face {
    font-family: ‘Open Sans’;
    font-style: normal;
    font-weight: 300;
    src: local(‘Open Sans Light’), local(‘OpenSans-Light’), url(https://fonts.gstatic.com/s/opensans/v13/DXI1ORHCpsQm3Vp6mXoaTQ7aC6SjiAOpAWOKfJDfVRY.woff2) format(‘woff2’);
    unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
    }

In my home page template it looks something like this:

<head>
    ...
    <?php if ($page->isHomePage()): ?>
        <style><?php echo file_get_contents($site->url() . '/assets/styles/critical.min.css') ?></style>
        <script>(function(u){function loadCSS(e,t,n){"use strict";function r(){for(var t,i=0;i<d.length;i++)d[i].href&&d[i].href.indexOf(e)>-1&&(t=!0);t?o.media=n||"all":setTimeout(r)}var o=window.document.createElement("link"),i=t||window.document.getElementsByTagName("script")[0],d=window.document.styleSheets;return o.rel="stylesheet",o.href=e,o.media="only x",i.parentNode.insertBefore(o,i),r(),o}for(var i in u){loadCSS(u[i]);}}(['assets/styles/fonts.css','assets/styles/styles.min.css']));</script>
        <noscript>
            <link href="assets/styles/fonts.css" rel="stylesheet" type="text/css">
            <link rel="stylesheet" href="assets/styles/styles.min.css">
        </noscript>
    <?php else: ?>
        <link href='https://fonts.googleapis.com/css?family=Open+Sans:300,600' rel='stylesheet' type='text/css'>
        <?php echo css('assets/styles/styles.min.css') ?>
    <?php endif; ?>
</head>

I’ve used this tool to extract my critical css: https://github.com/addyosmani/critical
It did need some custom editing but that was no big deal for me (removed some media queries, etc)…

Caveats:
On inital rendering of the page the font(s) may still be loading asynchronously, so whenever the font has finished loading, the text on the page that relies on the font may be flickering/refreshing for a brief moment to reflect the new font. I don’t mind this personally.

Here is an interesting article in case you haven`t read it: https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/webfont-optimization

Let me know what you think or if you have any other suggestions / remarks. :blush:

Hi @nockedout,

What an awesome read - sounds like we’re totally on the same page (no pun intended)! :smiley:

No, I was actually using https://github.com/bramstein/fontfaceobserver to load my fonts. But that way I think i had to manually extract the Google Webfont @font-face’s (i did that by visiting the link’s href that you posted, with a couple of different UserAgents, as Google is smart about serving certain font formats dependent on your UserAgent) but that might have been redundant, I just blindly assumed ahead of time that I’d ran into some CORS restrictions otherwise.

Anyway, a post from the Filament Group about how they got their rendering down to just 300ms with webfonts, can be found over here https://www.filamentgroup.com/lab/font-events.html it better explains what I was trying to do :smile:

Render blocking CSS

For this I’m using https://github.com/drdk/dr-css-inliner (actually made by our largest national TV network here in Denmark) with that I can actually overwrite my regular stylesheet linked in the head (for fallback and pre-kirby-cache rendering), I’ve installed PhantomJS on my server, extended Kirby with a custom “kirby.cache.set” hook and made a plugin which hijacks the kirby cache to set a .lock file to ensure a single inline process at a time, and kicks this stuff off async, something along the lines of

$atf = shell_exec(
    "my command " 
    . escapeshellarg(serialize($payload)) 
    . " > /dev/null 2>/dev/null &"
);

So it’s 100% automated and specific to each individual page, without slowing down the first visit to the page, as the first visit (pre kirby cache) isn’t inlined but instead used to kick off the ATFinliner.

And then of cause, at the end loading in the entire stylesheet async with JS.

I know it’s a really rough and confusing explanation, but I don’t have time to polish my entire code off and share it in here right now (although it works pretty well, it’s also a pretty crazy way you have to go about it with Kirby still) - my budget dictates 100% client work atm :grin:

Think I’ve found my solution. Instead of dropping a cookie, I might resort to localStorage - this check might need to be render blocking, but I think I’ll give it at try and see what happens and what I can come up with.

Someone mentions a similar technique over here https://github.com/bramstein/fontfaceobserver/issues/40

Btw. Highjacking the kirby cache, also allows me to automatically parse src, srcset and make filename cachebusting throughout the entire template :thumbsup: