Bram Vanroy Bram Vanroy - 5 months ago 12
CSS Question

Optimising CSS delivery according to Google

So I was running my site through Google's PageSpeed Insights and it told me that I could improve CSS delivery by delaying loading non-critical resources. In particular it referenced to the inclusion of font awesome:

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">


I figured I could delay its load by simply putting it before the scripts before the closing body tag, like so:

...
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="js/min/scripts.min.js"></script>
</body>
</html>


However! When taking a look at Google's documentation on delivery speed, I found two different solutions.

In my native language (Dutch; you can change the language of the documtnation in the right lower corner) the documentation states that I should place non-essential css below the closing html tag. This seems awefully odd to me. Wouldn't this complicate things when trying to access the file through JS? Why wouldn't you place it before the closing body tag?

...
</body>
</html>
<link rel="stylesheet" href="small.css">


However, in the English documentation things get more complicated and require JavaScript:

...
<script>
var cb = function() {
var l = document.createElement('link'); l.rel = 'stylesheet';
l.href = 'small.css';
var h = document.getElementsByTagName('head')[0]; h.parentNode.insertBefore(l, h);
};
var raf = requestAnimationFrame || mozRequestAnimationFrame ||
webkitRequestAnimationFrame || msRequestAnimationFrame;
if (raf) raf(cb);
else window.addEventListener('load', cb);
</script>
</body>
</html>


Why would JS required? Isn't that a bit over the top?

What is wrong with my approach? Why can't you load non-critical CSS before the closing body tag?

Answer

An external stylesheet will block rendering of the page until it has been fully loaded. Google is recommending to place the style that is needed for the initially visible (critical, above the fold) part of the document inside <style> tags in the head (the only place where you can define non-inline style) to avoid this render-blocking. The non-critical style (what you don't directly see when you land on the page) is added as an external stylesheet inside the head after the HTML has been read. This way it's rendered first and only then will all other styles be loaded. All for the sake of showing the visitor the content as quickly as possible and not letting her/him wait any longer than necessary.

It think for most cases it is over the top what Google is recommending and they're just being freaky about a few milliseconds - their approach only makes sense somewhat if the CSS is huge. But I think it's hard, if not near impossible, to maintain with the current tools available. What for example if it is a returning visitor that has scrolled down the page at an earlier point and will automatically land there again (Opera is a browser that is very stubborn with this)? For that alone you'd need more JS and possibly juggle with the styles. That can't be a good way to go. And even for the initial display you'd have to stuff some media queries inside the head directly in order to try and get things right without resorting to full screen sections. That's all quite counterproductive and overcomplicating.

Putting <style> or <link> tags outside the head section might work but it's incorrect. I'm sure Google's not on that standpoint anymore and that the English version is the only valid documentation. Edit - see comments for a nuance on this.

Even if one does do it the google way and get a 'good' score on PageSpeed Insights that doesn't mean too much. You could still hide the whole page and only show it when everything has been loaded (which isn't unusual) without that effecting the score.

Edit - the above paragraph isn't valid anymore since they now seem to take this into account.

I've personally decided to ignore it until they've implemented a feature where you can load CSS asynchronously (like you can with JavaScript and async already) like is announced in the documentation. It would still require a special use case then but at least you could build a framework for it. Not a fan of plugins for something rather trivial myself.

One important thing is missing in the Google documentation - providing a fallback when JavaScript has been disabled. Luckily with HTML5, <noscript> tags can be used for this :

<head>

/* other stuff */

<noscript><link rel="stylesheet" href="small.css"></noscript>
</head>

Sidenote - Google's own analytics script will prevent a perfect PageSpeed score because of the (logically) quick cache expiration they've set on it. Figure that.

Comments