Critical CSS for blazing fast page loads

Speed up page rendering in the browser by eliminating render blocking resources, discussing manual and automated methods

Front-End web performance has always been a hot topic but it started getting even more attention in recent years. It is 2015 and completely acceptable that users want their sites load faster. Also, it is 2015 and apps and pages are packed with features, new libraries, or application logic delegated to the browser. Payload and demand are both higher.

Contrary to the above, I wager optimising for performance started to receive more attention only when Google PageSpeed Insights updated their method around a year ago, now expecting web sites to match a higher standard. CSS files added via a <link> tag are render-blocking, and the average site gets a warning that none of the above-the-fold content can be rendered without waiting for those files to load. And the suggested solution is to inline the critical portions of those files directly into my HTML.

How this matters

Many people already covered this in a number of places across the web, so I give a rather short explanation here. External stylesheets prevent browsers from rendering the page until all the CSS is downloaded and parsed. They don’t start painting the page. Also, there is a so-called initial experience which should fit into 14 kB and into a single HTTP request. So this initial experience is served without extra round-trips and that means that critical portions of the stylesheet should be inlined. This, when properly implemented, greatly improves the perceivable speed of the page.

The rest of the styles then can be loaded asynchronously via JavaScript. There are quite a few implementations around, my approach is the following:

<!doctype html>
<html>
<head>
  <style>
    /* Critical CSS inlined here */
    body { margin: 0;}
    p { font-family: serif; }
  </style>
  <noscript>
    <!-- When JavaScript is disabled, load the rest of the styles in a tag -->
    <link rel="stylesheet" href="styles.css">
  </noscript>
  <script>
    // Load the non-critical CSS asynchronously.
    (function(w, d) {
      var raf = w.requestAnimationFrame || w.mozRequestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || false,
          // Paths to CSS files and their associated media are defined in this array.
          f = [
            ['styles.css','all'],
          ],
          // The function which generates the link tag.
          l = function(s) {
            for (var i = f.length; i--;) {
              var l = d.createElement('link'), s = d.getElementsByTagName('script')[0];
              l.rel = 'stylesheet'; l.href = f[i][0]; l.media = f[i][1]; s.parentNode.insertBefore(l, s);
            }
          };
      // Use requestAnimationFrame if available, fall back to setTimeout otherwise.
      (raf) ? raf(function(){ l(f); }) : setTimeout(function(){ l(f); }, 1);
    }(this, this.document));
  </script>
</head>

How to define what is critical

Ideally, styles should be included for elements which fall onto the viewport when the page is first loaded. Simples.

Responsive sites serve the same markup and content to their users so the critical CSS should ideally be added for at least the most common viewports.

Large sites on a CMS with multiple, differently styled sections, therefore the same set of critical CSS cannot be applied across the templates.

Large responsive sites on a CMS are supposed to serve critical CSS for each section or page type, including the most important media queries. So implementing this is not an easy ride at all.

Determining critical CSS manually

The most reliable method, as usual, is to hand pick critical CSS. This ensures that the inlined styles accurately reflect above-the-fold elements. The process can be painful though, and it may not always be implemented in any given workflow.

SASS files could have two main .scss files defined, one for the critical CSS and one for the rest.

// critical.scss - to embed inline
@import "layout";
@import "header";
// styles.scss - to load asynchronously
@import "everything-else";
@import "footer";

Compass users can add the Jacket component which enables conditional loading by providing context for style blocks or individual rules. It is a rather clever piece of code. Example use is to wrap blocks of code in the jacket mixin:

// _common.scss - containing both critical and non-critical rules
@include jacket(critical) {
  body {
    margin: 0;
    font-family: serif;
  }
}

@include jacket(default) {
 .footer {
    color: blue;
  }
}

Then the two master files can define different contexts in the $jacket variable

// critical.scss
$jacket: critical;
@import "common";
// styles.scss
$jacket: default;
@import "common";

libSass users can still benefit from using this approach as though Jacket is advertised as a Ruby gem for Compass, it essentially is a single partial file without any dependencies which can be included in your SASS project just like any other partial.

I like this method for its simplicity and it works well for smaller projects. This very website makes practical use of this, and to great satisfaction.

Automated ways

Several automation tools are available already which enable the delegation of generating critical CSS to a build pipeline.

Critical by Addy Osmani a tool to extract and inline critical-path CSS from HTML. It takes a HTML page, parses it and generates CSS for a given HTML page. It actually depends on Penthouse, see below.

Penthouse by Jonas Ohlsson – “Penthouse is a tool generating critical path css for your web pages and web apps in order to speed up page rendering”

CriticalCSS by the Filament Group – “Finds the Above the Fold CSS for your page, and outputs it into a file”

critical-css is my own attempt, albeit similar to the listed solutions above. It tries to solve the problem in a more holistic way, analysing all CSS at once. It is a work in progress and not currently production ready – at least not for projects I aim to use this on.

Still waiting for PhantomJS 2.0 so it can analyse ::before and ::after rules which are widely used in many grid layouts.

There are also a few issues to iron out and I will cover the capabilities of this in a separate post.

Final thoughts

After the initial assessment of the possible solutions I quickly came to realise that there is no silver bullet here. Most of the time I will try to use jacket to separate styles manually. If that is not possible then fall back to one of the automated ways, keeping in mind that they probably never can be accurate.

What is your approach?

Last updated on by Attila