webolator
  • Usage
  • Links
  • Syntax reference
  • features
    • Mermaid diagrams
    • Syntax highlighting
    • Single-file output
    • Custom styling
    • Writing
    • Reading
  • Publishing to GitHub Pages

features

  • Mermaid diagrams
  • Syntax highlighting
  • Single-file output
  • Custom styling
  • Writing
  • Reading
PreviousSyntax referenceNextMermaid diagrams

Usage

If webolator isn't installed yet, see the installation instructions.

webolator [OPTIONS] <INPUT>

Arguments:
  <INPUT>  Markdown file or directory

Options:
  -o, --out <OUT>                Output directory (default ./site), or output file with --single (default <name>.html)
      --single                   Produce one self-contained HTML file
      --serve                    Serve locally with live reload instead of writing output
      --port <PORT>              [default: 8000]
      --mermaid-js <MERMAID_JS>  Use this local mermaid.min.js (inlined/copied) instead of the CDN
      --css <CSS>                Extra stylesheet applied after the built-in styles (in addition to <root>/webolator.css)
      --exclude <GLOB>           Leave out files matching this gitignore-style pattern (repeatable)
      --no-ignore                Include files that .gitignore / .ignore would leave out (.webolatorignore still applies)
      --no-git                   Don't add "last updated" dates and "edit this page" links from git
      --check                    Fail (exit code 1) if there are broken or excluded links
      --files <DIR>              Show this folder as a file list instead of rendering its contents (repeatable)
      --gallery <DIR>            Show this folder as a thumbnail gallery of its images (repeatable)
      --allow-large              Publish files larger than 10 MB instead of failing
  -h, --help                     Print help
  -V, --version                  Print version

A folder

webolator docs/              # writes ./site
webolator docs/ -o public    # writes ./public

Every markdown file becomes a page. Each folder gets an index.html:

Source Result
index.md or README.md in a folder that folder's index.html
a folder with no index a generated listing page
foo.md foo.html (or foo.md.html if a raw foo.html already exists)
any other file copied as is
hidden files and folders, node_modules, target, venv skipped
anything matched by an ignore rule skipped, see Leaving files out

Image galleries

--gallery works like --files, but shows the folder's images as a grid of thumbnails:

webolator docs/ --gallery screenshots
  • Click a thumbnail to open the full image. Arrow keys step through the folder and Esc closes it.
  • Thumbnails are generated (up to 320 px, PNG, JPEG, GIF and WebP) and written to a _thumbs folder next to the images. Images that can't be decoded, or that are already smaller than their thumbnail, are shown as they are. SVG and AVIF files are always shown as they are.
  • Other files in the folder (and subfolders) are still listed in a table below the grid.
  • Everything else behaves like --files: nothing inside is rendered, names are kept exactly, and it works with --single, where thumbnails and full images are embedded.

Folders of files

Some folders hold downloads, attachments or assets rather than documentation. Pass --files to list such a folder's contents instead of rendering them:

webolator docs/ --files assets --files downloads/archive
  • Nothing inside is rendered. Markdown files are copied as .md, HTML files are served as is, and everything else is copied.
  • The folder and every folder inside it get a file list with each file's name, size and last-changed date. The date comes from git when available, otherwise from the file's modification time. Each subfolder's list has a ../ link back up.
  • The sidebar shows the folder once, tagged files and linking to the list, rather than every file in it.
  • Links from your pages into the folder ([installer](assets/setup.zip), or [assets](assets/) for the list) work as usual.
  • File names are kept exactly, including 01- prefixes, which only order the sidebar outside --files folders.
  • If the folder has its own index.html, that file is kept and the list is written to _files.html instead.
  • With --single, every file is embedded, so downloads work offline. The output grows by the total size of the files.

The path is relative to the input folder, or to the current directory if it doesn't exist there. --files . turns the whole input into a file list. --files also works with a single-file input: the folder is included in full, whether or not anything links to it.

Leaving files out

