Adventures in Tera

As promised, the Stargate SG-Fun podcast website is up and running.

In related news: I don’t always make websites, but when I do, I prefer static site generators: systems that, rather than constructing website pages right when you visit them using databases and complicated server software, instead run their potentially complicated creation logic ahead of time. That way, the site, after each behind-the-scenes update, always exhibits the same, inert, safe pages to the world.

When my Edge Cases cohost Wolf Rentzsch set up our Edge Cases podcast website (now back from the dead!), he used Jekyll.

When I, Michael Helmbrecht, Mike Critz, and Samuel Giddins set up the updated NSCoderNight website, we used Middleman. In that case, the system bitrotted so thoroughly even within four years that I had to abandon it completely!

So when I went looking around for an SSG to use for SG-Fun, I knew I wouldn’t be using that. And when I did pick one, I picked Zola, an SSG that prided itself on having “no dependencies”. This is, of course, lie — it requires a ton of other components in order to be installed correctly. But I guess, in theory, because it’s a compiled Rust binary, it’s in less danger of being at the mercy of updated or abandoned thirty-party libraries over time, like Middleman was.


There are two ways to install Zola: brew and MacPorts.

Attempting to install via brew hung on my computer, and all my attempts to update brew — even to try to uninstall and reinstall brew — were unsuccessful. A great start! (Edit: this Mastodon post might help, but I haven’t tried it.)

MacPorts worked better, probably because I could install/update MacPorts by downloading a macOS package file and running that.

Once I had MacPorts working, I could use that to install Zola as described in the Zola docs.

Zola Basics

Zola uses the Tera template engine, created by Netlify, a well-funded startup aimed at enterprise websites. This allows Zola, a one-person open source project, to have a much more capable templating engine that it would be able to create for itself otherwise. Many SSGs adopt external systems for precisely this reason. Tera has loops, conditionals, string manipulation, math operators, macros, all the logic I needed for my relatively simple website.

The downside of such an approach is that, now, you have to look in two sets of documentation to figure out how to do things, instead of just one.

And even then, it was often difficult! I recommend doing what I did, which is download a couple of the theme example sites that are listed in Zola, to see how they put things together.

Zola, like most simple SSGs, has a concept of “pages” (think: blog posts) driven by Markdown data files. These are then organized in “sections”. So you could, say, have a main section for your blog posts (or podcast episodes), and a separate section for an About page.

They also have pagination functionality. If you have two hundred blog posts, it can, with only a little bit of effort (check out those theme sites!), split up your posts into individual pages with the number of posts you specify, such as ten or twenty.

Single Source of Truth

I don’t like retyping the same information into more than one place, because that allows those multiple sources of truth to diverge. Programmers call it D.R.Y.: don’t repeat yourself.

Zola helps with this, but it isn’t perfect.

  • Zola can extract the date of a post from the filename, so I only had to put it there, not both there and in the file’s contents. Well…in the post filename and also the audio filename.
  • I could use macros to take the length of the podcast audio file in seconds, an integer, and convert it into hours + colon + minutes + colon + seconds I used on the web page, while still being able to use it as an integer in the RSS feed.
  • I did have to specify the podcast title in two formats: once in slug format in the post filename (and separately in the audio filename), and once as the full human-readable title. E.g. thanks-send-more and Thanks, Send More.

It all wound up being doable, but I wrote more find/replace logic than I was expecting. Luckily, the Tera programming language, while simple, was up for the task.

One interesting wrinkle is that the “post” file (i.e. podcast episode file), which is an .md file, could not itself contain conditional logic, just plain old data. So when I wanted to use a title in quotes, like Unjustly Maligned Episode #1 “Stargate SG-1” with Jason Snell, I had to put the logic to convert the smart quotes into the appropriate format somewhere else. For HTML pages? “ and ”. But for the podcast RSS file, which would only accept Unicode characters? The vastly less human-readable x201C; and x201D;.

Another example: each podcast episode’s content section contains HTML tags like <p>, for display in web pages and in the <content:encoded> section of the RSS feed. But for the OpenGraph og:description section, that same content had to simply be divided by returns, not HTML tags. Luckily, there are Tera built-in functions to strip HTML tags, and to perform find/replace actions like “find every return and replace it with two returns”.


In addition to Tera, Zola includes Sass, a CSS preprocessor. Sassy was absolutely necessary to follow my policy of a single source of truth in the site’s CSS file.

I found myself wanting to, say, use a single value to set the left and right margins of a page, so I could iterate through different values without having to do a bunch of tedious copy and pasting throughout the CSS file.

Sass lets you use variables in your CSS file that are set to one value, but then used in a bunch of places. During site generation, it “compiles” your .scss file by turning all those variables into their concrete values, resulting in a regular CSS file.

It even lets you do simple math on these values.

I only use eight variables total in my .scss file, but it still felt like my visual adjustment/tweaking process became much easier.

One Last Thing

There wasn’t much that Zola wound up not being able to do for me, but there was one thing.

Zola won’t let you generate arbitrary individual files at the top level. Instead, you can only generate files of the pattern NameOfSection/index.html.

This is fine for “post” files (podcast episode files), but I wanted to use the power of the templating engine to automatically generate an .htaccess file for my site.

The best I could do was generate an htaccess/index.html file, and then write my own local script to move it to the right place.

%d bloggers like this: