A minimalist, client-side static site generator that turns Markdown files into a fast, searchable SPA using vanilla JavaScript and Caddy.
/opt/www/quillhut-static/ <-- Web Root ├── index.html <-- The engine & layout ├── nav.json <-- Header/Footer links ├── index.md <-- Homepage content ├── rss.xml <-- Generated Feed ├── sitemap.xml <-- Generated Sitemap ├── manifest.json <-- Standalone pages (about, index, etc) ├── posts/ │ ├── manifest.json <-- List of all post filenames │ ├── post-1.md <-- Individual post │ └── post-2.md
Your server must handle "fallback" routing so that if a user refreshes /posts/my-story, Caddy knows to serve index.html.
yourdomain.com {
root * /opt/www/quillhut-static/
file_server
# Metadata headers
header /rss.xml Content-Type "application/rss+xml; charset=utf-8"
header /sitemap.xml Content-Type "application/xml; charset=utf-8"
# SPA Routing
try_files {path} /index.html
}
nav.json{
"header": [
{ "name": "home", "path": "/" },
{ "name": "posts", "path": "/posts" }
],
"footer": [
{ "name": "source", "path": "https://sourcehut.org", "external": true }
]
}
Posts live in /posts/. Use YAML-like frontmatter:
---
title: My First Post
date: 2024-05-22
tags: tech, web
author: linuxgoose
---
Post content goes here...
Add the filename to posts/manifest.json:
["post-1.md", "post-2.md"]
Because this is a static site without a database, robots (like RSS readers or Google) cannot see the content generated by JavaScript. You must manually generate the "static" XML files when you publish new content. Generating RSS & Sitemap
Open your website in a browser.
Open the Developer Console (F12 or Ctrl+Shift+I).
Type the following command and hit Enter:
generateRSS(); generateSitemap();
Two files (rss.xml and sitemap.xml) will download to your computer.
Upload these files to your server's root directory.
Zero Build Step: Just upload Markdown and refresh.
Tag Filtering: Automatic tag cloud generation from post metadata.
Clean URLs: No .html extensions required. (set USE_CLEAN_PATHS to true)
Dark Mode: Automatic support based on system preferences.
Lightweight: Powered by marked.js and vanilla JS.
Open source under the MIT License. Contributions welcome.