I have been saying for years that I’m going to dive deeper into web performance, but each time I tried, I found it too complicated and gave up / lost interest. I think the problem was that I would read some articles and then not know how to apply it or measure any changes. This time, I decided to start with my own website, even though it’s a very simple static website.
- HTML: This site is built using Middleman, a Ruby-based static site generator, and it works great.
- CSS: The site loads normalize.css and tachyons.css, both of which I just
copy-pasted into my source code, rather than loading from CDN or NPM module/RubyGem or something.
It also loads a syntax highlighting stylesheet using the
middleman-syntaxgem and a embedded ruby stylesheet.
- JS: There is a tiny bit of JS that sets the Copyright year in the footer, and a Google Analytics script.
- Fonts: The site downloads two fonts from Google Fonts and also the Font Awesome font.
- Images: Images in subpages / blog posts are deployed as part of the site’s bundle and loaded from the same domain, but there is one image from my Gravatar profile also.
Here are a set of things I played around with:
Replace Font Awesome with custom SVG
I used to default to installing Font Awesome for icons, so it’s on this site too, but I noticed that I was only using two icons here: the Twitter logo and an “envelope” icon for an email link. I downloaded the SVG icon from Twitter Brand Resources and used Sketch to create a simple envelope icon. Both of these icons looks a little worse, but I don’t really care and it would be easy to improve them in a future iteration. The SVG from twitter brand resources also adds a background to the icon, which I could probably edit out from the SVG, but I didn’t care too much about that either.
Resource Hints to Google Fonts and Gravatar
preconnectResource hints for Google Fonts and Gravatar. I’m not yet sure if this actually had an impact, but it’s supposed to tell the browser to establish a connection with 3rd party hosts earlier so that so when requests are actually requested, DNS resolution and SSL handshakes are already done. I think in my case, both these requests are made pretty early, so it didn’t make a big difference.
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin> <link rel="preconnect" href="https://gravatar.com" crossorigin>
I learned about what these mean from this post: https://www.keycdn.com/blog/resource-hints.
Precompile vendor styles (normalize, tachyons, and syntax highlighting into one stylesheet
A confusing thing about
@importin SCSS is that
@import 'foo.css';acts like a CSS import statement, and is requested at runtime, whereas
@import foo;acts like a SASS import, meaning that the style is imported and inlined at build time.
I previously had this setup.
<!-- HTML layout file --> <%= stylesheet_link_tag "all", "syntax" %>
// all.css.scss @import 'vars'; @import 'vendor/normalize.css'; @import 'vendor/tachyons.css'; @import 'flex'; @import 'post';
// syntax.css.erb <%= Rouge::Themes::Github.render(:scope => '.highlight') %>
This setup resulted in four CSS requests. One for the compiled
all.css, which would then load
tachyons.css, and then another one for the compiled
Thinking about it a little bit, I reduced these two requests:
<%= stylesheet_link_tag "vendor", "app" %>
// vendor.css.scss @import 'vendor/normalize'; @import 'vendor/tachyons'; <%= Rouge::Themes::Github.render(:scope => '.highlight') %>
// app.css.scss @import 'vars'; @import 'flex'; @import 'post';
I could have reduced farther to a single stylesheet, but I read somewhere that because vendor dependencies don’t change that much, and because static assets in production are hashed filenames for caching / cache busting, it’s good to keep vendor assets hashed independently so users can take advantage of thier local caches.
Google Fonts stylesheets
Another mistake I was making was to use
@importto load Google Fonts:
@import url('https://fonts.googleapis.com/css?family=Indie+Flower'); @import url('https://fonts.googleapis.com/css?family=Libre+Baskerville');
I changed this to load both fonts in a single request and using
link, rather than
@importwhich blocks parallel downloads.
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Indie+Flower|Libre+Baskerville">
Download smaller profile image
Another trivial optimization was to replace the 300px Gravatar image to a more appropriate 80px size. I considered using
srcsetto optimize this for different screen reoslutions, but simply decided the extra 2kb saving wasn’t worth it.
I previously had an
all.jsfile that was using the
middleman-sprocketsgem to bundle all JS files in
all.jsand put both these scripts in the
<head>section of my HTML layout file.
In doing this, I also updated the Google Analytics snippet, which seems to have added another request to some Google Site Manager. I am not sure what that is, but it seems like a generally good idea to use the latest snippet recommended by the service that provides the snippet. I’ll have to look into what the difference is at some point
Here’s are the WebPageTest waterfalls from before and after on Mobile - Slow 3G using the “Simple Testing” option on https://www.webpagetest.org/easy.php.
I am not great at reading these charts yet, but I skimmed through this post by Matt Hobbs to get my bearings, and saw a couple things that confirmed that the steps I took above actually did something:
- The thin yellow line marking DOM Interactive went from ~5.1 seconds to ~4.4 seconds.
- The blue line marking “Document Complete” went from ~9.5 seconds to ~8.5 seconds
- Number of requests went down from 15 to 11.
One other more nuanced things that I noticed is that CSS downloading from Google Fonts now starts at the same time as all the other CSS.
Looking at that chart, it looks like the next main thing holding up DOM Interactive is the Google Fonts
CSS and a large part of that is DNS, Connection, and SSL. I wonder if the
preconnect resource hint
might be making this more complicated than it should be? Or maybe I should inline the
directives that are in this stylesheet. Any breaking changes from Google would break the site, but
that would probably not be the end of the world, since fallback fonts are a thing.