Diagrams
Overview
Accent CMS ships a server-side diagram pipeline that turns text-based diagram sources – Mermaid flowcharts, Svgbob ASCII sketches, and plugin-declared languages (via WASM plugins) – into inline SVG. The SVG arrives in the initial page payload, so search engines and reader-mode clients see the diagram, and there is no flash of unrendered text in the browser.
Three integration levels meet authors where they already write:
- Fenced code blocks – the shortest path. Tag a fenced block with
mermaid,svgbob, orboband the renderer takes over. [diagram]shortcode – adds caption, alt text, theme override, width hints, or an externalsrcfile.diagram()template function – programmatic use from theme templates and frontmatter (planned; see below).
Three render modes choose where the work happens:
| Mode | Server SVG | Client JS | Default in |
|---|---|---|---|
server | yes | – | – |
client | – | yes | – |
hybrid | prod builds | dev | both |
hybrid is the default and is what most sites want: production builds
ship fully rendered SVG, while accent serve in dev mode emits a
pass-through marker so you can iterate without paying the render cost
on every keystroke.
Quick start
Wrap your diagram in a fenced code block tagged mermaid:
```mermaid flowchart LR A[Idea] --> B[Draft] B --> C{Review} C -->|approve| D[Publish] C -->|revise| B ```
renders as:
Supported languages
The diagram pipeline is renderer-agnostic. Each fenced-block language
is routed to the renderer that registered for it, and the supported
set depends on which features were compiled in and which renderers
are enabled in config.yaml.
| Language | Renderer | Binary | Cargo feature |
|---|---|---|---|
mermaid | MermaidRenderer (via mermaid-rs-renderer) | Standard | diagrams-mermaid |
svgbob, bob | SvgbobRenderer | Standard | diagrams-svgbob |
plugin-declared (e.g. d2, dot) | plugin renderer (Component Model) | Standard | diagrams-plugins |
Svgbob is a pure-Rust ASCII-art renderer with no system dependencies.
All three renderers – including the plugin-declared languages – ship in
the Standard floor binary and are free in accent build and
non-production accent serve (free to build, pay to serve); a Standard
license is only required to serve the result in production. Plugin
renderers load through the WASM plugin host; see the migration section
for examples of slotting in d2, dot, or Kroki-style backends.
Mermaid dialects
The Mermaid renderer covers the full 23 DiagramKind variants
implemented in the upstream mermaid-rs-renderer crate. The dialect
is selected by the first non-empty line of the fenced block, matched
case-insensitively as a prefix – so sankey, sankey-beta, and
Sankey-Beta all resolve to the same renderer:
| Header keyword | Dialect | Notes |
|---|---|---|
flowchart / graph | Flowchart | TD/TB/LR/RL/BT directions, subgraphs, decision diamonds |
sequenceDiagram | Sequence | actors, activations, notes, alt/opt/par/loop fragments |
classDiagram | Class | inheritance, association, members and method visibility |
stateDiagram (incl. -v2) | State | states, transitions, composite regions, notes |
erDiagram | Er | entity-relationship cardinality |
pie | Pie | pie chart with optional title |
mindmap | Mindmap | radial mindmap with shape syntax |
journey | Journey | user-journey scoring |
timeline | Timeline | event timeline grouped by section |
gantt | Gantt | task schedule with sections, dependencies, milestones |
requirementDiagram | Requirement | requirements + relations (rendered via the flowchart layout) |
gitGraph | GitGraph | git branch / merge graph |
C4Context (and other C4*) | C4 | C4 architecture (Context / Container / Component) |
sankey-beta | Sankey | sankey flow |
quadrantChart | Quadrant | 2x2 quadrant scatter with axis labels |
zenuml | ZenUML | sequence-diagram dialect via the zenuml syntax |
block-beta | Block | block / partition diagram |
packet-beta | Packet | packet / byte-field layout |
kanban | Kanban | kanban board columns |
architecture-beta | Architecture | infra / service-architecture diagram |
radar-beta | Radar | radar / spider chart |
treemap-beta | Treemap | hierarchical treemap |
xychart-beta | XYChart | x-y axis chart with bars and lines |
The -beta suffix matches the Mermaid project’s naming for dialects
still in active development upstream. Layout coverage and visual
fidelity vary across the newer dialects, but the embedded font and
deterministic SVG emission described in Render modes
apply to every dialect equally – a diagram that renders cleanly once
is byte-stable across platforms.
Dialect samples
The five primary dialects (flowchart, sequenceDiagram,
stateDiagram-v2, classDiagram, erDiagram) get full
copy-pasteable examples in the Agent-authoring
cookbook. The remaining ten dialects
each get a minimal live render below – expand a panel to see the
rendered SVG. All ten render server-side through the currently
pinned mermaid-rs-renderer revision; the embedded inline SVG you
see when the panel opens is the same payload that ships in the
production HTML, with no client-side JS and no CDN. View the page
source for the original Mermaid input.
requirementDiagram -- requirements with id, text, risk, verifymethod
quadrantChart -- 2x2 risk-versus-effort scatter
zenuml -- sequence-diagram dialect
block-beta -- block / partition diagram
packet-beta -- packet / byte-field layout
kanban -- kanban board columns
architecture-beta -- service / infra architecture
radar-beta -- radar / spider chart
treemap-beta -- hierarchical treemap
xychart-beta -- x-y axis chart with bars or lines
Configuration
Defaults work out of the box. Override in config.yaml:
diagrams: enabled: true # master switch (default true) render_mode: hybrid # server | client | hybrid (default hybrid) on_error: warn # warn | fail | silent (default warn) cache: directory: ./.diagram-cache # default; resolved relative to config max_entries: 1024 # moka LRU cap renderers: mermaid: enabled: true # default true theme: default # default | dark | forest | neutral cdn_url: https://cdn.jsdelivr.net/npm/mermaid@11.15.0/dist/mermaid.esm.min.mjs sri_hash: "" # empty = no SRI; pin to sha384-... when self-hosting svgbob: enabled: true # default true plugins: enabled: true # default; free in every edition timeout_ms: 5000 # default per-render budget; warning box on overrun
The cdn_url and sri_hash knobs feed the default-theme client-side
loader. The loader is a small ES module that the default theme injects
in dev mode (only) when the page contains at least one passthrough
diagram block. To self-host Mermaid.js, set cdn_url to your origin
and either compute and pin the SRI hash (recommended, once SRI
emission is wired – see below) or leave sri_hash: "". Production
builds (accent build or accent serve --production) server-render
the SVG and never load the CDN script.
Note
The sri_hash value is validated and accepted today, but the
default theme’s loader does not yet emit the matching integrity=
attribute. SRI for ES module imports requires <link rel="modulepreload">
wiring that lands in a follow-up.
Render modes
server– always render to SVG. Best SEO and first-paint, slowest hot-reload on dev edits.client– emit<pre class="diagram-passthrough">markup for a theme-supplied JavaScript renderer (e.g.mermaid.js) to render in the browser. Useful when you want a single rendering pipeline across server and client, or when you need a Mermaid feature the upstream Rust crate doesn’t yet support.hybrid(default) –serverin production builds (accent build,accent serve --production),clientin dev mode. Trades production SEO/first-paint for fast hot-reload during authoring.
Theme default and the polyfilled dark/forest currently render
with the modern (slate-on-white) palette; neutral renders with the
classic Mermaid (cream/lavender) palette. Real dark and forest
palettes will land when the upstream crate ships them – your config
keeps working through the upgrade.
All four themes use Accent’s embedded Inter font at the same size, so
swapping theme changes colours only – element positions, edge
routing, and overall layout stay identical. The embedded font also
makes the rendered SVG byte-reproducible across Linux / macOS /
Windows: a diagram cached on one platform serves cleanly when the
build is reproduced on another.
Mixing server and client rendering on one page
The site-level render_mode sets the default for every diagram, but
any individual block can opt into a different mode with a render=
attribute. This lets a single page contain both server-rendered SVGs
(static, indexable, fast first paint) and live Mermaid.js diagrams
(interactive, latest Mermaid syntax, live click directives).
| Value | Behaviour |
|---|---|
render=server | Force server-render for this block. Output is an inline <svg>. |
render=client | Force passthrough for this block. Output is <pre class="diagram-passthrough"> and the theme’s Mermaid.js loader renders it in the browser. |
render=hybrid | Identical to omitting the attribute – inherit the site default. Documented for completeness. |
| (omitted) | Inherit the site render_mode. |
Fenced block:
```mermaid render=client flowchart LR A -->|click handler| B click A call openModal() ``` ```mermaid render=server sequenceDiagram Alice->>Bob: ping ```
[diagram] shortcode:
[diagram type="mermaid" render="client" caption="Live build flow"] flowchart LR A --> B [/diagram]
diagram() template function:
{{ diagram("mermaid", page.meta.architecture_diagram,
render="client",
caption="Architecture") }}
A typo (render=cient) surfaces immediately as a warning box (or
fails the build under on_error: fail) so the override is never
silently ignored.
The default theme’s Mermaid.js loader is included whenever a page
contains at least one passthrough block, regardless of how that block
was tagged. A page that opts a single block into render=client under
render_mode: server still ships the loader on that page only;
production output stays loader-free for pages with no passthrough
blocks.
Svgbob ASCII diagrams
For lightweight box-and-arrow sketches, Accent ships svgbob
out of the box. Tag a fenced block with svgbob (or the shorter
bob alias) and the renderer turns ASCII art into a clean SVG:
```svgbob +-------+ +-------+ | A |--->| B | +-------+ +-------+ ```
renders as:
Svgbob is a pure-Rust renderer with no system dependencies, so it works everywhere Accent does. Disable it (or opt in to the shortcode-only path) with the same per-renderer toggle as Mermaid:
diagrams: renderers: svgbob: enabled: true # default
Plugin-provided diagram languages
WASM plugins can extend the diagram pipeline with additional renderers
(D2, Graphviz, PlantUML via WASM ports, etc.). This ships in the Standard
floor binary and works for free in accent build and non-production
accent serve.
A plugin declares the languages it handles in its plugin.toml:
[plugin]
name = "d2-renderer"
version = "0.1.0"
api_version = "0.1.0"
[diagram]
languages = ["d2"]
Built-in language tags (mermaid, svgbob, bob) cannot be
shadowed: a plugin that claims one of them is logged at startup
and ignored.
Configure the per-render timeout and master toggle in
config.yaml:
diagrams: plugins: enabled: true # default; free in every edition timeout_ms: 5000 # default; plugins exceeding this surface a warning box
A plugin that exceeds timeout_ms produces the same inline warning
box as a syntax error (see Error handling below). Builds without
the diagrams-plugins feature parse the plugins config block but
do nothing with it.
[diagram] shortcode
The fenced ```mermaid syntax is the shortest path. For diagrams
that need a caption, accessible alt text, or an external source
file, use the [diagram] shortcode – it routes through the same
renderer and cache as fenced blocks, so output stays equivalent for
the same source:
[diagram type="mermaid" caption="Build flow" alt="A flowchart showing CI build steps"] flowchart LR A[Idea] --> B[Draft] B --> C{Review} C -->|approve| D[Publish] C -->|revise| B [/diagram]
External-file variant – read the diagram source from a .mmd file
next to the page (the body must be empty):
[diagram type="mermaid" src="diagrams/architecture.mmd" caption="System architecture"] [/diagram]
| Attribute | Required | Description |
|---|---|---|
type | yes | Renderer language (mermaid, svgbob / bob, plus future plugin renderers) |
src | – | Path to a diagram source file, relative to the current page’s directory; cannot escape via .. and absolute paths are rejected |
theme | – | Theme override (Mermaid: `default |
width / height | – | Pixel hints forwarded to the renderer |
caption | – | Rendered as a <figcaption> next to the SVG |
alt | – | Forwarded as the SVG’s <title> for screen readers |
class | – | Extra CSS class appended to the wrapper |
You must supply either a non-empty body or src – both or
neither produces a warning box that respects the same on_error
policy as fenced blocks.
diagram() template function
The diagram() template function is the third author path, alongside
fenced blocks and the [diagram] shortcode. Use it when the diagram
source comes from frontmatter, layout context, or a shared template
file rather than the page body itself.
It routes through the same RenderDispatcher and DiagramCache as
the other two paths, so the rendered SVG is byte-identical for the
same source.
Signature
diagram(type, source, *, width=None, height=None, theme=None,
caption=None, alt=None, class=None) -> safe html
| Arg | Kind | Description |
|---|---|---|
type | positional | Renderer language (mermaid, svgbob, bob, plugin-registered) |
source | positional | Raw diagram source text (a string, not a path; pass it inline or from a frontmatter field) |
width | keyword | SVG width in pixels |
height | keyword | SVG height in pixels |
theme | keyword | Renderer theme name |
caption | keyword | Rendered as <figcaption> next to the SVG |
alt | keyword | Forwarded as the SVG’s <title> for screen readers |
class | keyword | Extra CSS class appended to the wrapper |
Frontmatter-driven example
--- title: Architecture Overview architecture_diagram: | flowchart LR Browser --> Accent Accent --> Markdown ---
{% if page.custom.architecture_diagram %}
{{ diagram("mermaid", page.custom.architecture_diagram,
caption="Architecture",
alt="Browser hits Accent which renders Markdown") }}
{% endif %}
Output
When caption, alt, or class is set, the SVG is wrapped in
<figure class="diagram-wrapper diagram-wrapper--<type>"> with a
<figcaption> for the caption. Otherwise it is wrapped in a bare
<div class="diagram-wrapper diagram-wrapper--<type>">. This is the
same wrapping the [diagram] shortcode emits, so theme CSS rules
written for one path apply to the other.
Errors
diagram() honours diagrams.on_error from the config:
warn(default): render an inline warning-box SVG and keep goingfail: raise a MiniJinja error soaccent buildexits non-zerosilent: emit an empty string
Caching
Rendered SVG is cached by SHA-256 of the normalised source plus the
renderer version. Cosmetic edits (whitespace, comments, line endings)
do not invalidate the cache. Editing a .md file invalidates every
diagram block in that file via the file watcher; SIGHUP also clears
everything.
Error handling
When a diagram source has a syntax error the renderer rejects it.
Configure how Accent reports the failure with diagrams.on_error:
diagrams: on_error: warn # warn (default) | fail | silent
| Mode | Behaviour on renderer error |
|---|---|
warn (default) | Substitute the diagram with an inline SVG warning box that shows the offending source, the structured error, and (when available) the line/col marker. Build continues. |
fail | Same warning box, but accent build exits non-zero on the first error so CI surfaces the problem. The error message names the page URL. In accent serve the visible output matches warn. |
silent | Skip the warning box and emit the original fenced code block unchanged so the syntax highlighter can render it. Build continues. Useful when authors are mid-edit and the noise of a warning box is unwelcome. |
The warning box is self-contained inline SVG: no external CSS, no JS, no network calls. It is themable via three CSS variables on a containing element with sensible amber/red fallbacks:
article .content { --diagram-warning-bg: #fef3c7; /* background fill */ --diagram-warning-border: #d97706; /* outer stroke */ --diagram-warning-highlight: #fde68a; /* offending-line bar */ }
For example, a malformed flowchart with an unclosed subgraph:
```mermaid flowchart TD subgraph S1 A --> B ```
renders (in warn mode) as a warning box containing the source, the
parser’s expected end at line N message, and a highlight rect over
the offending line. Fix the source, save, and the warning box is
replaced by the rendered diagram on the next request.
Agent-authoring cookbook
LLM-generated documentation is a primary audience for the diagram
pipeline: a model that emits a Mermaid flowchart inside a markdown
response can have it server-rendered to SVG with no extra tooling. The
patterns below all parse cleanly through mermaid-rs-renderer today.
Each one is followed by its rendered output so the page itself
proves the pipeline is working.
Flowchart
Linear left-to-right flowcharts (flowchart LR) and top-down trees
(flowchart TD) cover most “step 1, step 2, decision, branch” prose.
Square brackets are rectangles, curly braces are diamonds (decisions),
and -->|label| annotates the edge.
```mermaid flowchart LR A[Request] --> B{Cached?} B -->|hit| C[Serve cached] B -->|miss| D[Render] D --> E[Cache] E --> C ```
Sequence diagram
Sequence diagrams render conversations between named actors with
arrows for messages. Use ->> for synchronous calls and -->> for
responses; Note over A,B: ... annotates a span.
```mermaid sequenceDiagram participant Browser participant Server participant Cache Browser->>Server: GET /docs/page Server->>Cache: lookup(page) Cache-->>Server: miss Server->>Server: render markdown Server-->>Browser: 200 OK (HTML) ```
State machine
State diagrams describe lifecycles: [*] is the start/end pseudo-state,
--> marks a transition, and a label after : annotates the trigger.
```mermaid stateDiagram-v2 [*] --> Draft Draft --> Review : submit Review --> Draft : reject Review --> Published : approve Published --> Archived : retire Archived --> [*] ```
Class diagram
Class diagrams document object models. + marks public members, -
private; the <|-- arrow shows inheritance, and a plain -- is an
association.
```mermaid classDiagram class Page { +String title +String url +render() String } class MarkdownPage { +String source +parse() Events } Page <|-- MarkdownPage ```
ER diagram
Entity-relationship diagrams describe data models. Cardinality is
encoded in the link symbols: ||--o{ is “exactly one to zero or
many”, }o--|| is its mirror.
```mermaid erDiagram AUTHOR ||--o{ PAGE : writes PAGE ||--o{ TAG : tagged-with AUTHOR { string name string email } PAGE { string title date published_at } ```
Migration guides
From Hugo
Hugo renders Mermaid via a partial template that wraps the diagram
source in a <pre class="mermaid"> element and ships
mermaid.js from a CDN. The shortcode invocation looks like
{{< mermaid >}}flowchart LR; A --> B{{< /mermaid >}} (custom
themes) or a fenced ```mermaid block in newer Hugo
configurations.
In Accent the fenced block is the canonical path – no theme partial,
no shortcode wiring required. The Hugo params.mermaid toggle in
config.toml maps to Accent’s diagrams.enabled (which defaults to
true). Mermaid theme selection moves from Hugo’s
params.mermaid.theme to diagrams.renderers.mermaid.theme.
| Hugo | Accent |
|---|---|
{{< mermaid >}}...{{< /mermaid >}} | ```mermaid fenced block, or [diagram type="mermaid"]...[/diagram] |
params.mermaid = true | diagrams.enabled: true (default) |
params.mermaid.theme = "dark" | diagrams.renderers.mermaid.theme: dark |
client-side render via mermaid.js | server-side SVG by default; render_mode: client for parity |
From Docusaurus
Docusaurus needs @docusaurus/theme-mermaid enabled in
docusaurus.config.js and markdown.mermaid: true to recognise
fenced ```mermaid blocks. The theme renders client-side: a script
hydrates each block in the browser after page load.
Accent recognises the same fenced syntax with no plugin install. The
key migration is the rendering policy: Docusaurus is client-only,
Accent’s hybrid default is server-render in production and
client-render in dev. To preserve Docusaurus-style behaviour, set
render_mode: client.
| Docusaurus | Accent |
|---|---|
themes: ['@docusaurus/theme-mermaid'] | built-in; remove |
markdown.mermaid: true | diagrams.enabled: true (default) |
themeConfig.mermaid.theme.dark = "dark" | diagrams.renderers.mermaid.theme: dark |
| client-only render | diagrams.render_mode: client (or keep hybrid for prod SVG) |
From MkDocs
MkDocs Material renders Mermaid via the pymdownx.superfences
extension with a custom fence registered through
markdown_extensions:. For non-Mermaid languages the Kroki plugin is
the usual escape hatch and proxies the source to a remote rendering
service.
Accent replaces both: built-in Mermaid and Svgbob support cover the
common cases without any plugin install, and the diagrams-plugins
feature exposes a WASM plugin hook that can host a d2, dot,
or local Kroki bridge in-process – no external service.
| MkDocs / Material | Accent |
|---|---|
pymdownx.superfences custom_fences: mermaid | built-in Mermaid; remove |
Kroki plugin (mkdocs-kroki-plugin) | diagrams-plugins with a local renderer |
Material theme mermaid palette | diagrams.renderers.mermaid.theme |
| client-side render | diagrams.render_mode: server for full SSR |
Troubleshooting
- Diagram doesn’t render, code block shows instead – the fenced
block tag (the bit immediately after the triple backticks) must
match a registered renderer language exactly:
mermaid,svgbob, orbob. Capitalisation matters here. Note that this is a different layer from the Mermaid dialect keyword inside the source body (flowchart,sankey-beta, etc.) – the body keyword is matched case-insensitively as a prefix, soSankey-Betaworks inside the diagram, but```Mermaidas a fence tag does not. Also checkdiagrams.enabled: trueand that the per-rendererenabledflag isn’tfalse. Ifon_error: silentis set, parse failures intentionally fall back to the syntax highlighter; switch towarnto see the underlying error. - Stale render after editing the source – the file watcher
invalidates the in-memory cache when the surrounding
.mdfile is saved, so most edits update without further action. If a stale render persists (e.g. after a deploy or a cross-process change), runaccent cache clear --config <your-config.yaml>to empty the on-disk diagram cache (.diagram-cache/, under the path configured atdiagrams.cache.directory). The command is idempotent and safe to run while the server is stopped or running. For a running server, follow up withpkill -HUP accentso the in-memory cache in the live process is dropped as well. - Warning box on a previously-good diagram – the inline SVG
warning box (Mode
warnorfail) names the parser error. The most common cause after upgrading is a Mermaid syntax extension added in a newer JS version that the Rust crate has not yet picked up; the workaround isrender_mode: clientfor that page, oron_error: silentto suppress the box while you migrate. - Plugin renderer never fires – plugin renderers register
through the WASM plugin host. Confirm the plugin’s
plugin.tomldeclares the diagram type under its[diagram]section, and thatdiagrams-pluginswas compiled into the binary. Built-in tags (mermaid,svgbob,bob) cannot be shadowed: a plugin that claims one is logged and ignored at startup. - Plugin renderer hangs or times out – the per-render
budget defaults to 5000ms via
diagrams.plugins.timeout_ms. A plugin that exceeds the budget produces the same inline warning box as a syntax error. Lower or raise the budget inconfig.yamlif your plugin’s expected render time is consistently outside the default.
Cross-references
- Markdown Guide – frontmatter, shortcodes, syntax highlighting.
- Templating Guide – using diagrams from
theme templates via the
diagram()helper. - Configuration Reference – full
config.yamlschema for every key underdiagrams:.