Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 986
Bram Vanroy Avatar asked Sep 06 '15 12:09

Bram Vanroy


3 Answers

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.

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.

like image 198
Shikkediel Avatar answered Oct 02 '22 02:10

Shikkediel


I'd suggest you have a look at this repository: https://github.com/filamentgroup/loadCSS

LoadCSS is a very savvy script by the people at Filament Group to lazyload stylesheets in a way, that most browsers play along nicely. If Javascript is disabled or for whatever reason the script won't work, there are fallbacks included as well.

like image 27
Marco Hengstenberg Avatar answered Oct 02 '22 04:10

Marco Hengstenberg


To answer your specific questions:

  1. Javascript is NOT required to accomplish what you want. There are several methods for loading CSS in a non-blocking fashion. Some rely on JS, some do not. Filamant Group's LoadCSS is a JS option. Critical Path's CSS Generator is one non-JS method. The process of generating the critical CSS can also be automated using Gulp or Grunt.

  2. While your method will work it's not recommended. Google recommends loading the non-critical CSS files using Javascript so the CSS is loaded into the head after the page has finished loading.

Alternatives

There are 2 ways to improve your current implementation.

  1. It sounds like you're currently loading 2 CSS files - your site's CSS, and font-awesome.min.css. This requires 2 HTTP requests and will cause a small delay. Combine the CSS from the 2 files into a single CSS file.

  2. As the others have pointed out Google recommends inlining the critical CSS into the head of the page, and loading the remaining CSS in a non-blocking way. An alternative option I've found useful is to load the entire contents of the CSS into the head of the site within a style tag. I recommend this only if your CSS file is relatively small and minimized.

 <head>
   <style>
     // ALL YOUR CSS
   </style>
 </head>
like image 39
Brett DeWoody Avatar answered Oct 02 '22 04:10

Brett DeWoody