Square one: minified CSS

Editing minified source code sucks.

I should probably set up a build pipeline to just automatically compile my hand-written barf CSS down to properly minified CSS, but for now I’m using what came inside the pure-hugo1 theme, which started off as minified CSS.

Diffs are terrible when you have 1 line 4,000 characters across. I recommend using

git diff --color-words     # for default whitespace-separated tokens
git diff --color-words=.   # diff every single freaking character :)

But it’s still a pain scrolling across.

Square II: Enter the Prettifier

I thought about the build pipeline step, but what I came across first2 was a Sublime Text plug-in. That’s fine, since I’m pretty much always editing from within Sublime. It’s pretty easy to have it format-on-save3.

So how much space am I spending/wasting if I switch to a more readable, less-minified style?

Enter the bash

I needed something quick, so I wrote it in bash.

The idea is I don’t want to clutter up my git working directory, especially since I might clobber the whole thing when I inevitably rm my test crap later, losing who knows what hadn’t been staged yet, et cetera. It’s like littering.

I want to do my gzipping4 in a temporary working directory. So here we go

$ test-compress() { local dir=$(mktemp -d); cp "$@" $dir/; pushd $dir; ls -l; gzip --keep --verbose --best ./*; ls -l; popd ;}

And we’re in business:

  $> test-compress themes/purehugo/layouts/partials/style.css.html 
/var/folders/rv/jkzyqfws2y54mrr1l3jb952r0000gn/T/tmp.h0uFIPfO ~/Dropbox/yyyy-mm-dd/2019-08-22/blog/quick
total 8
-rw-r--r--@ 1 dude  staff  3793 Sep 18 17:38 style.css.html
./style.css.html:    63.7% -- replaced with ./style.css.html.gz
total 16
-rw-r--r--@ 1 dude  staff  3793 Sep 18 17:38 style.css.html
-rw-r--r--@ 1 dude  staff  1377 Sep 18 17:38 style.css.html.gz

Let the games begin

Mutian’s CSS Format can format code like 7 different ways, at many points along the spectrum.

I probably should automate the test, but I feel like if it takes less than 2 minutes, I should probably just do it by hand first:

Style Size (full) Size (gzip –best)
Expanded 4,114 1,398
Expanded (Break Selectors) 4,114 1,396
Compact 3,793 1,378
Compact (No Spaces) 3,382 1,333
Compact (Break Selectors) 3,793 1,377
Compact (Break Selectors, No Spaces) 3,424 1,335
Compressed 3,310 1,314

Now let’s go back and add brotli, since Can I Use seems to indicate it’s pretty much available in most browsers.

$ test-compress() { ... gzip ..; brotli --keep --verbose -9 ./!(*.gz); ... }

Make sure you run bash with extglob turned on (run shopt -s extglob if you’re not sure, and read about it in man bash, and, while you’re at it, turn on globstar, too). You just need shopt -s extglob in your ~/.bash_profile, assuming you run a login shell (why wouldn’t you?). You’ll need extglob for !(*.gz), that is, any file that wasn’t just put there by gzip. I’m not going to be test-compressing any .gz files.

A second run, with Brotli

Style Size (full) Size (gzip –best) Size (brotli -9)
Expanded 4,112 1,397 1,242
Expanded (Break Selectors) 4,112 1,394 1,241
Compact 3,791 1,377 1,221
Compact (No Spaces) 3,380 1,331 1,168
Compact (Break Selectors) 3,791 1,376 1,224
Compact (Break Selectors, No Spaces) 3,422 1,333 1,179
Compressed 3,310 1,314 1,149

You might say hey, some numbers changed and you’re right: I worked backwards the second time, from most-compressed back up to most-expanded. It seems the output depends on previous runs; it is not idempotent. I wouldn’t have noticed that if I had automated the test.

Today’s choice

I like Compact (Break Selectors). It looks like

#layout,
.nav-list { padding: 0 }
.brand-title,
.content-subhead { text-transform: uppercase }
.footer,
.header { text-align: center }

I’m not even sure if my blog host (currently just static hosting on FastMail) even serves resources with gzip compression or Brotli compression.

Testing determines:

Content-Encoding: gzip
Server: nginx

I remember reading (years? ago) that nginx could actually pre-compress static files that it sees. It won’t compress on-the-fly (I mean, it could, but that’s the ordinary behavior) but it’ll check for a foo.html.gz file first before compressing foo.html. This Blobfolio blog post is a good learning resource that sums it up really well.

It sounds like back when Debian Stretch shipped (2017?), it was too early for static Brotli compression in the built-in nginx. It’s plausible FastMail just hasn’t upgraded their entire fleet of production servers. I don’t blame them! Static hosting is surely a small sliver of their business and the diminished return of brotli-over-gzip versus gzip-over-nothing means it’s hardly worth it, even if it would cost nothing.

I’ve heard lots of great JMAP-related work coming out from them, so if it’s just a matter of upgrading nginx and updating configurations / deployments (the gzip part is already behind the scenes to me), it’s probably a walk in the park for them.

In the end, I learned that my control over the bytes received is limited, especially relative to the control nginx has. Gzip compression saves thousands of bytes, literally 60%+ reduction in bytes received. In comparison, I can only reduce by around 25%, and Gzip steals most of my thunder, letting me have only 6% impact (this is ok). Brotli is a true progress__ion, though: even __fully-expanded CSS through Brotli is smaller than minified CSS through gzip.

That’s right:

fully-expanded CSS through Brotli is smaller than minified CSS through gzip

(results may vary)

The returns may be diminishing but they are so, so real. Minification might be going out of style (woo hoo!).

  1. https://themes.gohugo.io/purehugo/ 

  2. Literally third result, and if you look under the hood, it’s just 274 lines of re.search()s and re.sub(..)

  3. Just copy the Preferences > Package Settings > CSS Format > Settings - Default to Settings - User, and adjust 

  4. and bzipping, and lzmaing, lzma2ing, brotli-ing, whatever the browsers support these days