Ovellum

3 min read · Updated

Building a manual-mode site#

Manual mode is the simplest pipeline. You write Markdown; Ovellum produces HTML, CSS, and a tiny bit of JavaScript. There's no source parsing, no merge engine, no orphan archive — just rendering.

The minimum project#

my-docs/
  ovellum.config.json
  content/
    index.md
    getting-started.md

Config:

{
  "mode": "manual",
  "input": "./content",
  "output": "./dist",
  "site": {
    "title": "My docs"
  }
}

Build:

npx ovellum build

Result:

dist/
  index.html
  getting-started/index.html
  assets/
    ovellum.css
    ovellum.js

Pretty URLs are the default. Every page becomes <slug>/index.html so the URL is /<slug>/. No server-side rewrites needed; works on any static host.

Adding navigation#

The sidebar nav is built automatically from your file tree. Page titles come from:

  1. The frontmatter title: field, if set.
  2. The first # H1 in the body, otherwise.
  3. The filename, as a last resort (getting-started.mdGetting started).

For ordering and group titles, drop a _meta.json into any directory:

content/
  guides/
    _meta.json
    install.md
    configure.md
    deploy.md
{
  "title": "Guides",
  "order": ["install", "configure", "deploy"]
}

order is a list of slugs (file or subdirectory names without .md). Anything not listed sorts alphabetically after the explicit set.

Adding the right-side ToC#

There's nothing to enable — the right column populates automatically from every page's ## h2 and ### h3 headings. Each heading also gets a clickable # anchor on hover, so readers can deep-link.

You don't need a single configuration line for any of this. Write Markdown, get a working ToC.

Static assets#

Anything in content/ that isn't a .md file passes through verbatim:

content/
  images/
    architecture.svg
    screenshot.png
  hello.md

Reference assets with relative paths in your Markdown:

![Architecture](/images/architecture.svg)

After build:

dist/
  images/
    architecture.svg
    screenshot.png
  hello/index.html

Landing page #

Manual mode has an opt-in landing template for the root URL. Disabled by default — if you don't set site.landing.enabled: true, / renders whatever content/index.md says, with the regular doc layout.

When you do enable it, / becomes a Material for MkDocs-inspired homepage: hero, feature grid, optional prose body, optional trust strip. The topbar gains a "Docs" link so readers always have a path into the documentation proper.

{
  "site": {
    "landing": {
      "enabled": true,
      "docsHref": "/getting-started/",
      "hero": {
        "title": "My project",
        "subtitle": "What it does in one sentence.",
        "ctas": [
          { "label": "Get started", "href": "/getting-started/" },
          { "label": "GitHub", "href": "https://github.com/me/proj", "style": "secondary" }
        ]
      },
      "features": [
        { "title": "Fast", "description": "Builds in seconds." },
        { "title": "Themed", "description": "Auto/light/dark out of the box." }
      ]
    }
  }
}

If you have a content/_landing.md file, its prose body renders between the feature grid and the trust strip. Treat it as the "Why" section.

Full landing reference: config → site.landing.

Theme switching#

Three themes ship in the default template: auto (follow OS), light, and dark. The topbar toggle cycles between them; the choice is remembered in localStorage and applied before paint, so there's no theme flash on subsequent loads.

If you want to ship a different default for first-time visitors, set site.defaultTheme to light or dark. See Theming for restyling beyond the defaults.

Static-site essentials#

The default template ships with sensible defaults for the things that matter:

  • Light + dark themes from the same OKLCH palette.
  • System fonts only — no @font-face, no FOIT.
  • Build-time syntax highlighting via shiki; zero runtime JS for code.
  • Pre-paint theme script (no flash on reload).
  • Copy buttons injected client-side onto every code block.
  • Responsive grid: sidebar drops first, then collapses on narrow viewports.
  • Accessible: focus rings, semantic landmarks (<header>, <main>, <aside> with aria-labels), and proper heading levels.

Everything is generated; nothing here is configurable for now. The theming guide covers what's customisable today.

Edit this page