Templates are now plugins. The first official template, Summer, ships alongside two new opt-in UI features — cookie consent and an announcement banner — built directly into @docmd/ui. Also includes a Docker :latest fix so quick-start works out of the box, and a handful of post-release hardening fixes below.

✨ What’s new

Templates are now plugins

A template package declares capabilities: ['template'] and ships layout overrides plus its own CSS/JS. The new resolver checks four levels, falling back to the default at any step:

frontmatter.template  →  config.templates[glob]  →  config.theme.template  →  built-in default
// template-summer/index.js
export default {
  plugin: { name: 'template-summer', version: '0.1.0', capabilities: ['template'] },
  templates: [
    { type: 'layout', templatePath: '...' },
    { type: 'sidebar', templatePath: '...' },
  ],
  templateAssets: [
    { type: 'css', path: '.../summer.css', priority: 10, position: 'head' },
  ],
};

Templates can override any of 14 supported slots (layout, 404, sidebar, header, footer, banner, cookie-consent, and more) and can be set per-page via frontmatter, or site-wide via config. User customCss always wins — templates can’t use !important.

Templates — architecture & authoring guide

☀️ Summer — the first official template

A bright, airy layout with a top search bar, soft halo accents, and a denser card grid.

docmd add summer       # installs and switches your config
docmd remove summer    # reverts to default

Built into the default UI. Off unless you opt in.

{
  "cookie": {
    "enabled": true,
    "message": "We use cookies to ensure you get the best experience.",
    "policyUrl": "/privacy",
    "position": "bottom-right",
    "expiryDays": 180
  }
}

Fires a docmd:cookie-consent event on choice, so plugins and templates can react.

📢 Site-wide announcement banner

Sits above the menubar. Supports inline markdown or raw HTML.

{
  "layout": {
    "banner": {
      "content": "**v0.9 ships Friday** — read the announcement.",
      "type": "info",
      "link": { "text": "Read more", "url": "/blog/v0-9" }
    }
  }
}

Dismiss state is stored per-session, so the banner reappears on a fresh visit.

Fixed

Issue Fix
Docker :latest tag never published Workflow now tags :latest automatically on release events
Quick-start crashed on empty /docs in Docker Entrypoint seeds /docs from the bundled template when empty
Docker healthcheck always “unhealthy” Now probes ports 3000–3005 to match the dev server’s auto-increment
i18n strings not translating in sidebar/nav partials t() function now passed to all layout partials; 13 missing keys added across all 7 locales
Copy-code button floated above the code block Button now anchors to the outer wrapper instead of an inner one
Mermaid diagrams broke on SPA navigation (not on full reload) Plugin <script> tags are now synced into the live DOM on every page swap, not just initial load
Sidebar groups with real links only expanded, never navigated Click handler now also matches real links, not just the toggle icon
appearance: "system" did nothing Default-theme partial now reads DOCMD_APPEARANCE (the canonical name) and only honours localStorage when it holds a concrete light/dark value
Non-English search results jumped to the page top instead of the heading Semantic-search client now uses a Unicode-aware slug regex, matching the parser-side generator (fixes Chinese, Japanese, Korean, Cyrillic, diacritics)
Dev server kept rebuilding every few seconds with no edits New content-signature tracker ignores phantom fs.watch events from Spotlight/iCloud/Time Machine; config watcher now watches the parent directory instead of the file directly
tag container didn’t accept quoted URLs or the external: prefix Parser regex now supports "..." and '...' around option values; url: is the canonical property name, link: kept as an alias

Improved

  • Sensible config defaults — a fresh docmd.config.json (or no config at all) now ships a fully-featured site: page navigation, copy-code buttons, breadcrumbs, search, theme switch, and more are all on by default. Full defaults table →
  • Conditional plugin asset loading — plugins can now declare a condition on their assets so JS/CSS only ship to pages that actually need them. Verified on the playground: Mermaid’s ~500 KB library now loads on 3/11 pages (was 11/11) and KaTeX’s ~30 KB CSS on 1/1 math page (was 11/11).
  • Tighter UI — header and menubar height reduced 52px → 44px; smaller heading-anchor icons; denser card grids.
  • New default 404 page — glass-card layout with soft halo background, Return Home / Go Back actions, fully translated and RTL-aware.
  • Contrast pass — text and link colors adjusted across light and dark themes to meet WCAG AA.
  • Redesigned active sidebar link — a “node on thread” pill replaces the old left-border style.
  • Keyboard focus is now visible:focus-visible gets a halo glow site-wide.
  • Step permalinks — every <li> in a docmd steps list now carries a globally-unique id="step-N" and a hidden link-2 icon that fades in on hover or focus, so individual steps are easy to share and deep-link.
  • Changelog entry permalinks — mirrors the step-permalink pattern: every changelog entry date now carries an id and a hidden link-2 icon that fades in on hover, so individual releases are easy to share and deep-link.
  • --verbose is now universal — previously only respected by add/remove. Now sets DOCMD_VERBOSE=true in the environment so every subcommand (build, dev, live, init, validate) can opt-in to detailed logs, and is also forwarded to buildSite and buildLive for the structured path.
  • Docker image works with zero setupdocker run ghcr.io/docmd-io/docmd:latest now seeds a demo site automatically; no volume mount required.
  • Sidebar nav double-click bug — the main UI script could load twice under certain serving conditions, causing click handlers to fire twice and nav state to get stuck. Now guarded against double-execution.
  • xdg-open crash on dev server — a missing browser-launch binary no longer crashes docmd dev.
  • @docmd/plugin-search missing runtime dependencymarkdown-it was incorrectly listed as dev-only; installing the plugin now works on first try.
  • Docker tags simplified — releases now publish exactly two tags: the version number and :latest.

Looking ahead — Node 20+ in v0.9.0

The next minor release raises the floor to Node.js 20+ (from 18+), bringing the engine requirement, shipped Docker runtime, and CI environment all onto Node 22. Node 18 reached end-of-life in April 2025; this keeps docmd on a supported runtime with room to spare.

This release (0.8.7) is unaffected — the >=18 floor stays until 0.9.0 ships.

Now (0.8.x) Next (0.9.0)
Minimum Node 18+ 20+
Docker base 20-alpine 20-alpine
Type Node ^24 ^24
CI target 24 24

Skills Update (docmd-skills@1.1.0)

Skills have been updated for 0.8.7 with three specialised skills that work together. The agent automatically selects the most relevant skill based on the current task and context, reducing unnecessary token usage while maintaining accuracy.

npx docmd-skills [dir]

Replace [dir] with the directory location where you keep agent skills — common choices are ~/.claude/skills/, ~/.cursor/skills/, or a project-level folder like ./.skills/.