Mermaid¶
```mermaid blocks are drawn as SVG by Pele, a Mermaid renderer in TypeScript by the Obsidian team. ope runs it in the JavaScript engine that also runs DataviewJS, so it needs no Node.js, npm or browser. It reads Mermaid 12.1 syntax and draws every diagram type Mermaid has, in a few milliseconds per diagram. A block Pele cannot draw, for example because of a syntax error, stays a code block and the export prints a line starting with Warning:.
Markdown

The output is SVG, so text stays sharp and can be searched.
Options¶
| Option | Effect | Default |
|---|---|---|
-O mermaid=off |
Keep Mermaid blocks as code. | on |
-O mermaid.config=PATH |
JSON Mermaid config, such as themeVariables.fontSize. |
none |
-O mermaid.max_height=LENGTH |
Largest height, such as 10cm. Taller diagrams are scaled down. |
none |
-O mermaid.landscape=on\|off |
Put wide diagrams on landscape pages. | off |
-O mermaid.landscape_ratio=R |
Width to height ratio from which a diagram that has to shrink goes to landscape. | 1.4 |
-O mermaid.landscape_min_font=PT |
Smallest text size a diagram may shrink to on a portrait page before it goes to landscape, or to fit a tall diagram on one page. | 7 |
-O mermaid.fit=on\|off |
Scale or split diagrams that are taller than the page. | on |
A template can set a base config with the mermaid key in template.yaml or Template.mermaid_config(). mermaid.config is merged over it.
Earlier versions drew diagrams with mermaid-cli, which needed Node.js and a headless Chromium. ope no longer uses it. Its options mermaid.renderer, mermaid.theme, mermaid.format, mermaid.scale, mermaid.cli, mermaid.puppeteer_config and mermaid.trim have no effect, and the export prints a warning when one is set. mermaid.renderer=pele is accepted without a warning.
Colors and fonts¶
Pele draws with its own neutral style. Its colors are the CSS variables listed under Theming, such as --pele-fg, --pele-surface, --pele-border, --pele-line, --pele-accent and --pele-series-1 to --pele-series-8. WeasyPrint does not resolve CSS variables in SVG, so ope writes their values into the SVG. They come from the template's CSS:
- The label font is the
font-familyofbody, the monospace font the one ofcodeorpre, and the text color thecolorofbody. Diagrams use the font of the document. themeVariables.fontFamilyin the Mermaid config sets the label font too.themeVariables.fontSizein the Mermaid config or in the front matter of a diagram sets the base font size.- The other
themeVariablesof the Mermaid config or of the front matter of a diagram set the colors of that diagram, see Mermaid theme variables. They win over the template's values. --pele-*variables in a rule for:root,html,bodyor.peleset any color. They may usevar()of other custom properties of those rules.
Mermaid theme variables¶
Pele does not read Mermaid's themeVariables. ope maps each variable that has a counterpart in Pele's drawing, so a diagram looks as it does in Obsidian. A variable in the front matter of a diagram wins over the Mermaid config. These variables apply to every diagram type:
| Theme variable | Effect |
|---|---|
fontFamily |
Font of the labels. |
fontSize |
Base font size. Pele derives the size of every label from it. |
textColor, else primaryTextColor |
Text color (--pele-fg). |
lineColor |
Lines and arrows (--pele-line). |
background |
Background color, also used for text on dark bars (--pele-bg). |
titleColor |
Diagram title, unless a variable below sets it. |
Per diagram type, a list such as taskTextOutsideColor, taskTextDarkColor means the first one that is set wins, as in Mermaid:
| Diagram | Theme variable | Effect |
|---|---|---|
| Flowchart | mainBkg, primaryColor |
Node fill. |
nodeBorder, primaryBorderColor |
Node border. | |
nodeTextColor, primaryTextColor |
Node text. | |
clusterBkg, clusterBorder |
Subgraph fill and border. | |
titleColor |
Subgraph title. | |
edgeLabelBackground |
Background of edge labels. | |
| Sequence | actorBkg, mainBkg, primaryColor |
Participant fill. |
actorBorder, primaryBorderColor |
Participant border. | |
actorTextColor, primaryTextColor |
Participant text. | |
actorLineColor |
Lifelines. | |
signalColor, textColor |
Message lines and arrows. | |
signalTextColor, textColor |
Message text. | |
noteBkgColor, noteBorderColor, noteTextColor |
Notes. | |
activationBkgColor, activationBorderColor |
Activation bars. | |
labelBoxBkgColor, labelBoxBorderColor |
Loop and alt frames, and the box of their keyword. | |
labelTextColor, loopTextColor |
Frame keyword and frame label. | |
| Gantt | taskBkgColor, taskBorderColor |
Tasks and milestones. |
activeTaskBkgColor, activeTaskBorderColor |
Active tasks. | |
doneTaskBkgColor, doneTaskBorderColor |
Done tasks. | |
critBkgColor, critBorderColor |
Critical tasks. | |
taskTextColor |
Label on a bar. | |
taskTextDarkColor |
Label on a done or active bar. | |
taskTextOutsideColor, taskTextDarkColor |
Label next to a bar or milestone. | |
sectionBkgColor, altSectionBkgColor, sectionBkgColor2 |
Section bands. | |
gridColor, todayLineColor, excludeBkgColor, vertLineColor |
Grid, today marker, excluded days, vertical markers. | |
| Timeline | cScale0 to cScale7 |
Color of each period and its events (--pele-series-1 to 8). |
cScaleLabel0 to cScaleLabel7 |
Text of each period and its events. | |
| Quadrant chart | quadrant1Fill to quadrant4Fill |
Quadrant fills. |
quadrant1TextFill to quadrant4TextFill |
Quadrant labels. | |
quadrantPointFill, quadrantPointTextFill |
Points and their labels. | |
quadrantXAxisTextFill, quadrantYAxisTextFill |
Axis labels. | |
quadrantInternalBorderStrokeFill, quadrantExternalBorderStrokeFill |
Quadrant borders. Pele draws one kind of border. | |
quadrantTitleFill |
Title. | |
| Pie | pie1 to pie8 |
Slice colors (--pele-series-1 to 8). |
pieStrokeColor, pieStrokeWidth, pieOpacity |
Slice border and opacity. | |
pieSectionTextColor, pieLegendTextColor, pieTitleTextColor |
Slice labels, legend and title. | |
| XY chart | xyChart.plotColorPalette |
Colors of bars and lines (--pele-series-1 to 8). A shorter list starts again from its first color. |
xyChart.titleColor |
Title. | |
xyChart.xAxisLabelColor, xyChart.yAxisLabelColor |
Axis labels. Pele draws both axes in one color. | |
xyChart.xAxisTitleColor, xyChart.yAxisTitleColor |
Axis titles. | |
xyChart.xAxisLineColor, xyChart.yAxisLineColor |
Axis lines and ticks. |
The colors become --pele-* variables of that diagram or CSS rules for Pele's class names in the SVG. They never change sizes, so labels keep the width they were measured with.
Pele reads most of the per-diagram config itself, for example gantt.barHeight, gantt.fontSize, gantt.useWidth, quadrantChart.chartWidth, quadrantChart.chartHeight, quadrantChart.pointRadius, xyChart.width, xyChart.chartOrientation, timeline.disableMulticolor, pie.textPosition and sequence.mirrorActors. The font sizes of a quadrant chart come from quadrantChart.titleFontSize, quadrantLabelFontSize, xAxisLabelFontSize, yAxisLabelFontSize and pointLabelFontSize. Pele derives the size of other text from fontSize, so keys such as xyChart.titleFontSize or themeVariables.pieTitleTextSize have no effect.
The export prints one warning that lists every key of the Mermaid config with no effect: theme other than default or base, themeCSS, theme variables without a counterpart (such as pieOuterStrokeColor, cScaleInv0 or cScale8 to cScale11) and keys of the gantt, quadrantChart, xyChart, timeline, pie and sequence sections that Pele does not read.
Pele needs the width of every label to lay a diagram out. ope measures them with Pango, the text engine WeasyPrint uses, in the font of the PDF and at the size they are drawn, so labels fit their boxes in any font. When the template font is not installed, labels are measured and drawn in the font that replaces it. Each label is drawn as one line of shaped text, so Arabic letters join, Arabic and Hebrew read from right to left and Hindi or Thai vowel signs stay on their letters.
A character that no installed font has is drawn as □ (or ? when no font has □ either), and the boxes of the diagram are sized for it. The export prints a warning that lists these characters. Install fonts that cover the scripts of the labels, for example Noto, see Fonts and scripts.
Differences from Mermaid¶
Pele reads the same syntax as Mermaid, but it is not mermaid.js. A diagram can look different from the one in Obsidian:
- Pele has its own layout, so boxes and lines can be placed differently.
- Mermaid's built-in themes (
theme: dark,forest,neutral),themeCSSand%%{init}%%theme settings are not used. Theme variables are mapped where Pele has a counterpart, see Mermaid theme variables. Other colors can be set with--pele-*variables in the template, see Colors and fonts. - Pele moves quadrant chart point labels to avoid overlap, so labels can sit in other places than in Mermaid. A point label only wraps when it is wider than the plot area.
layout: elkis not used. Pele always lays a diagram out on its own.- Links and images in diagrams are turned off.
<br>and</br>both break a label into lines.
The Pele build is part of ope (version 1.12.0, MIT license, see the license header of obsidian_pdf_exporter/core/assets/pele.js). ope applies a few small changes to it when it loads, listed in PELE_PATCHES in obsidian_pdf_exporter/core/mermaid_pele.py. The quadrant chart font sizes and the width at which point labels wrap are such a change. It stays until a Pele release has the fix, see obsidianmd/pele#108. scripts/update_pele.py in the repository replaces it with another version and checks it against the hash the npm registry publishes. CI checks that the stored build is the pinned one.
Diagram size¶
A diagram keeps its natural size and only shrinks when it is wider than the text. mermaid.max_height scales down taller ones. Other sizes belong in the template's CSS: every diagram has the classes doc-image, mermaid and mermaid-<type>, see CSS reference.
Pele gives every diagram an exact size with a margin of 8 px, so there is no empty space to trim.
%% comment lines in a block are removed before rendering.
Wide diagrams¶
A diagram wider than the text shrinks, and so does its text. Diagrams stay on portrait pages by default. With -O mermaid.landscape=on, a diagram that gets hard to read goes to its own landscape page, like a wide table.
Markdown

With mermaid.landscape=on, a diagram moves when it has to shrink to fit a portrait page and either its width is at least mermaid.landscape_ratio times its height, or its text would get smaller than mermaid.landscape_min_font points. It only moves when the landscape page shows it larger, so a tall diagram stays where it is. On the landscape page it is sized to fit the page height, with room for a caption.
A short paragraph right below the diagram that starts with a word and a number, such as Figure 1: Overview, is its caption and moves with it. The diagram gets the class landscape, its wrapper wide-table-section landscape-figure, so a template's landscape page for tables applies to diagrams too.
ope export ./Notes -O mermaid.landscape=on
ope export ./Notes -O mermaid.landscape=on -O mermaid.landscape_ratio=2
Tall diagrams¶
A diagram taller than the page would run past the bottom margin. ope keeps every diagram inside the page content box. The size of the page and its margins come from the template, for portrait and landscape pages alike.
- When the diagram's text stays at least
mermaid.landscape_min_fontpoints tall, the diagram is scaled down to the height of the page. It keeps its aspect ratio. A caption below it stays on the same page. - Otherwise the diagram is cut into parts that follow each other over several pages. The parts keep the width the diagram had, so the text keeps its size. Each cut goes through a row near the bottom of the page where little is drawn, so it does not go through a box or a line of text when there is a gap between them.
Markdown
A sequence diagram that is taller than the page goes on over two pages:
```mermaid
sequenceDiagram
participant C as Client
participant S as Server
C->>S: Request 1
S-->>C: Response 1
C->>S: Request 2
S-->>C: Response 2
C->>S: Request 3
S-->>C: Response 3
C->>S: Request 4
S-->>C: Response 4
C->>S: Request 5
S-->>C: Response 5
C->>S: Request 6
S-->>C: Response 6
C->>S: Request 7
S-->>C: Response 7
C->>S: Request 8
S-->>C: Response 8
C->>S: Request 9
S-->>C: Response 9
C->>S: Request 10
S-->>C: Response 10
C->>S: Request 11
S-->>C: Response 11
C->>S: Request 12
S-->>C: Response 12
C->>S: Request 13
S-->>C: Response 13
C->>S: Request 14
S-->>C: Response 14
C->>S: Request 15
S-->>C: Response 15
C->>S: Request 16
S-->>C: Response 16
C->>S: Request 17
S-->>C: Response 17
C->>S: Request 18
S-->>C: Response 18
```
The text continues after the diagram.

Every part holds the whole drawing and shows only its band of it, so a search in the PDF also finds the text of the hidden bands. Fitting needs a second layout of the document, which only runs when a diagram is too tall. -O mermaid.fit=off turns it off, mermaid.max_height gives all diagrams a smaller limit.