My site sccl.cc was originally on Astro.
Astro is a cool thing.
For quickly whipping up something nice it’s fine, but over time it started pissing me off:
Heavy runtime. To build a site you need Node.js, npm, node_modules at a hundred megabytes. For some static pages.
Complexity. Astro does everything: islands, ssr, endpoints, middleware. And I have a site with three pages: contacts, projects, peripherals - I don’t need much.
Alternatives?
I stumbled upon Zine.
Zine is an SSG in Zig. One binary, no dependencies.
Templates use SuperHTML - it’s valid html with templating added. No {{ }}, no Pug/Jade. Just html with attributes like :text, :if, :loop.
Content uses SuperMD - extended .md without needing to drop into html.
Migration process
Overall the migration was pretty simple - rewrite layouts from Astro to SuperHTML, port over existing scripts. The most tedious part is that astro uses its fucking .astro files with their weird mix of js and html, while SuperHTML is just html with extras.
Deploy
Zine is compiled into a standalone statically-linked binary. No dependencies, no runtimes - download and run. It’s not in npm, you can’t install it from any package manager. So you gotta deliver it to the runner somehow.
Options:
- download with curl on every build
- commit to the repository
First option is janky. Second sounds more reliable…
Stuff it in the repo
First thought - just commit zine to the repo so it builds out of the box:
cp /tmp/zine bin/zine
git add bin/zine && git commit
git push
> remote: error: File bin/zine is 152.19 MB; this exceeds GitHub's file size limitAnd then you remember GitHub doesn’t allow files over 100 MB.
time to self-host git
maybe… later…
OK, rollback, pretend nothing happened. Check Zine docs - they recommend kristoff-it/setup-zine@v1 in GitHub Actions.
GitHub Actions
Throw kristoff-it/setup-zine@v1 into gh actions:
- uses: kristoff-it/setup-zine@v1
with:
version: v0.11.3
- run: zine release
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy public/ --project-name sccl-ccMakes sense: action installs zine, zine builds, wrangler pushes to CF Pages.
The only secrets needed are CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID
(Permissions: Account → Cloudflare Pages → Edit).
Should’ve follow the docs, but I wanted to do it without gh actions.
Benefits?
Speed. Build in milliseconds instead of seconds.
Simplicity. No node_modules. No package-lock.json. No hundreds of dependencies. One binary, a content folder, a templates folder.
Control. I know every line of generated HTML. No hidden JS that Astro adds by default.
Downsides?
Zine is in beta.
Documentation is sparse in places. Community is small. If something breaks - you’re the first to notice.
But for a personal site it’s great.
One binary, a couple .smd files, one layout, and done.
How I ended up deploying this thing is described in How-to-ZINE-on-CF.md.