The Web Masters will rise again

My site is breaking at the seams. Essential features of my generator are not automated yet. The source isn’t available yet, because it shames me.

With that disclaimer out of the way, I’d like to explain my goals, my choices so far, and my plans for the future.

Consider it a draft for a better writeup once my system is complete.

My initial requirements

  • An unopiniated and flexible static site generator that works close to the metal without “reasonable defaults”. The goal isn’t to abstract away the complexity, it’s to reduce boilerplate, hacks, polyfills, manual conversions, etc.
  • The input folder structure resembles the output. I should not be forced to segregate CSS, images, JS, layouts, and text to match the tool’s internal mapping, I should be able to put them wherever it makes sense to me, so I can build sub-sites easily.
  • Software I can continue to run as-is for twenty years, even if support is dropped tomorrow. Javascript libraries have a shelf life of like six months. I need to go with feature-complete tools that are either popular enough to stay supported, or simple enough I can maintain them myself.
  • Good performance. I prefer being able to do intelligent partial rebuilds than a very fast system that rebuilds things unnecessarily: it will scale better in the long run.
  • Multiple layouts. I want a collection of sub-sites with entirely different layouts. Or maybe different layouts for blog posts depending on my mood. I also want, as the site ages and evolves, for older pages to keep their older layout by default, as a memorial to the passage of time.
  • Live reload in browsers. Doing CTRL+S is much less friction than typing my-web-sight --rebuild -v -f -o ./output ALT+TAB & F5
  • Lightweight terse languages with minimal boilerplate or explicit structure, I prefer syntactically significant whitespace. My previous system used Coffeescript, Pug, Stylus, and Markdown.
  • Powerful markdown dialect. Markdown libraries have many optional features, some of which are useful, some others that cause issues.
  • Basic web-log features. Archives, categories, pagination and RSS are enough.
  • MIT or similar license. Easier to deal with the implications than GPL.
  • No insufferable developers I will be forced to interact with. Quick toxicity check on their homepages and Twitter, quick look at the user community hangouts, verify the absence of a “code of conduct” (while often meant naïvely, it is a red flag that sociopaths will thrive easier in a community.)
  • Bonus points: something with actual documentation. Hahahaha oh gods.

Some tools I looked at

There are like 58127 different static site generators more or less.

Some I considered:

  • Jekyll: The OG modern static generator. It just works. Big, complex, lots of features. But it sounded like having many layouts was a huge pain.
  • Hugo: The thing cool kids use now. Go language, which I don’t know. Tons of features I don’t care about, nasty looking layouts, literally every single site shown in the showcase is the exact same boring identical corporate app.
  • Cogear: A very strong contender for my use case.
  • 11ty: Another strong candidate.
  • Brunch: Seemed powerful but I didn’t really understand if it’d work out for me.

Enter Metalsmith

Metalsmith isn’t really a static site generator. It’s more of a standard. It’s a 400 lines script encoding the following idea: “How about we turn an input folder into a javascript data structure, perform transformations on the data structure sequentially, output that structure to another folder, and do so with this common API?”

The motto: Everything Is A Plugin. Even the most basic features, such as parsing templates and markdown files, are implemented with plugins.

Once I understood how it ticks, it became obvious that this was what I should use. More work, for sure, but there is no way a system this simple and elegant would force me to fight against sane defaults. Going with this system was a solution halfway between using a ready-made system and rolling my own.

Why Coffeescript?

Beauty matters.

With Coffeescript’s Ruby-like syntax, I can forget about the horrible underlying language it compiles to.

metalsmith::step_postcss = ->
  this.use postcss
    pattern: ['**/*.sss', '!**/_*/*', '!**/_*']
    parser: sugarss
    plugins:
      'postcss-import': {}
      'postcss-short': {}
      'tailwindcss': 'input/css/tailwind.config.coffee'
      'autoprefixer': {}
    map: false
  this.use move_remove
    remove: ['css/tailwind.config.coffee']
  this.use rename [[/\.sss$/, '.css']]
  this.use timer "⏳ Postcss"

It’s not fashionable. Everyone uses something else. Something that requires them to type a whole lot of {({[{((({{{()}}};)))}]})};(){()}();. I prefer to indent the code than type this junk.

I’m not a javascript expert, so a lot of stuff still trips me up, and coffeescript can make it harder to figure it out. But I don’t care.

Coffeescript is used for the server code, but will also be served (in compiled form) to the browser once I require client-side javascript.

Pug

I chose pug for templates because I like dogs. I do not like pugs as much as other dogs, however. I am not a fan of the wrinkled face.

a#microblog.card(href="https://twitter.com/AriaSalvatrice")
  h2 Microblog
  .details 
    span I have a Tweeter, the At thereof being AriaSalvatrice. I use it to make posts. You can follow it if you like posts. 
.picture#aria
  img(src="/homepage-2019-06/aria.jpg" alt="Portrait of Aria Salvatrice")
  .label ⬅ The author
a#soundcloud.card(href="https://soundcloud.com/ariasalvatrice")
  h2 Sound-Cloud
  .details 
    span Mine contains music, which I write much like this web sight: using computers.

I also chose it because it does things the way I like: terse, indent-based.

SugarSS

SugarSS is a PostCSS syntax for indent-based CSS. Removing the useless braces and semicolons is kind of a common theme, here.