Three kinds of rules keep files out of the site. All of them use gitignore syntax, including ** and ! negation.

  1. .gitignore files are honored by default. This includes nested ones and those in parent folders, and it works even when the folder isn't a git repository. Build output and scratch files usually stay out without any setup. .ignore files work the same way. Pass --no-ignore to turn this off.

  2. .webolatorignore is for files that belong in git but not on the site. It works like a .gitignore and can be placed in any folder:

    drafts/
    internal-*.md
    !internal-overview.md
    

    --no-ignore doesn't affect it.

  3. --exclude <GLOB> works for one-off runs. It can be repeated, and patterns are relative to the input folder:

    webolator docs/ --exclude 'scratch/' --exclude '*.log'
    

A link to an excluded file stays unchanged and is reported, so you'll notice if something you excluded is still linked:

warning: README.md: drafts/plan.md: target is excluded from the site

With --serve, excluded files aren't watched, and editing .webolatorignore or .gitignore rebuilds the site.

When the input is a single file, the site contains only that file and what it links to, so the ignore rules aren't needed there and aren't applied.

Large files

A build stops with an error if any file it would publish is larger than 10 MB, so a stray video or database dump doesn't end up on the site by accident:

error: 1 file over 10.0 MB would be published:
  media/demo.mp4 (48.2 MB)
Leave it out with --exclude '/media/'
(or in .webolatorignore), or pass --allow-large

