Tom MacWright

tom@macwright.com

This page weighs 15kb

The slow expensive internet is real and is here to stay. Between planes, other countries, and Comcast, there are places and times when the internet is limited and every byte counts.

And fast pages are in - pages that load in an instant and care about your time more than they care about gizmos and gadgets.

There are tons of resources for this topic - a good place to start is Google PageSpeed. These are just the things that helped the most in my case, as well as some insight into how I think about performance. First I’ll talk specifics, and then themes.

  • First I stopped wasting downloaded webfonts by fixing their CSS
  • Then I optimized the webfonts as WOFF2
  • Then I removed webfonts entirely
  • Whenever possible, I use inline SVGs for images
  • I removed one-off styles from the main stylesheet and put them in individual posts
  • Commonly-included images use the <picture> tag and have WebP versions

Webfonts

I used webfonts on macwright.org for years. I wanted to make typography part of ‘the look’, and fonts are fun. But for performance, I made two big mistakes:

A lot of webfonts include bad CSS.

I was using Charter, a beautiful typeface generously preserved by Matthew Butterick. Trying to be a good typography citizen, I included all four font variations - Regular, Italic, Bold, Bold Italic. And I used the included CSS:

@font-face {
    font-family: 'charterbold_italic';
    src: url('charter_bold_italic-webfont.eot') format('embedded-opentype'),
         url('charter_bold_italic-webfont.woff') format('woff');
    font-weight: normal; font-style: normal;
}
@font-face {
    font-family: 'charterbold';
    src: url('charter_bold-webfont.eot') format('embedded-opentype'),
         url('charter_bold-webfont.woff') format('woff');
    font-weight: normal; font-style: normal;
}
@font-face {
    font-family: 'charteritalic';
    src: url('charter_italic-webfont.eot') format('embedded-opentype'),
         url('charter_italic-webfont.woff') format('woff');
    font-weight: normal; font-style: normal;
}
@font-face {
    font-family: 'charterregular';
    src: url('charter_regular-webfont.eot') format('embedded-opentype'),
         url('charter_regular-webfont.woff') format('woff');
    font-weight: normal; font-style: normal;
}

Little did I know, this means that I wasted three font loads every time someone visited macwright.org. Why? This CSS is written extremely literally - either for old-school compatibility or because of a simplistic generator, it doesn’t defined a typeface Charter and then ensure that the bold, italic, and bold italic styles use proper fonts. Instead, it defines four separate fonts - charterregular, charterbold, charteritalic, and charterbold_italic - and unless you name them explicitly, they aren’t going to show up.

The corrected code is this - four fonts in the same family that apply to different styles and weights:

@font-face {
    font-family: 'Charter';
    src: url('charter_bold_italic-webfont.eot') format('embedded-opentype'),
         url('charter_bold_italic-webfont.woff') format('woff');
    font-weight: bold; font-style: italic;
}
@font-face {
    font-family: 'Charter';
    src: url('charter_bold-webfont.eot') format('embedded-opentype'),
         url('charter_bold-webfont.woff') format('woff');
    font-weight: bold; font-style: normal;
}
@font-face {
    font-family: 'Charter';
    src: url('charter_italic-webfont.eot') format('embedded-opentype'),
         url('charter_italic-webfont.woff') format('woff');
    font-weight: normal; font-style: italic;
}
@font-face {
    font-family: 'Charter';
    src: url('charter_regular-webfont.eot') format('embedded-opentype'),
         url('charter_regular-webfont.woff') format('woff');
    font-weight: normal; font-style: normal;
}

Once I got over the “wasting bytes” hump, the next step was optimizing the webfonts I did use. Charter came in EOT, OpenType, and WOFF versions, but not WOFF2. You can use FontSquirrel’s converter to generate WOFF2 files from sources, or, as I did, compile and use Google’s converter. In all, it provides something close to the advertised 30% savings.

At the end of this struggle, I read mrmrs’s piece on webfonts and decided to remove them. This type is in Georgia, headings are in Helvetica, monospace is in Monaco.

Be stingy about third-party services

On most pages on this site, the only other domain that’s accessed is gaug.es, which I use for statistics.

I used to have a little tweet button that hit twitter.com, a hacker news sharing button that hit some heroku site, fonts that came from TypeKit. All lovely services, but should they be in the critical path between a visitor and your writing? Especially the ‘sharing buttons’, which are unnecessary - the ‘Tweet’ link at the bottom of this post is just a normal link - are often slow to load, and can be a privacy concern.

Plus, the fewer third-party dependencies you have, the better stuff works on planes. I can run a local installation of Jekyll and this site works pretty well - anywhere.

Optimized images are cool, inlined SVGs are better

That nice, crisp graphic at the top of this post on Sorting is an inline SVG. I created it in Sketch, exported it to SVG, opened that SVG in a text editor, and just copied & pasted the sucker in.

That chart at the top of this post about performance in Big is an inline SVG. I created the numbers, wrote a chart with D3, ran the chart on my computer, copied the SVG content from the page, and pasted the sucker in.

Inline SVGs are so cool: they’re images with zero external dependencies, resizable, crisp - just all-around lovely. SVG support is pretty darn good cross-browser, too.

Inline your stylesheets and JavaScripts

After years of writing a blog, your central stylesheet accumulates junk - stuff you use in one post but you don’t end up using the same style ever again. To pay off this technical debt, I checked each rule in my stylesheet to see if it was used in more than 3 places. If it was a one-off, I just inlined the stylesheet. The HTML validator may complain, but <style> elements in HTML still work and are incredibly handy.

The one image that I want on each page is small and super optimized

Each page on this site has a picture of me. Silly - it could be removed, but I like it. So, it’s expressed in HTML like:

<picture style='flex:none; align-self: center;'>
  <source srcset='/css/self.webp' type='image/webp' />
  <img
    src='/css/self-70.jpg'
    alt='Tom MacWright'
    style='width:80px; height:80px; margin:15px 0;' />
</picture>

This way, browsers that support webp get the incredibly optimized WebP version of the image, and the rest get the as-well-as-it-can-be optimized JPEG version.

Anyway, on to the general bits:

  1. Don’t guess
  2. Some bytes block the page, some don’t
  3. The best optimization is deletion

Don’t guess

For most engineers, the problem isn’t premature optimization, it’s optimization without measuring anything. Read the Mature Optimization Handbook. Performance bottlenecks are surprising, and tools for measuring performance are real and they are excellent: use them. It’s a common theme in coding and everything that the way to move forward is to stop guessing, and start getting the facts.

For website performance, my hammer is webpagetest.org. It is unbelievably good and comprehensive for a completely free tool. And, of course, Chrome’s browser debugging tools. They are the second most important part of my computer, after vim.

Some bytes block the page, some don’t

It’s tempting to look at JavaScript, Webfonts, CSS, images, and so on, and think of them in terms of bytes. But there are really two values that you need to care about: the stuff you need to initially render the page, and the stuff that’s added in after that.

And in this way images are great - the page is displayed, people can read the text, and then the images load. But webfont loading produces a partially-blank page. JavaScript loading can produce an entirely blank page. This isn’t good: that stuff in your <head> tag, your stylesheet - that stuff is important and needs to be as small as possible.

The best optimization is deletion

You cannot get a simple system by adding simplicity to a complex system. - Richard O’Keefe

Nothing is faster than the lack of something. Nothing reduces more bugs than deleting code. There are always two ways - you can add complexity like script loaders or webfont loading tricks or image lazy loading - adding potential bugs, adding permanent complexity - or you can swear off the problem area for good. Sometimes you have to take the complexity hit, but it’s a dream to take the simple road.

Further reading