Mermaid Diagrams

mermaid , diagrams

This theme renders Mermaid diagrams from standard fenced code blocks. Just label the fence with mermaid and write the diagram source — no shortcodes required.

How it works

Authoring

Write a fenced code block with the mermaid info string. No front-matter flag, no shortcode:

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

What Zola emits

With syntax highlighting enabled ([markdown.highlighting] in config.toml), Zola tags the code element with data-lang="mermaid" and wraps each line in spans:

<pre class="..." style="..."><code data-lang="mermaid"><span class="..."><span>flowchart LR</span></span>
<span class="..."><span>    A --&gt; B</span></span></code></pre>

If you disable highlighting, you'd get the plainer <code class="language-mermaid"> form instead. The init script handles both.

Runtime pipeline

static/js/mermaid-init.js runs on every page (deferred). On pages without diagrams it does nothing — Mermaid itself only loads when needed:

var blocks = document.querySelectorAll(
    'pre code[data-lang="mermaid"], pre code.language-mermaid'
);
if (blocks.length === 0) return;

For each match, the highlighted <pre> is replaced with a clean container so Mermaid can take over. textContent strips the highlighter's <span> wrappers and decodes entities (--&gt;-->):

blocks.forEach(function (code) {
    var pre = code.parentElement;
    var container = document.createElement('div');
    container.className = 'mermaid';
    container.textContent = code.textContent;
    pre.replaceWith(container);
});

Mermaid is then loaded on demand from the vendored bundle. The URL is passed through a meta tag in base.html so Zola's get_url can resolve it correctly under any base_url:

<meta name="mermaid-src" content="{{ get_url(path='js/mermaid.min.js') }}">
var script = document.createElement('script');
script.src = document.querySelector('meta[name="mermaid-src"]').content;
script.onload = function () { renderAll(); /* ... */ };
document.head.appendChild(script);

Theme integration

The theme option is derived from <html class="dark"> at render time:

function currentTheme() {
    return document.documentElement.classList.contains('dark') ? 'dark' : 'default';
}

window.mermaid.initialize({
    startOnLoad: false,
    theme: currentTheme(),
    securityLevel: 'strict'
});

A MutationObserver on <html>'s class attribute re-renders all diagrams when you toggle dark/light. Each container's original source is cached on dataset.mermaidSource so the second render can reset innerHTML and clear Mermaid's data-processed flag:

var observer = new MutationObserver(function (mutations) {
    for (var i = 0; i < mutations.length; i++) {
        if (mutations[i].attributeName === 'class') {
            renderAll();
            return;
        }
    }
});
observer.observe(document.documentElement, {
    attributes: true,
    attributeFilter: ['class']
});

Why vendor mermaid.min.js?

Zola themes are installed as a git submodule and built without Node.js, so anything the browser needs has to live in static/. The bundle is ~3 MB but only fetched on pages that contain a diagram, so non-diagram posts pay zero cost beyond the tiny init script.

Flowchart

flowchart LR
    A[Start] --> B{Is it working?}
    B -->|Yes| C[Ship it]
    B -->|No| D[Debug]
    D --> B

Sequence Diagram

sequenceDiagram
    participant Reader
    participant Browser
    participant Mermaid
    Reader->>Browser: Open post
    Browser->>Browser: Detect mermaid code blocks
    Browser->>Mermaid: Load mermaid.min.js
    Mermaid-->>Browser: Render SVG
    Browser-->>Reader: Display diagram

Class Diagram

classDiagram
    class Animal {
        +String name
        +int age
        +makeSound() void
    }
    class Dog {
        +String breed
        +bark() void
    }
    class Cat {
        +bool indoor
        +purr() void
    }
    Animal <|-- Dog
    Animal <|-- Cat

State Diagram

stateDiagram-v2
    [*] --> Draft
    Draft --> Review : submit
    Review --> Draft : changes requested
    Review --> Published : approve
    Published --> Archived : retire
    Archived --> [*]

Entity Relationship Diagram

erDiagram
    POST ||--o{ COMMENT : has
    POST ||--o{ TAG : tagged-with
    POST {
        string title
        string slug
        date published
    }
    COMMENT {
        string author
        string body
        date posted
    }
    TAG {
        string name
    }

Gantt Chart

gantt
    title Blog Theme Roadmap
    dateFormat YYYY-MM-DD
    section Core
    Search             :done,    s1, 2026-01-01, 30d
    Dark mode          :done,    s2, 2026-02-01, 14d
    section Content
    Mermaid support    :active,  c1, 2026-05-01, 7d
    Math (KaTeX)       :         c2, after c1, 14d

Pie Chart

pie title Time spent writing posts
    "Outlining" : 20
    "Drafting" : 50
    "Editing" : 25
    "Wrestling with Markdown" : 5

Git Graph

gitGraph
    commit id: "init"
    commit id: "search"
    branch feature/mermaid
    checkout feature/mermaid
    commit id: "vendor mermaid.js"
    commit id: "init script"
    checkout main
    merge feature/mermaid
    commit id: "next feature"

Mindmap

mindmap
  root((Zola Devin))
    Content
      Posts
      Tags
      Comments
    Theming
      Dark mode
      Tailwind
    Features
      Search
      RSS
      Mermaid

Timeline

timeline
    title Theme milestones
    2025 : Project started
         : Tailwind integration
    2026 : Search added
         : Dark mode default
         : Mermaid diagrams

That's a sampling of what's available. See the Mermaid docs for the full diagram catalogue and syntax.