Skip to content

Creating a Theme

How to add a new template or theme without breaking the render pipeline โ€” the token contract, the shared skeleton, registration, XHTML variants, and the validation gates.

Browse Docs pages

Every template in bones/templates/ is a standalone HTML file rendered by the Oliver wrap dialect: the adapter feeds it the page's frontmatter tokens, the rendered body, and the asset root, and oliver wrap interpolates the $token$ / $if(token)$ markers. This page is the walkthrough for adding a new template or theme without breaking the pipeline โ€” the ground truth is oliver-contract.md plus the adapter source (bones/scripts/rc-oliver-adapter.sh).

1. Start from a member

Copy an existing theme pair โ€” theme-spooky-dark.html + theme-spooky.css are the plainest โ€” rather than writing a template from scratch. The shared skeleton is the contract: variation between themes lives in CSS, ornament, and surface treatment, not in divergent HTML structure.

2. The token contract

Typed tokens (html-escaped by wrap): $title$, $description$, $author$, $date$, $palette$, $version$, $subtitle$, $tags$, $asset_meta$. Raw tokens: $assets_root$ (asset prefix, literal) and $body$ (the rendered markdown, never escaped). Gating uses $if(name)$ โ€ฆ $endif$ โ€” keep the markers at column 0 so a removed block leaves no blank line. The generic hook (#269) interpolates any other frontmatter key the adapter merges into wrap_meta โ€” the necropolis theme's $page_type$ body hook is the example. Unknown $tokens$ pass through verbatim.

3. The shared skeleton

<footer class="<theme-footer>">
  <p class="footer-credit">Rendered by Rotkeeper ยท v$version$</p>
$if(asset_meta)$      <p class="footer-asset-meta">$asset_meta$</p>
$endif$$if(tags)$      <p class="footer-tags">$tags$</p>
$endif$    </footer>

v$version$ is live from bones/config/version (the same single source --version uses). Lore lines live above the slot (see #4).

4. Identity primitives (optional)

The haunted house voice โ€” dividers, lore blocks, icons โ€” lives in home/assets/css/rk-identity.css (#251). Opt in with one line at the top of the theme stylesheet (an @import must precede all other rules), then map the --rk-* tokens on your :root. Full contracts and the token table are in Theme Families under "Shared identity primitives". A theme that doesn't want the voice simply doesn't import the file.

5. Register it

The config-driven theme registry (theme_registry in bones/config/rotkeeper.yaml, #252) maps mode names to template files and is the per-site selector. Add your template to the registry so new lists it and status/render/glue resolve it; the resolver validates every registered entry exists. Per-page template: frontmatter always wins over the registry default.

6. XHTML variants (optional)

XHTML output is opt-in per page (render_profile: xhtml) or per site, and needs a wrapper variant โ€” the theme-spooky-dark-xhtml.html pattern (self-closing void elements, xmlns). Rendering fails closed on raw HTML under --to xhtml (error.RawHtmlNotXmlWellFormed), so keep content CommonMark-safe for XHTML pages or accept the constraint.

7. Validate before you call it done

Command What it proves
bash rotkeeper.sh render The site still renders with your theme wired in
bash rotkeeper.sh showcase Scaffolds a showcase page for your theme and refreshes the gallery โ€” every theme renders the same evaluation body
bash rotkeeper.sh a11y The gate auto-discovers your theme via its stylesheet link and follows @import chains: AA contrast pairs, visible focus, narrow-viewport overflow strategy
bash rotkeeper.sh test Full harness 3/3 layouts + contract/DIP/regression; the S7 registry assertions require your registered template to exist
bash rotkeeper.sh status Config sanity, registry resolution, manifest checks

When you change an existing template's structure, rendered goldens diverge โ€” regenerate them with RK_REGEN_TEMPLATE_GOLDENS=1 bash rotkeeper.sh test and commit the goldens alongside the template change.

Common mistakes


Back to: Theme Families ยท Documentation overview