Last updated

Mermaid Integration

MAIDR makes Mermaid diagrams accessible: every diagram type Mermaid draws -- flowcharts, sequence, class, state and entity-relationship diagrams, gantt charts, mindmaps, pie charts and the rest -- becomes navigable with the keyboard and readable as speech, sonification and braille.

The adapter does not guess from the drawing. It parses the diagram's text with Mermaid's own parser, reads what the parser produced, and pairs that with the SVG Mermaid drew, so a reader hears exactly what the author wrote: a flowchart's branch labels, a class's members, a relationship's cardinality, a message's place in its loop.

Quick Start

Load Mermaid, MAIDR and the Mermaid adapter, turn off Mermaid's automatic rendering, and let the adapter render instead:

<pre class="mermaid">
flowchart LR
  order[Order received] --> stock{In stock?}
  stock -->|yes| pack[Pack items]
  stock -->|no| back[Back-order]
</pre>

<script src="https://cdn.jsdelivr.net/npm/mermaid@12.2.0/dist/mermaid.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/maidr/dist/maidr.js"></script>
<script src="https://cdn.jsdelivr.net/npm/maidr/dist/mermaid.js"></script>
<script>
  mermaid.initialize({ startOnLoad: false });
  maidrMermaid.bindMermaid(mermaid);
</script>

bindMermaid takes the place of mermaid.run(): it renders every .mermaid element on the page, one at a time, and makes each one accessible. Click a diagram or Tab to it, then use the arrow keys.

startOnLoad: false matters. Mermaid replaces each diagram's text with its drawing, and the adapter needs the text; with startOnLoad left on, Mermaid would draw the diagrams before the adapter could read them.

Using with npm/Bundlers

npm install maidr mermaid
import mermaid from 'mermaid';
import 'maidr';
import { bindMermaid, createMaidrFromMermaid } from 'maidr/mermaid';

mermaid.initialize({ startOnLoad: false });
await bindMermaid(mermaid);

Binding a diagram you render yourself

When you call mermaid.render yourself, hand the adapter the same text and the SVG it produced:

const text = 'sequenceDiagram\n  Alice->>Bob: Hello\n  Bob-->>Alice: Hi';
const { svg, bindFunctions } = await mermaid.render('greeting', text);
container.innerHTML = svg;
bindFunctions?.(container);

const drawing = container.querySelector('svg');
const maidr = await createMaidrFromMermaid(mermaid, text, drawing, { title: 'Greeting' });
drawing.setAttribute('maidr', JSON.stringify(maidr));

