I’ve wanted to make a round of optimzations around this site for a while, trying to speed up the loading time of the pages.
This website is built with Hugo, so it’s pretty easy to adjust or create a new template for it, while maintaining all existing content.
I was originally using the awesome Hugo-Geo theme, but I was a bit unhappy with the fact that imports the whole bootstrap framework, so I wanted to slim it down a bit.
Originally I was serving around 280KB of files for the index, about 120KB of which was from minified css and js files.
First attacking the lowest hanging fruit: I added html minification and gzipping of all files on my build pipeline by using Minifi + gzip. I wrote a small shell script to perform a full build + rsync of data from my desktop to my hosting provider. This cut the bandwidth of the main files on my site by about half.
Next, I tried cutting down the amount of bootstrap modules (Hugo-Geo uses less), but I wasn’t able to really cut down on the fat. So I decided to rewrite the css from scratch using Mini.CSS as a base framwork.
That one worked wonderfully - I was able to reduce all my css to about 7KB, split among three files:
- site.css is the main style (embedded directly on the html - more on that below)
- extrastyles.css adds extra styles visible only below the fold
- fonts.css loads the nice-looking web fonts
Improving display response
After improving on total download size and number of requests, I wanted to see what I could do to improve display time for both new and repeated visitors.
I was already following the standard advice of putting css and js files at the end of the html block, however that presented two problems: on some pages with gist snippets, the display of the css would sometimes get delayed and I’d get an extremely noticeable Flash Of Untyped Text (FOUT) on the page and (when download of the webfont hung) a Flash of Invisible Text (FOIT).
I was annoyed by it and I also wanted a blazing fast display of the general page layout without having to wait for the download of the extra css at the end of the page.
After the redesign of the css, I started embedding the whole site.css file directly within the
<head> section of all pages (by using Hugo’s
readFile directive). This allows the browser to immediately display the correct general layout of the page and added only about 1KB (after gzip) to every page.
To improve font loading, I used the techniques described on this article about the FontFaceObserver js library. But that was not sufficient to prevent a FOUT on repeated visits.
Experimenting a bit with loading order of the CSS files and trying out dynamic css loading solutions was not enough. Pretty much anything except loading a css file at the
<head> section causes delay.
The final solution I came up with uses on old trick for creating dynamic pages: using document.write on an inline html script tag.
Before the closing of the
<head> section of every page, I check whether this is likely a repeat visit (using a web storage item). If it is, I write the css link tags directly at the head, counting on the browser’s cache to load it before continuing with the rest of the page rendering. This prevents all FOUT problems.
I’ve summed up the whole thing in the following gist.