Fast-forward a couple of years and whatever blessed combination of versions of Ruby, Middleman and its dependencies I'd built the site with was lost to the mists of time. I'd hacked up the source too much to simply upgrade everything.
bundle exec middleman was dead and I was too lazy to fix it.
Luckily, I've been travelling around the world for the past 10 months, which means I've had the occasional slice of bus, train, airport or collectivo time to build this new blog!
Under the hood
I had a few requirements.
- Write content in Markdown
- Generate static HTML
- Generate an RSS feed, because RSS is awesome
- Flexible URL structure (I don't want no
/blogor messy dates in my URLs)
- An asset pipeline:
- Image optimisation
- Eternally cacheable unique static asset file names
- Ability to choose (and change) CSS & JS tooling and preprocessors
- No out-of-the-box templates to hack at. I wanted to build some simple, minimal HTML & CSS from scratch.
I tried out a few of the popular static site generators including Middleman (again), Gatsby, Hexo, Harp.js, Wintersmith and Metalsmith. Ultimately none of them were a simple
yarn install away from doing everything I wanted, and I didn't want to customise them so much that I wouldn't be able to update dependencies easily (i.e., exactly the problem I made for myself last time).
Here's what I ended up with:
- Metalsmith for HTML & RSS
- PostCSS for (surprise!) CSS
- Gulp for image optimisation & static asset revving (those sweet eternally cacheable file names)
- node-foreman + http-server for a lil' dev server
- git-directory-deploy for easy deploys to GitHub Pages
- Read all the files in a source directory.
- Invoke a series of plugins that manipulate the files.
- Write the results to a destination directory!
I couldn't find Metalsmith plugins to satisfy all of my requirements, but I was able to generate static HTML with the URL structure I wanted, from Markdown + Front Matter source files and simple EJS templates, exactly like I wanted, as well as an RSS feed.
I'm very OK with this being all that Metalsmith does. It does these things very well and the scope of its responsibilities is small enough that it all fits in my head. If it also handled CSS and assets and a dev server and deployments (which it probably can with enough plugins), it might feel like more of a black box.
As mentioned, you need a plugin to do literally anything with Metalsmith. I used...
- metalsmith-collections for grouping specified files together into an ordered collection (e.g. blog posts)
- metalsmith-feed for generating an RSS feed
- metalsmith-layouts for reusable layout templates
- metalsmith-markdown for converting Markdown files to HTML and grabbing data from Front Matter
Everybody should use React for everything all the time, right?
Well, no. I love component-based architecture and hot reloading and new ways of doing CSS - they genuinely make life easier when the project and team size warrant it.
This site has 2 pages (home & posts) and 1 maintainer (me). A Facebook-sized project this is not.
I prefer EJS (and other embedded language templates like ERB or even plain old PHP) over things like
Jade Pug, HAML, Handlebars or Liquid for the simple reason that it's the same plain HTML I'll be inspecting in Chrome plus the power of a real programming language I already know - no converting HAML to HTML in my head and no need to learn a template-specific
I decided to see how far I could get with a single plain CSS file to go along with my two HTML templates and quickly discovered that I missed a few things from my usual CSS workflow:
- Variables (which we all mostly use as constants)
- Sass-style nesting
- Sass' indent-based syntax (I know I'm in the minority here but you're all wrong and it's amazing)
I was already including PostCSS for Autoprefixer, so I had a look around for a few extra plugins and found:
- postcss-custom-properties for future-proof constants via CSS custom properties
- postcss-nested for nesting
- sugarss for indent-based syntax
- css-mqpacker to find rules inside identical media queries and pack them into a single media query
Gulp asset pipeline
I needed file watching, image optimisation, asset revving and multiple PostCSS steps. I'm yet to find a neater option for all of that than Gulp.
It's easy to fall into the trap of using Gulp (with a bunch obscure, unmaintained Gulp plugins) for everything just because it's useful for some things. I really wanted to avoid that "one enormous Gulpfile to rule them all" approach.
I have simple, standalone Gulp tasks for the following:
- Watching Markdown and EJS source files, kicking off the (fast, 100% Gulp plugin-free) Metalsmith build when they change
- CSS pipeline (PostCSS transformations then minimising with cssnano)
- Image optimisation with imagemin (on deploy only)
- Static asset revving with the excellent gulp-rev-all (on deploy only)
The dev server is really simple. I just generate the site into the same
public folder I deploy from (minus image optimisation and asset revving), watch some files and serve everything with http-server.
metalsmith: gulp metalsmith-watchstylesheets: gulp stylesheets-watchimages: gulp images-watchhttp-server: http-server -p 4000 -c-1
-c-1 flag for
http-server just disables caching.
Deploying and hosting
After generating, the site is deployed to GitHub Pages. There are a bunch of tools that will take your
public folder, switch to a
gh-pages branch, commit and push. This sweet, adequately-named bash script called git-directory-deploy does the job for me.
There's a downside to just deploying to GitHub Pages - the cache headers they set on static assets:
Static assets will only cached by browsers for 10 minutes.
It's a reasonable default for the average GitHub Pages website - if you push a new version of an asset to GitHub without renaming it, you probably want it to update soon afterwards on your site (or sooner with a hard refresh).
However I've got these neat unique revved asset file names that mean assets can be cached forever. I'll never "update" an image or CSS file; I'll always deploy a brand new file with a brand new file name and update any references in the HTML.
httpswith my custom domain
- Control over cache headers for static assets
- HTTP/2 for browsers that support it
- The ability to redirect
This setup means my website is fast. Fast websites are good.
With the exception of Google Analytics and Disqus embed snippets, there's no client side JS on this site (yet).
However after a year of travelling and using patchy internet connections, I'm convinced that these page transitions are a poor default choice for most sites.
I've got a larger blog post/rant on this very topic in the works. Now that I have a functioning blog again I have somewhere to post it, so watch this space!
Or be cool and subscribe to this fancy RSS feed.