h1#title-effect // The solid color and text shadow
  font-family: 'Abril Fatface'
  font-size: 2rem
  color: #fcaebb
  text-shadow: -6px 3px #0e6977, -1px -1px 0 #0e6977, 1px -1px 0 #0e6977, -1px 1px 0 #0e6977, 1px 1px 0 #0e6977
  position: absolute
  z-index: 200
@screen lg
  h1#title-effect
    padding: 0
    transform: rotate(-11deg)
    transform-origin: top left
    font-size: 4rem

I must say I still like Stylus better: Stylus allows you to skip the colons in declarations for maximum terseness, and SugarSS throws errors willy-nilly for things like accidentally using a semi-colon, when I can’t see why it couldn’t just ignore them and throw a warning.

However, SugarSS offers integration with the PostCSS ecosystem, which the CSS framework I use, TailwindCSS, relies on, while Stylus has some integration issues.

TailwindCSS

After the initial surprise of encountering a framework that is, in effect, glorified inline styles, I realized just how powerful this approach is.

Because TailwindCSS is based entirely around utility classes, it has no preconceptions like other frameworks: it doesn’t assume you’re trying to do a standard trendy best practices black text on white boring-ass garbage “App”. I didn’t have to fight the framework to do the crazy effects on my homepage.

body(lang="en")
  #bg-text(aria-hidden="true")
  header.flex.justify-end
    .container.relative.overflow-hidden.pb-2.pt-2.lg_pt-24
      h1#title-effect
        .block lol hi welcome to my
        .inline-block page love whats hip 
        #emoji
          include ./_emoji.html

While the mess of classes in the HTML looks nasty, they’re just for prototyping and one-off elements: when used within PostCSS, it’s easy to copy-paste those classes from the HTML and move them to your own selectors with the @apply keyword:

.card
  @apply w-full block flex flex-col mx-3 my-2
  height: 20rem
  transform: rotate(-5deg) skew(-10deg, 0deg)
  opacity: 0.8
  transition: transform 0.6s, opacity 0.6s
  box-shadow: -6px 3px #0e6977, -2px -2px 0 #0e6977, 2px -2px 0 #0e6977, -2px 2px 0 #0e6977, 2px 2px 0 #0e6977
  z-index: 100

Markdown, of course

You’ve already used it. You know what it is.

---
title: The Web Masters will rise again
layout: post-2019-06.pug
description: A quick look at the static site generator tech I'm using so far
---


My site is breaking at the seams. Essential features of my generator are not
automated yet. The source isn't available yet, because it shames me.

[...]

## Enter Metalsmith

[Metalsmith][] isn't really a static site generator. It's more of a standard.
It's a [400 lines script](https://github.com/segmentio/metalsmith/blob/master/lib/index.js)
encoding the following idea: "How about we turn an input folder into a
javascript data structure, perform transformations on the data structure
sequentially, output that structure to another folder, and do so with this
common API?"

The motto: _Everything Is A Plugin_. Even the most basic features, such as
parsing templates and markdown files, are implemented with plugins.

[...]

[Metalsmith]: https://metalsmith.io/

But I’d like to point out that Markdown is a language that fulfills one of my requirements: terse, and indent-based.

At the start of markdown files, metadata can be embedded in the front matter.

Distribution?

Once the system is complete and proven to work well, I’m considering distributing and supporting it.

It is obvious the world doesn’t need yet another static site generator. There’s already way too many of them. Most people will be served well enough by Jekyll. And my code merely duct tapes libraries together: anyone can do it. It is more of a modpack than an original system. It took a lot more time to decide what to use than to make it work together. Which is why documenting my choices so far is more valuable than sharing the undocumented code.

So if I were to distribute it, it would only make sense to do so if I can provide excellent beginner-friendly documentation, with videos that walk you through everything.
The code could also be written in literate coffeescript, to make it as easy to understand as possible.
The target user is the artsy hobbyist who wants flexibility and control, rather than something acceptable enough. Someone who already knows a bit of web tech, is eager to learn more, but wants to get started right now, without getting locked into a specific set of technologies, and likes having a safe starting point.

The blind leading the blind? Sure. But everything else sucks.

My TODO-list, in no particular order

I work through my TODO-list as I encounter requirements for my own site, instead of adding things “just in case”.

  • Improve the CSS for posts as I use new elements and run into issues.
  • Clean up the CSS to use more variables and calculate the colors mathematically.
  • Make some CSS functions/mixins/macros/helpers/partials, whatever the hell it’s called in PostCSS.
  • Rig up self-hosted minimally creepy access stats.
  • Decide on a sync method and implement it. (Git push? SFTP? rsync?)
  • Get http redirections working and decide how. Should work with multiple servers (express, nginx, apache…)
  • Get partial rebuilds to work! Right now every file change rebuilds everything.
  • Get blog functions working: RSS, categories, pagination, archives…
  • Self-hosted comments?
  • Generate fixtures to stress test the system.
  • Figure out why the performance degrades when the server runs for too long.
  • Serve client-side JS from NPM
  • Automate image compression
  • Make a debug partial to show tailwind size breakpoints

We’ll see what happens in the future. For now, I’ll focus on making it the perfect system for me.

—Aria Salvatrice2019-06-23