The static-site generator behind jlt-commons project documentation. A project writes markdown; this turns it into a site that GitHub Pages serves.
It is a small babashka program. The only external dependency is markdown-clj,
fetched on first run.
A project keeps its own docs, its own config and its own homepage. The engine keeps the rendering. Nothing about a project lives in this repository, which matters in an organization where the person maintaining a project is usually not the person maintaining the engine.
Each project's CI checks this repo out at a pinned tag, builds, and deploys to that project's own Pages site. No repository ever pushes into another, so no cross-repo tokens exist to leak.
Give the project a docs/ directory:
your-project/
docs/
site.edn # config, all keys optional
guide/
index.md # pinned first in the nav
other-page.md
templates/ # optional, only if you want a bespoke homepage
home.html
demos/ # optional, any assets your pages reference
Then docs/site.edn:
{:base-path "/your-project"
:title "your-project"
:description "One sentence. It becomes the meta description and the footer."
:asset-dirs ["demos"]
:home-template "home.html"}:base-path is the one that bites. A project site is served at
jlt-commons.github.io/your-project/. Leave the base path out and every
stylesheet, script and nav link resolves against the organization site
instead. The page still renders, wearing the wrong clothes, which is what makes
it easy to ship. site.core-test covers it.
Leave :base-path as "" only for a site served at a domain root.
| Key | Default | What it does |
|---|---|---|
:base-path |
"" |
Prefix for every URL the engine emits |
:title |
the README's first H1, else the directory name | Site title and brand |
:description |
the title | Meta description and footer line |
:github-url |
git remote get-url origin, as https |
The nav's GitHub link |
:asset-dirs |
none | Directories under docs/ copied into the build verbatim |
:templates-dir |
templates |
Where project templates live, relative to docs/ |
:home-template |
none, so the generic homepage | A file in the templates dir |
:asset-dirs is an allowlist, and each entry names one directory directly under
docs/. No separators, no leading dot, no ... This decides what gets copied
into a published site, so a project that could write ["../../.ssh"] would
publish it.
bb test # the test suite
bb build ../your-project # generate ../your-project/_site/
bb serve ../your-project # build, then serve at http://localhost:3000
bb serve ../your-project 4000
bb clean ../your-project # delete the build output_site/ is generated. Add it to the project's .gitignore.
One caveat on bb serve: it serves the build at the server's root, so a site
with a :base-path will 404 its own assets locally while working perfectly once
deployed. To check a base-pathed site properly, copy the build into a directory
named after the base path and serve its parent.
Ship no template and the engine renders docs/guide/index.md (falling back to
README.md) into a plain, readable page. That is enough for most projects, and
onboarding one needs nothing but markdown.
Ship docs/templates/home.html and it is used instead. Write it as a Selmer
template extending the engine's chrome:
{% extends "base.html" %}
{% block title %}your-project{% endblock %}
{% block body-class %}home{% endblock %}
{% block content %}
<h1>...</h1>
<a href="{{site-base}}/guide/index.html">Read the guide</a>
<img src="{{site-base}}/demos/thing.gif">
{% endblock %}Every internal URL goes through {{site-base}}. A bare /guide/index.html in a
project template points at the organization site.
Available variables: site-title, site-brand, site-tagline, site-github-url,
site-base, site-docs-href.
A project template overrides any engine template of the same name, so a bespoke
404.html or docs.html works the same way. Overriding base.html means
inheriting none of the engine's later fixes to it, so prefer a block override
where one will do.
Standard markdown through markdown-clj, plus:
- Syntax highlighting for Clojure and Scheme, via a vendored highlight.js.
- Mermaid diagrams. A
```mermaidfence becomes a rendered diagram, themed to match the reader's light or dark setting. - Links between guide pages are rewritten from
.mdto.html, including across subdirectories. - Long reference pages get their
<h2>sections wrapped in collapsible<details>, with anchors, in-page find and printing all handled.
The project's own workflow does the deploying. See
raylib-jlt
for a working one to copy: it checks this repo out at a tag, builds, asserts the
output is not empty, and deploys to Pages from main only.
Pin a tag rather than tracking main, so a change here cannot break a project's
docs build without someone choosing it.
A trimmed port of a private single-author engine, reduced to one site and given
base-path support for the organization site
(jlt-commons.github.io),
then extracted here and generalized: project-supplied config and templates, asset
directories, a generic homepage, and mermaid.
The organization site still carries its own copy of the earlier port. Moving it onto this engine is worth doing and has not been done yet.