Mermaid in a Web Worker: the getBBox rules, side by side#
Mermaid 12.2.0 can run in a Web Worker in the browser: on a DOM without layout (linkedom), with getBBox, getComputedTextLength and getBoundingClientRect answered by a small measuring layer (text widths from OffscreenCanvas.measureText). The page's main thread then never runs mermaid.
Checked against Chrome on every example of mermaid's docs/syntax at 12.2.0 (538 examples, 33 diagram types including zenuml; htmlLabels: false, dagre, classic look): each example is drawn on the main thread and in the worker, both in Chrome, and compared. 538 of 538 match: the size within 5%, and every text within max(8 px, 2% of the diagonal).
Each rule below was needed because some diagram type drew differently without it. For each one: mermaid in Chrome, the worker as it is, and the worker with only that rule switched off. The images are captured, so they keep showing the difference whatever mermaid version is current. The source is under each one, as text.
1. CSS cascade order#
When a font setting is given in several places, the inline style wins over the stylesheet, and the stylesheet wins over the plain SVG attribute, so the worker has to measure text in the font the browser will actually use.
Example: ishikawa #0 of the docs.
Mermaid in Chrome (main thread)

The worker

The worker without this rule

ishikawa-beta
Blurry Photo
Process
Out of focus
Shutter speed too slow
Protective film not removed
Beautification filter applied
User
Shaky hands
Equipment
LENS
Inappropriate lens
Damaged lens
Dirty lens
SENSOR
Damaged sensor
Dirty sensor
Environment
Subject moved too quickly
Too dark
2. Empty elements in a group's box#
An empty label that holds an empty <tspan> still counts in Chrome as a single point at (0,0) in its group's box, and ignoring it changes the boxes mermaid lays the diagram out with: here the relationships come out shorter and the tables closer together.
Example: entityRelationshipDiagram #13 of the docs.
Mermaid in Chrome (main thread)

The worker

The worker without this rule

erDiagram
direction LR
CUSTOMER ||--o{ ORDER : places
CUSTOMER {
string name
string custNumber
string sector
}
ORDER ||--|{ LINE-ITEM : contains
ORDER {
int orderNumber
string deliveryAddress
}
LINE-ITEM {
string productCode
int quantity
float pricePerUnit
}
3. dominant-baseline middle = half the x-height#
Switching it off only shifts a whole diagram by 2 to 4 px: nothing moves relative to anything else, so a picture cannot show it.
4. SVG whitespace#
SVG drops newlines, turns tabs into spaces, collapses runs of spaces and trims the ends, so text measured with its raw spaces comes out wider than the browser draws it, and mermaid moves the label out of its bar.
Example: gantt #1 of the docs.
Mermaid in Chrome (main thread)

The worker

The worker without this rule

gantt
dateFormat YYYY-MM-DD
title Adding GANTT diagram functionality to mermaid
excludes weekends
%% (`excludes` accepts specific dates in YYYY-MM-DD format, days of the week ("sunday") or "weekends", but not the word "weekdays".)
section A section
Completed task :done, des1, 2014-01-06,2014-01-08
Active task :active, des2, 2014-01-09, 3d
Future task : des3, after des2, 5d
Future task2 : des4, after des3, 5d
section Critical tasks
Completed task in the critical line :crit, done, 2014-01-06,24h
Implement parser and jison :crit, done, after des1, 2d
Create tests for parser :crit, active, 3d
Future task in critical line :crit, 5d
Create tests for renderer :2d
Add to mermaid :until isadded
Functionality added :milestone, isadded, 2014-01-25, 0d
section Documentation
Describe gantt syntax :active, a1, after des1, 3d
Add gantt diagram to demo page :after a1 , 20h
Add another diagram to demo page :doc1, after a1 , 48h
section Last section
Describe gantt syntax :after doc1, 3d
Add gantt diagram to demo page :20h
Add another diagram to demo page :48h
5. x/y/dx/dy belong to the next character, the inner one wins#
A dy on a <text> and a dy on its first <tspan> do not add up: the tspan's value replaces the outer one, so adding them puts the first line a whole line lower and stretches the timeline.
Example: timeline #5 of the docs, with a 32 px font size added (the first line) so the difference shows.
Mermaid in Chrome (main thread)

The worker

The worker without this rule

%%{init: {"themeVariables": {"fontSize": "32px"}}}%%
timeline TD
title MermaidChart 2023 Timeline
section 2023 Q1 <br> Release Personal Tier
Bullet 1 : sub-point 1a : sub-point 1b
Bullet 2 : sub-point 2a : sub-point 2b
section 2023 Q2 <br> Release XYZ Tier
Bullet 3 : sub-point <br> 3a : sub-point 3b
Bullet 4 : sub-point 4a : sub-point 4b
6. Transforms and nested <svg> viewBox#
An icon is a nested <svg> whose content is in viewBox units and must be scaled onto its width and height before it counts in the box, otherwise mermaid frames the icon in the wrong place.
Example: flowchart #68 of the docs.
Mermaid in Chrome (main thread)

The worker

The worker without this rule

flowchart TD
A@{ icon: "fa:user", form: "square", label: "User Icon", pos: "t", h: 60 }
7. Emoji ink#
Chrome adds a colour emoji's own ink to the text's box, and it rises above the font's ascent, so without it each row of a tree with emoji comes out shorter.
Example: treeView #11 of the docs.
Mermaid in Chrome (main thread)

The worker

The worker without this rule

treeView-beta
🚀 rocket-app/
📦 packages/
🎨 ui/
🛠️ utils/
🧪 tests/
📝 README.md
⚙️ config.yaml