RWD Bloat Part II

A step-by-step walkthrough of making my site faster.

July 30, 2014 •

Building off of my previous post on RWD Bloat, the following is step-by-step how I made my site faster over the course of a few days. For the purposes of this test, I’m hyper-focused on my Speed Index numbers for my Home and Article templates (the 2 most visited templates of my site). Here is my baseline:

  Home Article
PageSpeed (Mobile) 79 78
PageSpeed (Desktop) 93 91
Speed Index (3G Fast) 1446 1749

Step 0. Normalize instead of CSS Reset

I made a quick unintended decision to use Node-Sass and Autoprefixer instead of Compass. I also did a quick refactor to “Normalize” instead of using CSS Reset. Curious to know the difference? Me too.

  Reset Normalize
CSS Selectors 679 578
Home Speed Index 1446 1376
Article Speed Index 1749 1412

Bullshit! I lost 15% of my selectors by ditching CSS Reset and my Start Render time has dropped anywhere from 100ms-300ms.

Step 1. Unblock webfonts

I added Filament Group’s loadCSS to lazyload my webfonts instead of blocking them in the <head>.1

  Webfonts in <head> Webfonts w/ loadCSS()
Home Speed Index 1376 1327
Article Speed Index 1412 1284

Faster! The downside there’s a pretty heavy FOUT on page loads. Browser gets the HTML, which requests the JS, which injects the CSS, which requests the WOFF… there is a webfont from cookie/localStorage trick, but that seems troublesome.

Step 2. Leaner JavaScript

Ugh. I really wanted this because “jQuery: Bad for Web Performance” would have been awesome HackerNews linkbait. Turns out, I’m more entrenched in jQuery-land than anticipated…

Step 3. SVG Spriting

I had 176 icons in my icon font, but I only use 6 icons sitewide: Home, Archive, About, RSS, Previous, and Next.2 Eventually I want to try putting logos into the same external sprite, reducing the document size, but am focusing an icon system replacement. IcoMoon allows you to download SVG Sprites, to use them, I just reference them like this:

<svg title="Home">
  <use xlink:href="/images/spritemap.svg#icon-house"></use>
</svg>

This got me up and running quickly. I ran into a problem around viewBox and <g> elements, so I handedited the <defs> block a bit, switching <g> elements to <symbol id="icon-XXXXXX" viewBox="0 0 32 32">3. I also added SVG4Everybody for IE9-11 and (r)Android phone support. The change?

  Icon font SVG Sprite
Home Speed Index 1327 1264
Article Speed Index 1284 1222

I’ll need to automate this in the future but that feels pretty good, man.

Step 4. CSS Cleanup

My CSS was in an awful state; incredible redundancy and quite possibly the worst filenaming scheme I’ve ever seen. It was my first Sass project. I cleaned up and made better CSS modules, variables to reduce random colors, and added a min-height to my Fusion Ad to prevent the layout from seizing.

After this and without the icon font, I’m down to ~172 selectors in CSS Stats. Let’s see how far good ol’ fashioned house keeping gets us.

  Old CSS Refactored CSS
Home Speed Index 1264 938
Article Speed Index 1222 1126

Three hundred points! Third-party content was going apeshit (also 28s to fully load a favicon?!?), but this validates to me that cleaning house can be one of the best things you can do for a project. Also saw the Speed Index dip below 1000. I’m nearly at my goal…

Step 5. Critical CSS

With CSS cleaned up, I can begin extracting my Critical CSS and putting that inline in the <head>. Borrowing from Addy Osmani’s Critical, I came up with a quick Gulp task that would output the CSS into Jekyll’s _include/ folder for me:

var penthouse = require('penthouse');
var Promise = require("bluebird");
var penthouseAsync = Promise.promisify(penthouse);

gulp.task('critical', function(){
  penthouseAsync({
    url : 'http://daverupert.com/',
    css : './stylesheets/style.css',
    height: 480
  }).then( function (criticalCSS){
    require('fs').writeFile('_includes/critical.css', criticalCSS );
  });
});

Now critical.css is inlined and my stylesheet is being lazyloaded by loadCSS(). I should probably do that uniquely for every template. I noticed I was using loadCSS() for two separate CSS files, so I decided to include the contents of fonts.googleapis.com in my _typography.scss. Those @font-face rules get picked up by my critical CSS task so I need to hand-edit a bit.

  All CSS Critical CSS
Home Speed Index 938 735
Article Speed Index 1126 1123

There’s some serious blue-link FOUC happening, but the score is very good.4 Although I’m still not sub-1000 on the article page. Looks like third-party things are gumming me up.

Step 6. Final Boss Battle

In an attempt to thwart third-party scripts and win on that Article template, I tried a few things to squeeze out more performance.

  1. Reorder scripts to make Disqus last. No Improvement.
  2. Due to the exhaustive number of “Your webfont looks terrible on Windows7 Chrome” complaints I got this weekend, let’s try only webfonts for headings! Eases body text FOUC.
  3. Moved logos to the SVG sprite. Learned SVG Sprites don’t support linear gradients. Now I have a new Flat® logo. Massive Improvement, but…!!

At this point I ran my site through WebPageTest and PageSpeed and noticed my Homepage Speed Index was now over 1000, but my PageSpeed score had dipped from 100 to 74. GASP! Either PageSpeed Insights changed their algorithm, or now that my document is faster, it’s revealing a whole new set of problems. Back to the drawing board.

  1. Use Google’s inline rAF() snippet instead of loadCSS(). Minimal improvement.
  2. Manually added rules to critical.css. Fixes FOUC. PageSpeed improvement.
  3. Lowered Fusion Ad on homepage without breaking my TOS. PageSpeed improvement.
  4. Inline SVG’d above the fold vectors at top of document5. BINGO BANGO! Winning everything.

It appears the secret was putting a bit more into my first response.

Final Results

  Home:before Home:after Article:before Article:after
PageSpeed (Mobile) 79 98 78 96
PageSpeed (Desktop) 93 98 91 97
Speed Index (3G Fast) 1446 728 1749 1065

I’ve nearly doubled the speed of my site. Not too shabby for a responsive design with jQuery, two webfonts, third-party ad, third-party comments, all while being tested on a 3G Connection. My Speed Index on Cable is currently 400~414 for both templates.

Hopefully that helps underline the point, as Scott Jehl so eloquently puts it , “How we load assets matters just as much as how many assets we’re loading.”

If any extreme perfinistas (or Googlers) have any quick tips on how to get that Article template to break the glass on 3G, I’d love to know them.

  1. Note: Switching from CodeKit to Gulp for script concatenation, I switched from Minify to Uglify. Both files (the control & the experiment with loadCSS) ended up being ~101kb unzipped exactly.

  2. TODO: I could/should have made a 6 icon font as a control. Someone (not me) should test that.

  3. Warning: Ran spritemap.svg through SVGO thinking that would be awesome… well… it wasn’t. SVGO renamed all the IDs (#icon-house#b) so it got non-semantic and hard to use. I’m sure there’s a setting, but hours lost there.

  4. Warning: Lazy-loading CSS breaks art directed posts. Will probably have to style off of post-ID.

  5. Note: Inline SVG had to be at top of document to render and pass WPT and PSI.