- Zola 0.22+ (
brew install zola) - Run
zola servefrom the repo root — site is athttp://localhost:1111
content/ Markdown content (pages, blog posts)
templates/ Tera templates (layouts, components)
sass/ SCSS partials → compiled to main.css
static/ Static assets (JS, images, fonts)
Create a new file in content/blog/:
content/blog/YYYY-MM-DD-slug-name.md
For posts with images, use a directory instead:
content/blog/YYYY-MM-DD-slug-name/
├── index.md
├── diagram.png
└── architecture.svg
Zola copies co-located assets to the output automatically.
+++
title = "Post Title"
description = "One-line summary — shown on listing cards and in <meta> tags."
date = 2026-03-05
draft = true
ready = true # opt in to auto-publishing on `date`; omit to keep it a pure draft
[taxonomies]
tags = ["deep-dive", "meld"]
+++datecontrols sort order on the blog listing, and (forreadyposts) the day the cron publishesdescriptionis required — it appears on the blog index cardstagsrender as accent badges and generate tag pages at/tags/<name>/draft = trueexcludes the post from production buildsready = trueis the explicit opt-in that lets the daily cron auto-publish the post oncedatearrives
A post is in exactly one of these states. The key property: a draft never auto-publishes unless you explicitly opt in with ready = true. Forgetting the flag can't ship a post early.
| State | Frontmatter | What the cron does |
|---|---|---|
| Draft | draft = true (no ready, or ready = false) |
Nothing — stays unpublished indefinitely. Preview with zola serve --drafts. |
| Ready / scheduled | draft = true + ready = true + date |
On/after date, flips draft = false, commits, deploys. Before date it shows as scheduled. |
| Published | draft = false |
Live on the site. |
hold = true is still honored as a hard belt-and-suspenders override (forces held even if ready = true), but it's no longer needed — absence of ready is the safe default.
To publish a post:
- Write it with
draft = true(and noready) — it's a private draft. - Preview locally:
zola serve --drafts. - When you genuinely want it to go live, add
ready = trueand setdateto the intended publish day. The daily cron publishes it on/after that date. - To publish immediately by hand instead, just set
draft = false, commit, push — deploy is automatic.
| Feature | How |
|---|---|
| Table of contents | Generated from ## and ### headings — appears as a glass card above the post |
| Syntax highlighting | Fenced code blocks with language tag (```rust). Theme: material-theme-ocean |
| Copy to clipboard | Code blocks get a "Copy" button on hover (top-right corner) |
| Reading time | Automatic via Zola |
| Tags | Badge links to tag listing pages |
| Article width | 720px max, centered |
Co-located (recommended for post-specific images):
Place the image file next to index.md in a post directory. Images get a glass border and rounded corners automatically.
Shared images:
Place in static/img/ and reference as:
Image captions — two options:
Markdown style (italic text after image):

*Figure 1: The component fusion pipeline*Component style (recommended for important figures):
{{< figure src="architecture.png" alt="Architecture overview" caption="Figure 1: The component fusion pipeline" />}}The component renders a semantic <figure>/<figcaption> with centered caption styling.
Use the mermaid component for inline diagrams. Mermaid JS loads only on pages that use it.
{% mermaid() %}
graph LR
A[.wasm] --> B[meld]
B --> C[loom]
C --> D[synth]
D --> E[kiln]
{% end %}The Mermaid theme is pre-configured to match the site's dark palette — no manual styling needed. The component renders as <pre class="mermaid"> to preserve newlines through Zola's HTML minification.
Avoid HTML inside Mermaid node labels — use plain text or Mermaid's native formatting. The content goes through Zola's markdown processor, so raw HTML tags get escaped.
For complex diagrams that don't change often, consider pre-rendering as SVG and including as an image instead (no client-side JS dependency).
Use the note component for callouts:
{% note() %}
Default info-style callout.
{% end %}
{% note(kind="tip") %}
A tip with green accent.
{% end %}
{% note(kind="warning") %}
Warning with amber accent.
{% end %}
{% note(kind="danger") %}
Danger with red accent.
{% end %}Open every post with {% insight() %} directly after the frontmatter, before the
first ## heading. All 19 posts published before this guide said so used one; it is
the house opening, not an option. It carries the thesis in four to eight lines, and it
must not state a claim the body does not carry.
{% insight() %}
The one paragraph you would want a reader to keep if they read nothing else.
Links and `inline code` work; the body is rendered as Markdown.
{% end %}Long or heavily-cited posts also carry the deep-dive tag, and close on a takeaway
section rather than trailing off.
This table is the full set. If you add or remove a file in templates/components/,
change this table in the same commit — a component the guide does not mention is a
component nobody uses, and one the guide invents fails the build.
| Component | Purpose | Usage |
|---|---|---|
insight |
Opening thesis block — required, see above | {% <insight> %}...{% </insight> %} |
note(kind="...") |
Callout box (info/tip/warning/danger) |
{% <note kind="tip"> %}...{% </note> %} |
mermaid |
Mermaid diagram | {% <mermaid> %}...{% </mermaid> %} |
figure(src, alt, caption) |
Raster/vector image with semantic caption | {{< figure src="img.png", alt="...", caption="..." />}} |
tree(root, parts) |
ASCII-art directory tree | {% <tree root="crates"> %}...{% </tree> %} |
project_card(name, desc, url, ...) |
Glass card for a project; optional icon, badge, label, releases, vscode |
{{< project_card name="meld", desc="...", url="...", icon="🔗", badge="accent" />}} |
compliance_artifact(id, prefix) |
One rivet artifact, resolved from site data | {{< compliance_artifact id="REQ-001" />}} |
compliance_stats(prefix) |
Artifact counts by type | {{< compliance_stats prefix="requirement" />}} |
factory_views |
The factory views panel; takes no arguments | {{< factory_views />}} |
There is no pipeline component. The pipeline SVG lives in the page templates
(about.html, projects.html, how-it-works.html) and is not callable from Markdown.
Use CommonMark footnotes, not a hand-numbered list: [^key] at the point of the claim
and [^key]: ... definitions at the bottom under ## Sources and caveats. config.toml
sets bottom_footnotes = true, and sass/_blog.scss styles the rendered list, the
back-references and the :target highlight. Give every definition a tier — primary,
secondary, peer-reviewed, or not read — so a reader can weigh it, and open the section
with a paragraph naming whatever you could not access.
A footnote reference does not work inside a component body. Component bodies are
rendered through a separate | markdown call, so [^key] inside {% note %} or
{% insight %} will not resolve. Put the reference in the surrounding prose instead.
These markdown elements are styled for the dark glassmorphism theme:
## H2— top margin + subtle bottom border separator### H3— top margin, no border> blockquote— accent-blue left border, italic`inline code`— glass-surface background, mono font (Atkinson Hyperlegible Mono)- Fenced code blocks — glass-surface background, syntax highlighted
- Tables — styled with hover highlights
- Lists — indented, dimmed text
- Links — subtle underline that brightens on hover
+++
title = "..."
description = "..."
date = YYYY-MM-DD
[taxonomies]
tags = ["..."]
+++
{% insight() %}
The thesis in one paragraph — required, and it goes here, above everything else.
{% end %}
Opening paragraph — hook the reader, state what this post covers.
## First section
Body text with context.

*Caption explaining the diagram*
## Implementation details
Code examples:
\```rust
fn example() -> Result<()> {
// ...
}
\```
{% note(kind="tip") %}
Highlight important takeaways in callout boxes.
{% end %}
## What's next
Wrap up and point to related resources.
## Sources and caveats
Access caveats first, then `[^key]: ...` definitions with a tier on each.| Token | Value | Usage |
|---|---|---|
$bg |
#0f1117 |
Page background |
$surface |
#1a1d27 |
Card/panel backgrounds |
$surface-raised |
#242836 |
Elevated elements |
$border |
#2e3345 |
Borders |
$text |
#e1e4ed |
Primary text |
$text-dim |
#8b90a0 |
Body text |
$text-faint |
#5c6070 |
Meta text, captions |
$accent |
#6c8cff |
Links, highlights |
$green |
#4ade80 |
Success/verified |
$red |
#f87171 |
Error/danger |
$amber |
#fbbf24 |
Warning |
$cyan |
#22d3ee |
Info/secondary accent |
$purple |
#c084fc |
AI/MCP category |
<span class="badge badge--accent">Label</span>
<span class="badge badge--green">Label</span>
<span class="badge badge--amber">Label</span>
<span class="badge badge--cyan">Label</span>
<span class="badge badge--purple">Label</span>
<span class="badge badge--red">Label</span>- Sans: Atkinson Hyperlegible Next (Google Fonts)
- Mono: Atkinson Hyperlegible Mono (Google Fonts)
main is protected — all changes go through pull requests with a passing CI build.
Use Conventional Commits:
feat: add dark mode toggle
fix: correct pipeline SVG viewBox on mobile
docs: update blog authoring guide
Common prefixes: feat, fix, docs, style, refactor, chore
| Trigger | Workflow | What it does |
|---|---|---|
PR to main |
CI (.github/workflows/ci.yml) |
zola build — validates the site compiles |
Push to main |
Deploy (.github/workflows/deploy.yml) |
zola build → tar + scp to Netcup |
After merging a PR, the deploy runs automatically (~20s). Check status:
gh run list --workflow=deploy.yml --limit=3 # recent deploys
gh run view <run-id> --log-failed # debug failuresgit checkout -b feat/my-change
# make changes
zola build # verify locally
git add <files>
git commit -m "feat: description"
git push -u origin feat/my-change
gh pr create --title "feat: description"
# CI runs → merge → auto-deployPush to main triggers GitHub Actions:
zola buildtar+scp+sshextract to Netcup (rsync not available on shared hosting)
Target: pulseengine.eu on Netcup Webhosting 1000 NUE.
Secrets required: DEPLOY_HOST, DEPLOY_USER, DEPLOY_KEY, DEPLOY_PATH
zola serve # http://localhost:1111 with live reload
zola build # build to public/
zola check # validate internal links