In the never-ending process of improving my web site, or what my wife calls "fiddling about with it", I've been working on another of the "opportunities" Google has presented me with. It seems that declaring CSS stylesheets as, well, CSS stylesheets, is a bad thing as far as Google is concerned, because that makes them "render-blocking resources". I first encountered this back in Updating An Old Web Site To HTML5 - Part 2 - More Google Analytics "Opportunities", so let's take a look and see what we can do about this, and see whether we can asynchronously load CSS stylesheets.
Using this code is quite normal, and declares the stylesheet and its location for web pages on a display:
<link rel="stylesheet" type="text/css" href="/css/2020.3/historicmedway.min.css" media="screen">
I also have another stylesheet used by my "Cookie Permission" script, because until 11pm GMT on 31 December 2020 my main target country was in the European Union, and between them they give me the following result in Google Analytics:
If you have multiple CSS stylesheets, there's also a good chance you'll see a warning to "Avoid chaining critical requests":
This looks quite bad, because these reports are for the mobile versions of the pages. This is of course the version that Google has decided to rank my site on! The score for the mobile version of the page at this stage is 48-53, with a desktop score of 94-98. There are a couple of other reasons why the mobile score is so poor, but I'll cover those in future articles.
It's fairly easy to stop regular scripts from blocking the page load process by using the
async property to make them load asynchronously. Making CSS stylesheets load asynchronously was a little harder to do.
Looking around the web it seemed that my original way of including CSS files was incredibly old-fashioned, and that the latest fashion is to use the
rel=preload attribute of the link element. The browser will load the CSS file, but won't apply it because the file isn't flagged as a CSS file yet. What you need to do is to wait until the file has loaded, and then set the
rel attribute to
stylesheet. Something like this:
<link rel="preload stylesheet" href="/css/2020.3/historicmedway.min.css" as="style" onload="this.onload=null;this.rel='stylesheet';this.media='screen'"> <noscript><link rel="stylesheet" href="/css/2020.3/historicmedway.min.css" media="screen"></noscript>
this.onload=null // Remove the onload event handler from this element on this page this.rel='stylesheet' // Set the rel type to stylesheet this.media='screen' // Use this CSS for screen displays only
Looking at the actual
<link> elements, you'll see that the actual value of the
rel attribute includes
stylesheet already. This seems odd, but it's because Firefox doesn't apply the CSS styles without it, so you need to include it here.
Once the CSS links have been changed to load asynchronously, the reports of "Render blocking resources" and "Avoid chaining critical requests" disappear from the Google Analytics report, so that seems to be a positive thing. Looking at the PageSpeed score for the updated page shows something a little puzzling though:
This looks like there has been no improvement at all – if anything, things are a little worse. That's not what I wanted at all.
Looking at the new versions of the Google Analytics reports, suddenly there are warnings of "Layout shifts" that have appeared. What's happened?
The problem that's now happening is this: By making the CSS stylesheets load asynchronously, the browser loads and renders the page before it obtains the stylesheets. Once the styles are available, the browser re-renders the page, and has to recalculate the layout that's now changed.
That's Not What I Expected!
It's not obvious when you read the Google Analytics documents, or many of the other articles around the web, but by fixing one problem we've created another.
Thankfully this new problem isn't too hard to fix, although it can be a bit "fiddly". What you need to do is to move the most important parts of your CSS into the HTML5 document that makes up your page. For those of us that have done this sort of thing, or indeed any software development, for a while, that seems counter-intuitive – the separation of presentation and data is usually kept as far apart as possible. What you learn quite quickly as an Engineer though, is that these "rules" are usually more "guidelines", and for performance you often need to make compromises.
To start, put a <style> element in the <head> section of your HTML5 page(s), above the point where you import the other stylesheets. In this, put the definitions of all, or at least most, of the styles you use on that page. There's actually a level of layout shifting that Google will tolerate, although it isn't much. The "fiddly" part is that you need to work out which styles to include, and which you can afford to avoid. Ideally you want the same styles in every page for simplicity, but that will take some work.
What you COULD do as well is to synchronously load some of the styles in one stylesheet, and asynchronously load the ones that appear further down the page in another. This all gets quite involved quite quickly (I DID warn you) so it's probably best to keep things simple to start with.
It's not always obvious, but simply running through the list of opportunities and warnings that Google reports isn't always the simplest way to fix things. In this case, for example, one fix leads to another problem that can get quite complex. Eventually fixing both will improve things though. Bear this in mind whenever you make any changes to fix problems on your site.