Skip to content

Evidence — Mermaid diagram figures (ZER0-013)

Proof for the Mermaid rework: _includes/components/mermaid.html (loader) + assets/js/mermaid-diagrams.js (behaviour) + _sass/components/_mermaid.scss (chrome), replacing the ~400-line inline <script>/<style> include. Generated by test/visual/mermaid-evidence.mjs; every number below is read off the live page, not asserted by eye.

Routes: /quickstart/ (the single flowchart the bug was reproduced on) and /docs/features/mermaid-diagrams/ (12 diagrams, 8 types, one intentionally broken).

Before → after

BEFORE is the old include on main (tracked changes stashed, site rebuilt, same script run with EVIDENCE_STATE=before). AFTER is this branch. Both states capture the same scenes so the montages compare like with like.

# Scene Before After
01 Light, 1280px 01-before-light-1280.png — Mermaid's stock purple (#9370DB) borders on white nodes, "trebuchet ms". 01-after-light-1280.png — bordered figure; node fill is a tint of the skin brand, border is the brand (--bs-primary), body font. Toolbar appears on hover.
02 Dark mode, after a live toggle 02-before-dark-after-toggle-1280.png — the diagram is gone. The div shows a wall of #mermaid-… {font-family: …} text (its own leaked stylesheet) plus a permanent "Loading diagram…" label. 02-after-dark-after-toggle-1280.png — re-rendered from source with a dark palette (node fill rgb(56, 76, 95), light ink).
03 Phone, 390px (touch) 03-before-mobile-390.png — scaled to fit, no way to enlarge. 03-after-mobile-390.png — same fit-to-width, plus a static toolbar row above the diagram (never covering it) with 36px tap targets; zoom or fullscreen enlarge it.
04 Skin neon 04-before-skin-neon-1280.png — still the broken state from scene 02 (nothing recovers). 04-after-skin-neon-1280.png — purple nodes: border rgb(131, 56, 236) = the skin's --bs-primary.
05 Syntax error (-> instead of -->) 05-before-syntax-error.png — Mermaid's bomb icon, "Syntax error in text", the source discarded. 05-after-syntax-error.png — error card with the parse message, a hint, and the source kept in an open <details>; only copy stays enabled.
06 Zoomed to 195 % — 06-after-zoomed-195.png — the SVG itself is resized (678 → 1325px) so the frame scrolls; page overflow stays 0.
07 Fullscreen — 07-after-fullscreen-1280.png — native <dialog> with title + toolbar + close; the same SVG node moved in (and back on Escape).
08 Series colours — 08-after-pie-light.png (12-colour series fanned out from the brand hue), 08-after-gantt-dark.png (done / active / crit bars from muted / accent / danger tokens; dark-ink text on the light brand bar).

Metrics (metrics.json / metrics-before.json)

Scene SVG present leaked CSS on screen node fill node border toolbar page overflow
light 1280 — before yes no #ffffff #9370db (Mermaid default) — 0 px
light 1280 — after yes no rgb(226, 238, 248) rgb(111, 168, 220) (= --bs-primary) floating 0 px
dark after toggle — before no yes — — — 0 px
dark after toggle — after yes no rgb(56, 76, 95) rgb(111, 168, 220) floating 0 px
back to light — before no yes — — — 0 px
back to light — after yes no rgb(226, 238, 248) rgb(111, 168, 220) floating 0 px
skin neon — before no yes — — — 0 px
skin neon — after yes no rgb(238, 227, 252) rgb(131, 56, 236) floating 0 px
mobile 390 — before yes no #ffffff #9370db — 0 px
mobile 390 — after yes no rgb(226, 238, 248) rgb(111, 168, 220) static, above the diagram 0 px
syntax error — before bomb SVG no — — — 0 px
syntax error — after no (error card, source kept) no — — floating (copy only) 0 px
zoomed 195 % — after yes, 1325 px wide no frame scrolls 0 px
fullscreen — after in dialog focus inside dialog, scroll locked, closed on Escape
docs page — after 11 of 12 rendered, 1 error card; types: flowchart, sequence, class, state, er, pie, gantt, git 0 px

Two things worth reading off that table:

  • The toggle bug is a total loss, not a glitch. Before, one colour-mode switch leaves hasSvg = false for the rest of the page's life — the "back to light" and "skin" rows never recover. After, every row has its SVG and the palette follows the mode and skin.
  • The palette really is the theme's. The after border colour equals --bs-primary in both skins (#6fa8dc air, #8338ec neon); no colour is hard-coded in the component.

A second bug this surfaced

While chasing grey pie slices, the culprit turned out to be assets/js/obsidian-wiki-links.js: it rewrites #hashtag-shaped tokens in text nodes and skips STYLE ancestors by nodeName — but SVG-namespace elements report style in lower case, so it walked into every rendered diagram's <style> and turned its #id selectors into tag links, unscoping the rules for the whole page. Fixed in the same PR (case-insensitive check, <svg> subtrees skipped, .zer0-diagram exempt), with a fixture in test/test_resolver.js and a pie-fill assertion in the spec.

Regression test

test/visual/features/mermaid.spec.js — 12 tests, green on Chromium: fences → figures for 8 types (+ no code-copy chrome, no stylesheet leak), toolbar/caption a11y, colour-mode toggle re-renders from source, skin recolour, error card keeps source, zoom resizes + scrolls, keyboard zoom, fullscreen dialog + focus return, clipboard copy, no overflow at 320/390 px, static toolbar on touch. test/test_resolver.js covers the wiki-link fix.

Regenerate

docker compose up                                              # serves :4000
BASE_URL=http://localhost:4000 node test/visual/mermaid-evidence.mjs      # after
# before: stash this branch's tracked changes, rebuild, then
EVIDENCE_STATE=before BASE_URL=http://localhost:4000 node test/visual/mermaid-evidence.mjs