mm

September 16, 2015 WATB Team
Follow me on Twitter

Many developers and designers these days are still trotting out the misnomer that it’s acceptable for web pages to run into the many MB, make dozens (if not hundreds) of http requests – because we have faster internet speeds now. Sure, we do, but that doesn’t mean websites shouldn’t be as fast and lightweight as possible. As a web design agency, we take the issue of speed very seriously.

Still think a slow website is acceptable?

How Loading Time Affects your Bottom Line – Kissmetrics

https://blog.kissmetrics.com/loading-time/

To quote: “A 1 second delay in page response can result in a 7% reduction in conversions.”

 

418conf

Up and coming in October on the 22nd is the second 418conf, and whilst changing the details on the site I took the opportunity to refresh it. With the new design and layout I also felt the need to improve the page speed, for such a simple one page site it was quite slow. I really took my task to the nth degree, but I’m pretty sure it was worth it, not just as an experiment but also in making the site incredibly quick.

To achieve this, I used a few additional tasks in my gruntfile: grunt-inline, grunt-contrib-htmlmin, grunt-processhtml, grunt-uncss.

The first task we use is uncss. Uncss removes unused CSS from your file, resulting in a leaner and faster website. Particularly when we use frameworks, unused CSS can make a CSS file much bigger in terms of filesize than it needs to be. Uncss traverses your HTML and parses your CSS, removing any rules it can’t find in the HTML. Effectively, it spring cleans your CSS for you.

First step – uncss

Uncss is not an excuse to be lazy with frameworks and you should still selectively load only the components you are using. But it’s an extremely valuable tool that even for the most careful of coder can knock many kb of your CSS filesize.

Below is my uncss task. Because I was dynamically loading a Twitter feed, I needed to exclude my Twitter classes as uncss would not find that markup within my index.html file (it’s dynamically generated). As we are also loading Typekit fonts, I also needed to exclude the Typekit stylesheet.

    uncss: {
      dist: {
        options: {
          ignoreSheets : [/use.typekit/],
          ignore: ['.twitter','#twitter'],
          stylesheets: ['css/app.css']
        },
        files: {
          'css/tidy.css': ['index.html']
        }
      }
    },

Despite my attempts to code as efficiently as possible, uncss still managed to get my CSS file down from 30.92kb to 8.8kb. That’s a staggering 72% decrease in filesize.

Second step – processhtml & inline

Inlining your CSS helps you cutback on http requests and ensures that what we call critical CSS is rendered as quickly as possible. There are also methods of only requesting the rest of your CSS once your “above fold” is ready. You can read a fantastic article about this over on CSS-Tricks: https://css-tricks.com/authoring-critical-fold-css/

For the sake of 418conf however, splitting an already small CSS file into above-fold and below-fold rules wasn’t likely to be beneficial. I therefore inlined the whole CSS file in the head of the document.

To do this, I used the processhtml and inline grunt tasks. We use processhtml because we need to swap out our old app.css file for our tidy.css file created by uncss.

In your original HTML file, you need to make some amendments. With grunt-inline we can inline our CSS using this method:

<link rel="stylesheet" href="css/twitter.css?__inline=true">

This will work for our Twitter CSS file, but because we are creating a tidy.css file using uncss, we need to be able to swap out the app.css file for the tidy.css file. Otherwise, grunt-inline will inline the app.css – but not the correct tidy.css. We can do this using the following method – note the position of the inline command:

<!-- build:css css/tidy.css?__inline=true -->
<link rel="stylesheet" href="css/app.css">
<!-- /build -->

Our processhtml task is then as follows:

  processhtml: {
    options: {

    },
    dist: {
      files: {
        'index-processed.html': ['index.html']
      }
    }
  },

We’re not using any options at the moment.

Our inline task is then as follows:

  inline: {
    dist: {
      options:{
        cssmin: true
      },
      src: 'index-processed.html',
      dest: 'index-inline.html'
    }
  },

In this instance, I affixed my files so I could easily distinguish them. For a larger project, you might have separate folders for these. You’ll also notice I have cssmin set to true, this is quite important to keep the CSS condensed in the head. After running processhtml, we have these two lines in our index-processed.html file:

<link rel="stylesheet" href="css/tidy.css?__inline=true">
<link rel="stylesheet" href="css/twitter.css?__inline=true">

As you can see, it’s swapped out the app.css file for tidy.css and included the inline command.

Third step – htmlmin

The last step in my process is to minify the HTML. The task for this is also quite simple:

  htmlmin: {                                  
    dist: {                                    
      options: {                                 
        removeComments: true,
        collapseWhitespace: true
      },
      files: {                                   
        'dist/index.html': 'index-inline.html'
      }
    }
  },

I like to put my minified file into my dist folder so I don’t have to give it a different name. As I’m just using FTP to upload the site, it’s the easiest way to do it.

Result

I haven’t tackled images, SVGs or scripts here. The use of these on this particular website is pretty minimal. That said, I did utilise an async method of loading the Typekit fonts. I did this by using the following code in the head.

    <script type="text/javascript">
      (function() {
        var config = {
          kitId: 'vwq0mva',
          scriptTimeout: 3000
        };
        var h = document.getElementsByTagName('html')[0];
        h.className += ' wf-loading';
        var t = setTimeout(function() {
          h.className = h.className.replace(/(\s|^)wf-loading(\s|$)/g, ' ');
          h.className += ' wf-inactive';
        }, config.scriptTimeout);
        var d = false;
        var tk = document.createElement('script');
        tk.src = '//use.typekit.net/' + config.kitId + '.js';
        tk.type = 'text/javascript';
        tk.async = 'true';
        tk.onload = tk.onreadystatechange = function() {
          var rs = this.readyState;
          if (d || rs && rs != 'complete' && rs != 'loaded') return;
          d = true;
          clearTimeout(t);
          try { Typekit.load(config); } catch (e) {}
        };
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(tk, s);
      })();
    </script>

I then use this in my CSS to prevent ugly FOUC:

.wf-loading  {

	h1,h2,h3,h4,h5,h6,p,ul,li,a {
		visibility: hidden;
	}

}

This seems to work pretty well and definitely has an impact on site speed. This was certainly quite a lot of effort to go to, and arguably on such a small page already the benefits could be seen as negligible. That said, once I was finished the site definitely felt rapid. The perceived performance was much better, and the actual loading times measure by pingdom are incredibly impressive:

http://tools.pingdom.com/fpt/#!/bMRVtG/http://418conf.co.uk/

Unfortunately, Typekit foiled my attempts to achieve a high mobile PageSpeed score. However, as Typekit is loading the CSS externally, I’m unsure how I could get around this without ditching Typekit altogether. The desktop score of 96 however is awesome, but the truth is I’m not going to get too hung up on a score. The page is lightning quick, fast to render, and lightweight – exactly how every website should be.

View the 418conf website

Warning, site may load quickly!

More resources:

Addy Osmani – Removing unused CSS: http://addyosmani.com/blog/removing-unused-css/

Grunt-inline: https://github.com/chyingp/grunt-inline

Grunt-uncss: https://github.com/addyosmani/grunt-uncss

Grunt-contrib-htmlmin: https://github.com/gruntjs/grunt-contrib-htmlmin

Grunt-processhtml: https://www.npmjs.com/package/grunt-processhtml

Looking for more tips?

Jump on our mailing list & get digital goodness straight to your inbox; including helpful tips, tricks and the latest articles surrounding marketing, web design and branding.

Sign up →