Joshua Johnson

Front-end Developer @ BT

Back to the future - Inlining CSS

With the fervour of responsive web design subsiding, us front-end developers have finally begun to discuss and debate how we might actually make the websites we produce, er, responsive.

Regardless of whether your beautiful new website caresses its users with thoughtful interface design; if it does not load within a second, your users will experience a lag that leads to impatience and often frustration – not something we should be aiming for, especially at a time where the popularity, proximity and fluidity of native applications is overshadowing the advancements coming to the web.

So putting latency aside – a delay relating to the server, what can front-end developers do to performance enhance our websites and web applications?

Or put another way, how we can achieve the transition below (using Google PageSpeed as a benchmarking tool) on the websites we release into the wild?

Google page speed comparison

Inlining critical CSS

It may sound strange to hear “inlining” cropping up as a suggestion in a blog post related to front-end development, as we have spent the past few years conditioning ourselves to detach our CSS and JavaScript from our HTML. Attempting to compete with the the responsiveness and immediacy of native applications however requires us to to break some previously well-intentioned rules.

To understand the benefits of inlining CSS into our HTML, it’s important firstly to know why loading CSS in an external stylesheet can in fact be slowing us down. To avoid continuous re-painting, browsers will block rendering of your page until all CSS has been loaded. You may have noticed that web designers often discuss FOUT (flashing of un-styled text), but you will rarely (if ever) hear discussions regarding un-styled content being presented to users – this is because browsers decide the rendering path for us whereas fonts do not.

For small websites this may be exactly what you wish to happen, however for the the majority of websites, there is no real necessity to load all of your stylesheet when a user first visits your website; their time is precious and we need to respect that. Luckily, we can resolve this with, yes you’ve guessed it, inlining our critical CSS to improve our site’s first render speed.

By inlining critical CSS (namely areas such as grid, header and logo styling) in the head of our HTML, the browser is no longer required to immediately download a large external CSS file and can therefore render our site faster to users. Once the page has initially rendered, we can then asynchronously load our full stylesheet behind the scenes, so by the time a user is ready to interact with your website, remaining styles further down the page have been painted by the browser.

Taking this step further, we can set a cookie when we first asynchronously load our full stylesheet to identify whether the full CSS file is cached by the browser and can therefore identify whether to load the stylesheet conventionally or via the inline method.

Using PHP as an example (not a requirement), this will look something like this in the head of your HTML:

<?php
    // Asset version number - change when CSS needs updating
    $version = '20150829'; 

    // If cookie has been set and matches the version above, load CSS normally
    if (!empty( $_COOKIE[ 'csscached' ]) && $_COOKIE[ 'csscached' ] === $version) :
?>
    <link rel="stylesheet" href="/assets/styles/main.min.css?v=<?php echo $version;?>">
<?php 
    else : 
?>
    <style>
        <?php include 'critical-css.php'; ?>
    </style>
    <script>
        /*!
            loadCSS: load a CSS file asynchronously.
            [c]2014 @scottjehl, Filament Group, Inc.
            Licensed MIT
        */
        function loadCSS(e,t,n,r){...};
        function onloadCSS(e,t){...};

        var ss = loadCSS( "/assets/styles/main.min.css?v=<?php echo $version;?>" );

        onloadCSS(ss, function() {
            var root = document.documentElement;

            // Add class to HTML element when full stylesheet has downloaded
            root.className += " full-css";

            if (root.classList) {
                // For modern browsers, remove the 'no-full-css' class (set on the HTML element) the modern way
                root.classList.remove('no-full-css');
            } else {
                // Otherwise remove the 'no-full-css' class the archaic way
                rootclassName = root.className.replace(new RegExp('(^|\\b)' + no-full-css.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
            }
        });

        // Set cookie with far expiry date after first load
        document.cookie = 'csscached=<?php echo $version;?>;expires="Wed, 20 Jan 2040 10:20:10 GMT";path=/';
    </script>

    // Load stylesheet conventionally for users with JavaScript switched off
    <noscript>
        <link rel="stylesheet" href="/assets/styles/main.min.css?v=<?php echo $version;?>">
    </noscript>
<?php 
    endif;
?>

Not only does this method increase the psuedo-performance of our website for users, it also has the added benefit of ensuring we keep our CSS modular. If you can easily include specific modules from your main stylesheet into a critical CSS file without your site’s structure and fundamental appearance breaking due to an over-reliance on the CSS cascade, you’re in a good position to inline your CSS.

If you are already equipped and accustomed to task runners within your development workflow, this approach can also be easily integrated using the following tools:

The future is bright

Fortunately with HTTP2 coming to the web in the near distant future and the changes that poses to the rendering path – especially the ability to conditionally load CSS without the performance hindrance of multiple HTTP requests – this method will become obsolete and once again the browser will be more competitive with the native-app industry. Combined with the offerings of Service Worker and the affordances of a more controllable cache, CSS will eventually pose far less of an issue in relation to the page-rendering speed of websites, meaning the speed to which our users can read the content they came for will only improve with time. Until then, inlining CSS is back on the menu!

In action: Twine Intranet

Further viewing: Patrick Hamann – CSS and the Critical Path