sailorfe

2026 Feb 21 – A small, sharp tool

I've been working on a static site generator with Jinja as a templating engine since September mostly to address my annoyance with web conventions that every page is secretly called index.html. We're insulated from this by "pretty URLs" and CMS and continuous deployment that keeps built files out of sight, but I handle HTML directly for hobby purposes on the small web where you simply drag-and-drop files into a web GUI. This is friction as a user on top of build inefficiency: every mkdir that a larger SSG runs under the hood takes more time and uses more blocks.

Essentially, all I wanted was for the Markdown input and HTML output directories to look the same, save for file extensions .md.html. This website, for example, is written in Markdown and stored

.
|-- blog/
|   |-- 2026-02-19-a-small-sharp-tool.md
|   `-- index.md
|-- index.md
|-- portfolio.md
`-- reading.md

Pretty URls turn this into

.
|-- blog/
|   |-- 2026-02-19-a-small-sharp-tool/
|   |   `-- index.html
|   `-- index.html
|-- index.html
|-- portfolio/
|   `-- index.html
`-- reading/
    `-- index.html

What I had posted on this hobby site was copied directly from my Markdown notes vault with light YAML, so for a few months I manually deleted any YAML before building with the initial script. I also manually edited a feed.xml and a JSON for non-blog data.

The iteration you see now is called Kamote because that's what I'm eating for breakfast lately. Its biggest improvements are parsing YAML, generating an Atom feed, a simple watch/serve wrapper, and a thin CLI so my commands are

uv run kamote build
uv run kamote watch

benchmarking

I made Git worktrees for the final commit on the 11ty branch and the first commit with Kamote so that the input Markdown is the same, ideally resulting in a hardly any output difference. I ran the time command twice with each.

$ time uv run kamote build
$ time npx @11ty/eleventy
user system cpu total
Eleventy 2.75s 0.23s 124% 2.401
Kamote 1.56s 0.20s 94% 1.857

Cold run

user system cpu total
Eleventy 2.87s 0.19s 118% 2.597
Kamote 0.39s 0.06s 99% 0.448

Warm run (incremental build)

The time that means the most to humans here is total. For incremental builds, Eleventy takes almost three seconds while Kamote is done in less than one.

The resulting HTML has never been that large, but disk usage:

$ du -sh pages-11ty/docs
4.7M    pages-11ty/docs

$ du -sh pages-kamote-initial/sailorfe/_build
4.6M    pages-kamote-initial/sailorfe/_build

I ran ncdu for nicer, more informative output and found that the output /blog had a 44KiB difference of 240KiB on Eleventy and 196KiB on Kamote, which is likely due to Eleventy nesting directories for pretty URLs.

future

The earliest version of this hardcoded my site information in the main generation module. My main experimentation is happening over on my hobby site, so I've been working across two Git repositories with different purposes and content, which is wildly inefficient.

The best improvement so far is configuration with site.json, which lets me replicate Eleventy's site data for globals like {{ site.title }}. I don't see myself packaging this anytime soon, but you're welcome to clone this repo and play around in the dev branch. Packaging would take... god, I don't know...

I also can't figure out pymdownx.tasklist for the life of me.

Recent posts