The suggested --exclude leaves out the whole folder holding each large file (or just the file, if it's at the top level). Use it as is, put a narrower pattern in .webolatorignore, or pass --allow-large if it really belongs on the site. --serve doesn't check, since it only previews locally.

Ordering

The sidebar is sorted by name, with files before folders. To choose the order, give files and folders a number prefix such as 01-usage.md or 02-guides/. Numbered items come first, in numeric order. The prefix doesn't appear in labels, and it's removed from output paths too, so 02-guides/01-setup.md becomes guides/setup.html. Links in your markdown still use the real file names.

A page can also set its position with order: in front matter, which takes precedence over the prefix. The same order is used for the previous and next links at the bottom of each page.

Checking links in CI

webolator docs/ --check

--check builds as usual, but exits with code 1 if there were any broken links, links to excluded files or unknown [[wikilinks]]. Use it to keep docs from rotting.

The site title is the first heading of the root README.md or index.md. If there is neither, the folder name is used.

A single file

webolator notes.md

webolator follows the file's local links, including links into parent folders, and builds a site from everything it reaches. The file you passed becomes the home page.

Preview

webolator docs/ --serve --port 8000

This serves the site on http://127.0.0.1:8000/. The site is rebuilt whenever a file changes, and open pages reload themselves.

Single HTML file

webolator docs/ --single -o docs.html

See Single-file output.

Last updated 2026-10-08 · Edit this page

PreviouswebolatorNextLinks

On this page

  • A folder
  • Image galleries
  • Folders of files
  • Leaving files out
  • Large files
  • Ordering
  • Checking links in CI
  • A single file
  • Preview
  • Single HTML file

Links

You write links the same way you would for GitHub, and webolator rewrites them to point at the generated pages.

You write Static site gets Single file gets
[x](other.md) other.html #p-other
[x](guide/) guide/index.html the guide's index section
[x](../README.md#install) ../index.html#install #p-readme--install
[x](/guide/setup.md) (root-relative) the relative path to guide/setup.html #p-guide-setup
[x](#section) unchanged prefixed with the page id
[x](report.html), [x](build.log) copied and linked as is shown inside the page
[x](spec.pdf) copied and linked as is opened from embedded data
https://…, mailto:… unchanged unchanged
[[Page name]] the matching page the matching section

Links written as raw HTML inside markdown, such as <img src="…"> and <a href="…">, are rewritten too.

See Wikilinks for how [[…]] names are matched.

Heading anchors

Heading ids follow GitHub's rules, so a #some-heading anchor that works on GitHub also works here. Hover over a heading to see its # link.

Broken links

A link to a file that doesn't exist is left alone and reported during the build:

warning: README.md: broken link: nope.md

Add --check to make these warnings fail the build.

Folders that work from disk

Links to a folder always point at its index.html explicitly. That way a static build also works when opened directly from disk (file://), without a web server.

Last updated 2026-09-27 · Edit this page

PreviousUsageNextSyntax reference

On this page

  • Heading anchors
  • Broken links
  • Folders that work from disk

Mermaid diagrams

A fenced code block whose language is mermaid becomes a diagram:

```mermaid
sequenceDiagram
    You->>webolator: docs/
    webolator->>Browser: index.html
```
sequenceDiagram
    You->>webolator: docs/
    webolator->>Browser: index.html

Diagrams follow the page's light or dark mode.

Another example

stateDiagram-v2
    [*] --> Scan
    Scan --> Render
    Render --> Write
    Write --> [*]
    Write --> Scan: file changed (--serve)

Offline use

By default mermaid.js is loaded from the jsDelivr CDN, and only on pages that contain a diagram. To avoid the network entirely, pass a local copy:

webolator docs/ --single --mermaid-js mermaid.min.js

In a static build the file is copied to _webolator/. With --single it is inlined into the output file.

Last updated 2026-09-27 · Edit this page

PreviousfeaturesNextSyntax highlighting

On this page

  • Another example
  • Offline use

Syntax highlighting

Code blocks are highlighted at build time with syntect, so no JavaScript is involved and highlighting also works in single-file output. Colors follow the reader's light or dark mode.

fn main() {
    let pages = ["index.md", "usage.md"];
    for p in pages.iter().filter(|p| p.ends_with(".md")) {
        println!("rendering {p}");
    }
}
from pathlib import Path

def titles(root: Path) -> dict[str, str]:
    """First heading of every markdown file."""
    return {p.name: p.read_text().splitlines()[0].lstrip("# ") for p in root.rglob("*.md")}
for f in docs/*.md; do
  echo "$f: $(head -1 "$f")"
done
{ "name": "webolator", "config": null }

Use the language's name or file extension after the fence (rust, py, sh and so on). A block with no language, or a language syntect doesn't know, is shown as plain text. The Syntax reference lists every supported language.

Last updated 2026-09-27 · Edit this page

PreviousMermaid diagramsNextSingle-file output

Single-file output

webolator docs/ --single -o docs.html

This produces one .html file that you can email, attach to a ticket or drop anywhere. This documentation as a single file is an example.

What's inside

  • Every page becomes a <section>, and only one is shown at a time.
  • Links become #page-id or #page-id--heading, so the back and forward buttons and deep links work.
  • Images are inlined.
  • Raw .html files are shown in a frame, with a link to open them on their own.
  • Text files (.txt, .log, source code) are shown as preformatted text.
  • PDFs and other files are embedded and open in a new tab when clicked.
  • Mermaid diagrams are drawn when their section is first shown.

Size

Everything is embedded, so large PDFs or images make the file large. mermaid.js is loaded from the CDN unless you pass --mermaid-js.

Last updated 2026-09-27 · Edit this page

PreviousSyntax highlightingNextCustom styling

On this page

  • What's inside
  • Size

Custom styling

There are two ways to add your own CSS. Both are applied after the built-in styles, so your rules win.

  1. webolator.css in the root of your docs folder is picked up automatically, so a styled site still needs no flags. It isn't listed as a page.
  2. --css path/to/file.css adds a stylesheet from anywhere. If both exist, webolator.css comes first and --css after it.

In a static build the combined CSS is written to webolator.css at the site root, so relative url(...) references work just as they do in your source folder. With --single it is inlined into the file, where relative url(...) references won't resolve, so use absolute URLs or data: URIs there. --serve reloads the page when either file changes.

Colors

All colors are CSS variables. Overriding them is usually all a theme needs:

:root {
  --link: #c2185b;
  --side: #fff3e0;
}
Variable Used for
--bg, --fg page background and text
--muted secondary text: blockquotes, footnotes, tags
--border rules, table borders, heading underlines
--code-bg code blocks and inline code
--link links and the active sidebar item
--side, --hover, --active sidebar background, hover and current page
--note, --tip, --important, --warning, --caution > [!NOTE]-style alerts

A plain :root rule of yours overrides both themes. To change only the dark theme, target both ways it can be active: chosen by the OS setting, or by the theme toggle (which sets data-theme on <html>):

@media (prefers-color-scheme: dark) { :root:not([data-theme=light]) { --link: #ff79c6; } }
:root[data-theme=dark] { --link: #ff79c6; }

Page structure

<body>
  <nav class="side">                      <!-- sidebar; missing when there's only one page -->
    <div class="nav-head"><a data-site href="…">Site title</a> <button class="menu-toggle"> <button class="theme-toggle"></div>
    <ul>
      <li><a href="…" class="active">Page</a></li>
      <li><details open><summary><a href="…">folder</a></summary><ul>…</ul></details></li>
    </ul>
  </nav>
  <main>
    <div class="page-wrap">
      <article> … your markdown … <p class="page-meta">…</p> <nav class="pager">…</nav> </article>
      <aside class="toc">…</aside>              <!-- only with 2+ headings -->
    </div>
  </main>
</body>

Inside article you get plain HTML (h1–h6, p, table, blockquote, pre > code), plus a few classes:

Selector What it is
pre.mermaid a mermaid diagram
pre.syntax-highlighting, .hl-* highlighted code and its tokens (.hl-keyword, .hl-comment, …)
.markdown-alert, .markdown-alert-note, … GitHub-style alerts
.footnotes the footnote list
.anchor the # link shown when you hover over a heading
.tag the small file-type label next to non-markdown files in the sidebar
ul.listing the file list on a generated folder page
.page-wrap the article plus its table of contents
aside.toc, .toc-h2, .toc-h3 the "On this page" sidebar
.page-meta the "Last updated · Edit this page" line
nav.pager, .prev, .next previous / next links
.code-wrap, button.copy code block wrapper and its copy button
.theme-toggle, .menu-toggle the light/dark button and the phone menu button
.math-inline, .math-display rendered math
section.page one page, in --single output only

Examples

A narrower serif reading column:

article { max-width: 70ch; font-family: Georgia, serif; font-size: 18px; }

Hide the sidebar:

nav.side { display: none; }

Last updated 2026-09-27 · Edit this page

PreviousSingle-file outputNextWriting

On this page

  • Colors
  • Page structure
  • Examples

Writing

Everything from GitHub-flavored markdown works: tables, task lists, footnotes, strikethrough, autolinks and > [!NOTE] alerts. webolator adds a few extras on top. The Syntax reference lists every form with an example.

Math

Use $…$ for inline math, and $$…$$ or a ```math block for display math. It's rendered with KaTeX, which is loaded from a CDN only on pages that contain math.

Euler: $e^{i\pi} + 1 = 0$

$$\int_0^1 x^2\,dx = \frac{1}{3}$$

Euler: e^{i\pi} + 1 = 0

\int_0^1 x^2\,dx = \frac{1}{3}

Emoji

Shortcodes like :rocket: and :tada: become emoji: 🚀 🎉

Wikilinks

[[Page name]] links to a page by name, as in Obsidian and similar note tools:

You write Links to
[[setup]], [[Setup Guide]] the page whose file name or title matches
[[guides/advanced]] a page by path, without the .md
[[setup|how to install]] the same page, with custom link text
[[setup#Step two]] a heading on that page
[[#Step two]] a heading on the current page

Matching ignores case and treats spaces, - and _ the same. A name that matches nothing is reported as a warning.

Front matter

An optional --- block at the top of a file adjusts that one page. Every field is optional:

---
title: Getting started
order: 1
hidden: true
---
Field Effect
title replaces the first # heading as the page's title in the sidebar, browser tab and previous/next links
order sets the sidebar position and takes precedence over a 01- filename prefix. On a folder's README.md or index.md, it positions the whole folder.
hidden the page is still built and can be linked to, but it's left out of the sidebar and previous/next links

The front matter itself is not shown on the page.

Last updated 2026-09-27 · Edit this page

PreviousCustom stylingNextReading

On this page

  • Math
  • Emoji
  • Wikilinks
  • Front matter

Reading

Every page gets these without any setup.

On this page

A right-hand sidebar lists the page's ## and ### headings and highlights the one you're reading. It appears on pages with at least two such headings, and it's hidden on narrow screens.

Previous and next

At the bottom of each page are links to the previous and next page, in sidebar order. That means the ordering you set with 01- prefixes or front matter also sets the reading order. Pages marked hidden: true are skipped.

Last updated and "Edit this page"

When the docs are in a git repository, each page shows the date of the last commit that changed it. When the origin remote is on GitHub or GitLab, it also shows a link to edit the file there. Files not yet committed get neither. Pass --no-git to leave both out.

In CI, fetch the full history (fetch-depth: 0 in actions/checkout). Otherwise every page shows the date of the latest commit.

Light and dark

Pages follow the operating system's setting. The ☾/☀ button in the sidebar switches themes, and the choice is remembered in the browser. Mermaid diagrams and code highlighting switch too.

Code blocks

Hover over a code block to show a Copy button.

Images

Click an image to see it full size. Click again or press Esc to close it. Images that are links behave as links.

Phones

On narrow screens the sidebar collapses into a ☰ menu at the top.

Last updated 2026-09-27 · Edit this page

PreviousWritingNextPublishing to GitHub Pages

On this page

  • On this page
  • Previous and next
  • Last updated and "Edit this page"
  • Light and dark
  • Code blocks
  • Images
  • Phones

Publishing to GitHub Pages

This site is published by the following workflow (.github/workflows/pages.yml). To use the same approach in your own repository:

  1. In Settings → Pages, set Source to GitHub Actions.
  2. Add this workflow:
name: Docs

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: true

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # full history, for per-page "last updated" dates
      - uses: astral-sh/setup-uv@v6
      - run: uvx webolator docs -o _site --check
      - uses: actions/upload-pages-artifact@v4
        with:
          path: _site

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - id: deployment
        uses: actions/deploy-pages@v4

--check fails the deployment if a link is broken, so a bad link never reaches the published site. webolator's own workflow builds webolator from source instead of running uvx, so the docs always match the current code.

Last updated 2026-09-27 · Edit this page

PreviousReading

webolator

Point it at a folder of markdown and get a website. No config file, no index file, no SUMMARY.md.

This site is itself built by webolator from the docs/ folder of the repository. See Publishing to GitHub Pages for how.

uvx webolator docs/ --serve

How it works

flowchart LR
    A[folder or .md file] --> B[scan / follow links]
    B --> C[render markdown<br/>+ highlight code]
    C --> D[rewrite links]
    D --> E1[static site]
    D --> E2[single HTML file]
    D --> E3[live preview]

Features

  • Zero config. The sidebar comes from the directory tree, and page titles come from each file's first # heading.
  • Mermaid diagrams are rendered in the browser.
  • Syntax highlighting happens at build time, with light and dark themes.
  • Cross-file links to .md files, folders and headings are rewritten to the right place.
  • Sibling files (raw HTML, text, PDFs, images) are kept and linked.
  • Single-file output puts a whole folder of docs into one .html file.
  • Live reload with --serve.
  • File lists: --files assets/ lists a folder of downloads instead of rendering it.
  • Image galleries: --gallery screenshots/ shows a folder of images as a thumbnail grid.
  • Syntax reference: every supported markdown form, with examples.
  • Reading comfort: an "On this page" sidebar, previous/next links, "last updated" and "Edit this page" from git, a light/dark toggle, copy buttons, image zoom and a phone menu.
  • Writing extras: math, emoji, [[wikilinks]] and optional front matter.
  • Leaves out junk: .gitignore is honored, and .webolatorignore and --exclude cover the rest.
  • Custom CSS: drop a webolator.css into the folder, or pass --css.

See it in action

  • The demo site is built from examples/demo. It shows raw HTML, log and PDF files next to the markdown.
  • This documentation as a single file is every page of these docs in one self-contained HTML file.

Install

For a one-off run, you don't need to install anything:

uvx webolator notes.md --single

To keep it around:

uv tool install webolator

For every install option (uv, pre-built binaries, or building from source), see the installation instructions in the README.

Next: Usage.

Last updated 2026-10-08 · Edit this page

NextUsage

On this page

  • How it works
  • Features
  • See it in action
  • Install

Syntax reference

Every markdown form webolator understands. Standard markdown gets a quick summary, and the special forms are shown with their source and how they render on this page.

Standard markdown

Everything in GitHub-flavored markdown works as expected: headings, bold, italic, strikethrough, inline code, lists, - [x] task lists, tables (with :---: alignment), block quotes, footnotes1, images, horizontal rules, and autolinks for bare https://… and www.… URLs.

Raw HTML such as <details>, <kbd>, <sup> and <img width=…> is passed through as is, and its href and src attributes are rewritten like markdown links.

A few details specific to webolator:

  • The first # heading is the page title.
  • Every heading gets a GitHub-style anchor id, so ## Step two becomes #step-two.
  • ## and ### headings fill the "On this page" sidebar.
  • Clicking an image zooms it.
  • Images are inlined in --single output.

Emoji

Shortcodes use GitHub's names: :rocket: :tada: :+1: becomes 🚀 🎉 👍. A shortcode that isn't recognized is left as typed.

Links

Form Source
Link to another page [Usage](01-usage.md)
Link to a heading [ordering](01-usage.md#ordering), or [text](#links) on the same page
Link to a folder [features](03-features/) goes to that folder's index page
Root-relative link [x](/01-usage.md), relative to the docs root
Wikilink [[Usage]], [[03-features/01-mermaid]], [[Usage|custom text]], [[Usage#Ordering]], [[#Links]]
Any other file [log](build.log), [spec](spec.pdf), [report](report.html)

For example, [[Usage#Ordering]] renders as Usage#Ordering. See Links for how each form is rewritten and Wikilinks for how names are matched.

Alerts

GitHub-style callouts, in five types:

Note

> [!NOTE]: useful information.

Tip

> [!TIP]: a helpful suggestion.

Important

> [!IMPORTANT]: something the reader must know.

Warning

> [!WARNING]: needs attention.

Caution

> [!CAUTION]: a risk of something going wrong.

Code blocks

```python
print("hello")
```
print("hello")

The word after the fence can be a language name or a file extension. It's case-insensitive, so python, py, Rust and rs all work. These languages are highlighted:

ActionScript, AppleScript, ASP, Batch (bat, cmd), BibTeX, Bash (bash, sh, zsh), C, C#, C++, Clojure, CSS, D, Diff (diff, patch), Erlang, Go, Graphviz (dot), Groovy, Haskell, HTML, Java, Java properties, JavaScript (js), JSON, LaTeX (tex), Lisp, Lua, Makefile, Markdown, MATLAB, Objective-C, OCaml, Pascal, Perl, PHP, Python (py), R, reStructuredText, Ruby, Rust (rs), Scala, SQL, Tcl, Textile, XML, YAML (yml).

Anything else, including TypeScript, TOML, Dockerfile, Kotlin and Swift, is shown as plain text. Every code block gets a copy button.

Diagrams

A ```mermaid block is drawn with Mermaid: flowcharts, sequence, class, state and ER diagrams, Gantt charts and more.

```mermaid
flowchart LR
  A --> B
```
flowchart LR
  A --> B

Math

Form Source
Inline $e^{i\pi} + 1 = 0$
Display $$\sum_{n=1}^{\infty} \frac{1}{n^2} = \frac{\pi^2}{6}$$
Display block a ```math fenced block

\sum_{n=1}^{\infty} \frac{1}{n^2} = \frac{\pi^2}{6}

\begin{aligned}
f(x) &= (x + 1)^2 \\
     &= x^2 + 2x + 1
\end{aligned}

Math is rendered with KaTeX. As on GitHub, a $ followed by a digit doesn't close math, so "costs $5 and $10" stays plain text.

Front matter

An optional block at the very top of a file:

---
title: Shown in the sidebar and browser tab
order: 3
hidden: true
---

These are the only fields, and any others are ignored. See Front matter. This page uses it (title and order: 3) to sit between Links and the features folder.

Special file names

Name Meaning
README.md, index.md the folder's home page (index.md wins if both exist)
01-name.md, 02-folder/ number prefix: sets the order, hidden from labels and URLs
webolator.css (in the root) custom styles, applied automatically
.webolatorignore files to leave out, in gitignore syntax
.gitignore, .ignore honored by default, --no-ignore turns them off

To show a folder as a plain file list instead, see --files.

  1. Like this one. ↩

Last updated 2026-09-27 · Edit this page

PreviousLinksNextfeatures

On this page

  • Standard markdown
  • Emoji
  • Links
  • Alerts
  • Code blocks
  • Diagrams
  • Math
  • Front matter
  • Special file names