renderMermaid(mermaid, element, text?) does the same for one element: it renders text (or the element's own text) into the element and binds it.

Quarto

A {mermaid} cell is made accessible as it is, with nothing to add to it. Either install the MAIDR Quarto extension, which also covers {dot} (Graphviz) and Observable Plot {ojs} cells:

quarto add xability/maidr
filters:
  - maidr

or load the scripts yourself:

---
title: "Order handling"
format:
  html:
    include-in-header:
      text: |
        <script src="https://cdn.jsdelivr.net/npm/maidr/dist/maidr.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/maidr/dist/mermaid.js"></script>
---

```{mermaid}
%%| label: fig-order
%%| fig-cap: "How an order is handled"
flowchart LR
  order[Order received] --> stock{In stock?}
  stock -->|yes| pack[Pack items]
  stock -->|no| back[Back-order]
```

Quarto draws its {mermaid} cells itself, once the page has loaded, with the Mermaid it bundles, and replaces each cell's text with the drawing. On a page Quarto rendered -- recognised by the <meta name="generator" content="quarto-…"> it writes -- the adapter starts by itself (watchQuartoMermaid()). It keeps each cell's text before Quarto draws it, and binds the drawings once Quarto has drawn them all, so the diagram the reader gets is the one Quarto drew, with its label, caption and cross-reference. The scripts have to be in the head, where both ways above put them, to run before Quarto draws. To keep the adapter from starting on a Quarto page, set window.maidrMermaidAutoInit = false before the script loads.

Quarto 1.10 bundles Mermaid 11.12, and the adapter reads it. Mermaid 11 does not tag a sequence diagram's message lines and notes with their ids as Mermaid 12 does, so the adapter pairs them with the messages in drawing order instead. Cells rendered to an image (mermaid-format: png or svg) have no diagram text left in the page and are not read.

How It Works

  1. Parses the text with Mermaid -- mermaid.mermaidAPI.getDiagramFromText returns the diagram's type and its database: the nodes, edges, messages, tasks or slices the parser read.
  2. Chooses the reading -- each diagram type becomes the MAIDR layer it is read for (see Supported Diagram Types).
  3. Pairs the reading with the drawing -- each point is matched to the element Mermaid drew for it, so the highlight follows the reader. Mermaid's element ids carry counters that move from one parse to the next, so the adapter matches on everything but the counter and falls back to drawing order; a layer whose elements cannot all be paired is read without a highlight rather than with one on the wrong shape.
  4. Hands the figure to MAIDR -- by setting the maidr attribute on the SVG.

The title comes from the diagram's title (or its front matter), and the caption from its accDescr.

Supported Diagram Types

Every diagram type in Mermaid 12 is read, and ZenUML through its plugin. Several become a type MAIDR already had; two -- sequence and Venn diagrams -- were given trace types of their own, because nothing else reads order in time or set membership.

Stable chart types

Diagram Mermaid keyword MAIDR reads it as
Pie pie one pie slice per section; a slice Mermaid leaves undrawn (under 1%) is still read
XY chart (bar and line) xychart one bar or line layer per plot; Page Up / Page Down switch between them
User journey journey a line per section, a task's score with the mood it is drawn with and who takes part
Quadrant chart quadrantChart a point layer of the named points on two axes from 0 to 1, gridded by quadrant; the quadrants are named in the layer title
Wardley map wardley-beta a point layer of the components by evolution and visibility, gridded in quarters, and a directed_graph of the value chain

Experimental chart types

These may change without a deprecation period; see Trace type stability.

Diagram Mermaid keyword MAIDR reads it as
Flowchart [experimental] flowchart, graph a directed_graph: steps, subgraphs as scopes, each arrow by its label, a node's shape when it means something (decision, database)
Sequence diagram [experimental] sequenceDiagram a sequence: messages in time, lifelines, fragments, notes and sequence numbers
ZenUML sequence diagram [experimental] zenuml (with the @mermaid-js/mermaid-zenuml plugin) a sequence: sync, async, creation and reply messages, nested calls and their fragments
Class diagram [experimental] classDiagram a directed_graph: classes with their attributes and methods; inheritance, composition and aggregation with cardinalities; namespaces as scopes
State diagram [experimental] stateDiagram-v2 a directed_graph: states joined by their events, composite states and concurrent regions as scopes, start and end named
Entity relationship diagram [experimental] erDiagram a directed_graph: entities with their attributes and keys, each relationship by its verb and both cardinalities
Requirement diagram [experimental] requirementDiagram a directed_graph: requirements with id, text, risk and verification; elements; relationships by what they assert
Use case diagram [experimental] usecase-beta a directed_graph: actors and use cases inside their system boundaries
C4 diagram [experimental] C4Context and the other C4 levels a directed_graph: people, systems and containers in their boundaries, relationships with their technology
Block diagram [experimental] block a directed_graph: blocks inside composite blocks, block arrows by their direction
Architecture diagram [experimental] architecture-beta a directed_graph: services and junctions in their groups
Git graph [experimental] gitGraph a directed_graph: commits on their branches, each fed by its parents, with tags and merge and cherry-pick commits named
Event model [experimental] eventmodeling a directed_graph: time frames in their swimlanes, along the flow between them
Swimlane diagram [experimental] swimlane-beta a directed_graph: steps lane by lane
Agent flow [experimental] agentflow-beta a directed_graph: an agent's steps, tools and decisions inside its flows
Mindmap [experimental] mindmap a tree from the central topic
Kanban board [experimental] kanban a tree of columns and their cards, each card with its ticket, assignee and priority
Tree view [experimental] treeView-beta a tree of folders and files
Ishikawa diagram [experimental] ishikawa-beta a tree from the effect to its categories of cause and their causes
Timeline [experimental] timeline a tree of sections, periods and their events
Railroad diagram [experimental] railroad-beta, railroad-ebnf-beta, railroad-abnf-beta, railroad-peg-beta a tree of grammar rules and the expressions they are made of
Cynefin framework [experimental] cynefin-beta a tree of domains and what sits in each, and a directed_graph of the transitions between domains
Gantt chart [experimental] gantt a gantt lane per section, each task an interval announced as dates, with its status (done, active, critical, milestone)
Packet diagram [experimental] packet a gantt lane per row of bits, each field an interval of bits
Radar chart [experimental] radar-beta a radar curve per series
Sankey diagram [experimental] sankey a sankey flow per link
Treemap [experimental] treemap-beta a treemap of sections and sized items
Venn diagram [experimental] venn-beta a venn: the sets, then their overlaps by depth, each with its name, size and the items written in it

ZenUML (zenuml) is drawn by a separate Mermaid plugin that keeps no diagram database -- ZenUML parses its own text -- so the adapter reads the text itself, following the grammar Mermaid documents for ZenUML. Register the plugin with Mermaid before binding:

<script src="https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-zenuml@1.0.1/dist/mermaid-zenuml.min.js"></script>
<script>
  const zenuml = window.__esbuild_esm_mermaid_nm['mermaid-zenuml'];
  mermaid.registerExternalDiagrams([zenuml.default ?? zenuml]).then(() => {
    mermaid.initialize({ startOnLoad: false });
    maidrMermaid.bindMermaid(mermaid);
  });
</script>

What the adapter reads, not the drawing

Code Examples

Stable chart types

Pie

<pre class="mermaid">
pie title Pets adopted by volunteers
  "Dogs" : 386
  "Cats" : 85
  "Rabbits" : 42
</pre>

Left and right walk the slices; each announces its value and its share of the whole.

XY chart (bar and line)

<pre class="mermaid">
xychart-beta
  title "Sales revenue"
  x-axis [jan, feb, mar, apr]
  y-axis "Revenue (in $)" 4000 --> 11000
  bar [5000, 6000, 7500, 8200]
  line [5000, 6000, 7500, 8200]
</pre>

The bars and the line are two layers; Page Up and Page Down switch between them.

User journey

<pre class="mermaid">
journey
  title My working day
  section Go to work
    Make tea: 5: Me
    Do work: 1: Me, Cat
</pre>

Each task is announced with its score, the mood Mermaid draws it with, and who takes part: Task is Do work (Me, Cat), Score is 1, unhappy.

Quadrant chart

<pre class="mermaid">
quadrantChart
  x-axis Low Reach --> High Reach
  y-axis Low Engagement --> High Engagement
  quadrant-1 We should expand
  Campaign A: [0.3, 0.6]
  Campaign B: [0.45, 0.23]
</pre>

Wardley map

<pre class="mermaid">
wardley-beta
title Tea shop
anchor Business [0.95, 0.63]
component Tea [0.63, 0.81]
component Kettle [0.43, 0.35]
Business -> Tea
Tea -> Kettle
evolve Kettle 0.62
</pre>

The first layer places each component by evolution and visibility; Page Down reaches the second, the value chain read along its dependencies.

Experimental chart types

Flowchart [experimental]

<pre class="mermaid">
flowchart LR
  order[Order received] --> stock{In stock?}
  stock -->|yes| pack[Pack items]
  stock -->|no| back[Back-order]
  back --> stock
</pre>

Left and right walk the steps in flow order. Each step names what it comes from and leads to, with the arrows' labels: Step is In stock?, From is Order received, Back-order, To is Pack items (yes), Back-order (no), Shape is decision. The rotor's Outputs and Inputs follow the arrows themselves.

Sequence diagram [experimental]

<pre class="mermaid">
sequenceDiagram
  autonumber
  actor U as User
  participant W as Web app
  U->>W: Log in
  loop Until the password matches
    W->>W: Check password
  end
  W-->>U: Welcome
</pre>

Left and right walk the messages in time: Message is Log in, Route is User to Web app, Number is 1. Down moves onto each participant's lifeline, where left and right walk only that participant's messages, and the cursor keeps the moment the reader was at. The rotor's Fragments unit jumps to where the next loop or alternative opens or closes.

Class diagram [experimental]

<pre class="mermaid">
classDiagram
  Animal <|-- Duck
  Animal : +int age
  class Duck {
    +swim()
  }
</pre>

Class is Duck, To is Animal (inherits from), Methods is public swim().

State diagram [experimental]

<pre class="mermaid">
stateDiagram-v2
  [*] --> Idle
  Idle --> Running : start
  Running --> Idle : stop
</pre>

Entity relationship diagram [experimental]

<pre class="mermaid">
erDiagram
  CUSTOMER ||--o{ ORDER : places
  CUSTOMER {
    string name PK
  }
</pre>

Entity is CUSTOMER, To is ORDER (places, exactly one to zero or more), Attributes is string name PK.

Gantt chart [experimental]

<pre class="mermaid">
gantt
  dateFormat YYYY-MM-DD
  section Design
    Research  :done, r1, 2026-01-05, 10d
    Prototype :active, after r1, 15d
</pre>

Section is Design, Research (done), Date is 1/5/2026 through 1/15/2026, Length is 10 days.

Mindmap [experimental]

<pre class="mermaid">
mindmap
  root((Accessible charts))
    Audio
    Braille
</pre>

Down moves from a topic to its first branch and up back to its parent.

Venn diagram [experimental]

<pre class="mermaid">
venn-beta
  set A["Frontend"]:20
  set B["Backend"]:12
  union A,B["Full stack"]:5
</pre>

The first row is the sets; down goes into the overlaps a set is part of: Region is Frontend and Backend, Size is 5, Name is Full stack, Share of its smallest set is 41.7%.

Every other diagram type has a runnable page under Examples.

Highlighting

The highlight outlines the shape Mermaid drew for the point the reader is on: a flowchart's node, a sequence diagram's arrow or note, a gantt task, a Venn region. Where a diagram's shapes cannot all be paired with what was read -- a railroad diagram's expressions are drawn as one path, a journey's tasks are not told apart in the SVG -- the diagram is read without a highlight, and every modality still works.

Configuration Options

createMaidrFromMermaid(mermaid, text, svg, options), renderMermaid(mermaid, element, text?, options) and convertMermaidDiagram(diagram, svg, options) take:

Option Type Description
id string ID of the MAIDR figure. Defaults to the SVG's id.
title string Figure title. Defaults to the diagram's own title or front matter title.
subtitle string Figure subtitle.
caption string Figure caption. Defaults to the diagram's accDescr.

bindMermaid(mermaid, selector = '.mermaid', root = document) renders and binds every matching element and resolves to the figures it made. A diagram that fails to parse or convert is reported on the console and left as Mermaid drew it; the others are still bound.

bindMermaid can be called again, for instance after adding diagrams to the page, and touches only what it has not handled yet. A diagram it already bound is skipped. So is a diagram that failed: it is reported once, marked with a data-maidr-mermaid-failed attribute holding the text it failed on, and not rendered again until that text changes. To retry it with new text, set the element's data-maidr-mermaid-source attribute to the new text, or replace the element's text and remove that attribute.

supportedMermaidDiagrams() lists the Mermaid diagram type names the adapter reads.

Limitations

Examples

One runnable page per diagram type, under examples/mermaid/ and in the examples gallery: flowchart, sequence, ZenUML, class, state, ER, requirement, use case, C4, block, architecture, git graph, event model, swimlane, agent flow, mindmap, kanban, tree view, Ishikawa, timeline, railroad, Cynefin, pie, gantt, packet, quadrant, XY chart, radar, sankey, treemap, user journey, Venn and Wardley map.

Keyboard Controls

Once a diagram is focused, use standard MAIDR keyboard shortcuts:

Function Key (Windows) Key (Mac)
Move between points Arrow keys Arrow keys
Open a scope, group or branch / close it Down / Up Arrow Down / Up Arrow
Change lifeline (sequence diagram) Down / Up Arrow Down / Up Arrow
Switch layers Page Up / Page Down Page Up / Page Down
Follow arrows or jump between fragments Rotor (Alt + Shift + Up / Down), then Left / Right Rotor (Option + Shift + Up / Down), then Left / Right
Toggle Sonification S S
Toggle Braille Mode B B
Toggle Text Mode T T
Toggle Review Mode R R
Description D D

For the full list, see the Keyboard Controls reference.

API Documentation

For the complete TypeScript API reference, see the API Documentation.