← back
The Tiny Website
by Alex Lance
463 words, non-fiction, ©2025


The question is "Why?". Why not use Wordpress? Why not use Ghost? There are hundreds of ways to create a tiny website and they each have their pros and cons.

My response: HTML, Javascript and CSS are complex enough, I don't want to put more abstractions on top of them, or to become reliant on a third-party app with uncertain longevity and robustness.

The main problem I wanted to solve was code duplication. The side menu on this website needed to appear on every page. There are solutions like generating the menu on the server-side. Or putting the menu into a frame. Or copying the menu code around from page to page. Or dynamically constructing it from Javascript on every page. But for me, the solution that seemed the least worst was to add a small compilation step that automatically runs when vim performs a save.

So this website is just HTML files and a Makefile to smoosh them together.

Ok this isn't unique, there are hundreds of static website generators out there. But it's six lines of code and it makes me feel happy:

Makefile

SRC_HTML := $(wildcard src/*.html)
OUT_HTML := $(patsubst src/%,%,$(SRC_HTML))

all: $(OUT_HTML)

%.html: src/%.html header.html
    MAIN="$$(cat $<)" envsubst < header.html > $@

.PHONY: all
Which means: when make all is executed, take the files in the folder named "src/" and mix them together with header.html to create top-level files.

Eg: this article is taking /src/tiny.html and /header.html and generating the entire top-level file /tiny.html that you are reading now.

It uses the program envsubst to swap environment variables in a file, in this case the contents of /src/tiny.html get assigned to the $MAIN variable and envsubst swaps it into header.html:

header.html

<html>
  <head>...</head>
  <body>
    <header>
      (side menu code)
    </header>

    <!-- Page content gets swapped in here -->

    $MAIN
  </body>
</html>
And because it's make, it only builds particular files if it really needs to (i.e. if the file in src/ or the header.html has changed). The end result is a compile step that's faster than a blink.

To execute make all when my text editor saves, it means adding a snippet to my vim configuration.

.vimrc

" update blog after a write

augroup BlogProject
    autocmd!

    " ensure pwd is correct for make

    autocmd BufEnter /path/to/blog/* lcd /path/to/blog

    " re-generate html files whenever something changes in src/ folder

    autocmd BufWritePost /path/to/blog/src/* silent! make all | redraw!
augroup END
This approach feels minimal and understandable. It results in fast page loading, and the utilities involved: make, envsubst and vim are very widely available and will likely outlive us all.

However, I do concede that in writing all of this down it does feel like I'm a crazy person, but perhaps that's just the hidden cost for a tiny slice of perfection.


← back