From a5c7f55ce36b8cce3c9b68a62ca9b385020e32ca Mon Sep 17 00:00:00 2001 From: Eric Windmill Date: Tue, 18 Aug 2026 13:59:10 -0700 Subject: [PATCH 1/8] Add client-side Mermaid diagram rendering support --- .../common/client/mermaid_diagram.dart | 156 ++++++++++++++++++ packages/site_shared/lib/page_extensions.dart | 1 + .../lib/src/extensions/mermaid_processor.dart | 34 ++++ .../lib/src/markdown/markdown_parser.dart | 2 + .../lib/src/markdown/mermaid_syntax.dart | 44 +++++ sites/docs/lib/main.client.options.dart | 6 + sites/docs/lib/main.server.options.dart | 10 ++ sites/docs/lib/src/extensions/registry.dart | 1 + 8 files changed, 254 insertions(+) create mode 100644 packages/site_shared/lib/components/common/client/mermaid_diagram.dart create mode 100644 packages/site_shared/lib/src/extensions/mermaid_processor.dart create mode 100644 packages/site_shared/lib/src/markdown/mermaid_syntax.dart diff --git a/packages/site_shared/lib/components/common/client/mermaid_diagram.dart b/packages/site_shared/lib/components/common/client/mermaid_diagram.dart new file mode 100644 index 00000000000..de0cf0a9284 --- /dev/null +++ b/packages/site_shared/lib/components/common/client/mermaid_diagram.dart @@ -0,0 +1,156 @@ +import 'dart:async'; + +import 'package:jaspr/dom.dart'; +import 'package:jaspr/jaspr.dart'; +import 'package:universal_web/js_interop.dart'; +import 'package:universal_web/web.dart' as web; + +@client +final class MermaidViewer extends StatefulComponent { + const MermaidViewer({required this.diagram, super.key}); + + final String diagram; + + @override + State createState() => _MermaidViewerState(); +} + +final class _MermaidViewerState extends State { + static int _idCounter = 0; + final String _diagramId = 'mermaid-diagram-${_idCounter++}'; + + String? _svg; + web.MutationObserver? _themeObserver; + + @override + void initState() { + super.initState(); + if (kIsWeb) { + context.binding.addPostFrameCallback(() { + unawaited(_renderDiagram()); + _observeTheme(); + }); + } + } + + @override + void dispose() { + _themeObserver?.disconnect(); + super.dispose(); + } + + void _observeTheme() { + final body = web.document.body; + if (body == null) return; + + var isDark = body.classList.contains('dark-mode'); + _themeObserver = web.MutationObserver( + ((JSArray _, web.MutationObserver _) { + final newIsDark = body.classList.contains('dark-mode'); + if (newIsDark != isDark) { + isDark = newIsDark; + unawaited(_renderDiagram()); + } + }).toJS, + ); + + _themeObserver?.observe( + body, + web.MutationObserverInit( + attributes: true, + attributeFilter: ['class'.toJS].toJS, + ), + ); + } + + Future _renderDiagram() async { + try { + final isDark = + web.document.body?.classList.contains('dark-mode') ?? false; + final theme = isDark ? 'dark' : 'default'; + + // Dynamically import Mermaid ESM from CDN only when a + // diagram component mounts + final module = await _importMermaid(); + final mermaid = module.defaultExport; + + mermaid.initialize( + MermaidConfig( + startOnLoad: false.toJS, + theme: theme.toJS, + securityLevel: 'loose'.toJS, + ), + ); + + final result = await mermaid + .render(_diagramId.toJS, component.diagram.toJS) + .toDart; + + if (mounted) { + setState(() { + _svg = result.svg.toDart; + }); + } + } catch (e) { + if (kDebugMode) { + print('Failed to render Mermaid diagram: $e'); + } + } + } + + @override + Component build(BuildContext context) { + return div( + classes: 'mermaid-container', + [ + if (_svg case final svg?) + RawText(svg) + else + // Fallback during SSR or while loading + pre( + classes: 'mermaid', + attributes: {'data-source': component.diagram}, + [.text(component.diagram)], + ), + ], + ); + } +} + +// ----------------------------------------------------------------------------- +// JS Interop Bindings (Dart 3 Extension Types) +// ----------------------------------------------------------------------------- + +@JS('eval') +external JSAny? _eval(JSString code); + +Future _importMermaid() { + final importFn = _eval('(url) => import(url)'.toJS) as JSFunction; + final promise = importFn.callAsFunction( + null, + 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'.toJS, + ) as JSPromise; + return promise.toDart; +} + +extension type MermaidModule._(JSObject _) implements JSObject { + @JS('default') + external MermaidApi get defaultExport; +} + +extension type MermaidApi._(JSObject _) implements JSObject { + external void initialize(MermaidConfig config); + external JSPromise render(JSString id, JSString text); +} + +extension type MermaidConfig._(JSObject _) implements JSObject { + external factory MermaidConfig({ + JSBoolean? startOnLoad, + JSString? theme, + JSString? securityLevel, + }); +} + +extension type MermaidRenderResult._(JSObject _) implements JSObject { + external JSString get svg; +} diff --git a/packages/site_shared/lib/page_extensions.dart b/packages/site_shared/lib/page_extensions.dart index 3b889b65c0b..e99249c0e16 100644 --- a/packages/site_shared/lib/page_extensions.dart +++ b/packages/site_shared/lib/page_extensions.dart @@ -9,4 +9,5 @@ export 'src/extensions/attribute_processor.dart'; export 'src/extensions/code_block_processor.dart'; export 'src/extensions/header_extractor.dart'; export 'src/extensions/header_processor.dart'; +export 'src/extensions/mermaid_processor.dart'; export 'src/extensions/table_processor.dart'; diff --git a/packages/site_shared/lib/src/extensions/mermaid_processor.dart b/packages/site_shared/lib/src/extensions/mermaid_processor.dart new file mode 100644 index 00000000000..b5c721037e0 --- /dev/null +++ b/packages/site_shared/lib/src/extensions/mermaid_processor.dart @@ -0,0 +1,34 @@ +import 'package:jaspr_content/jaspr_content.dart'; + +import '../../components/common/client/mermaid_diagram.dart'; + +final class MermaidProcessor implements PageExtension { + const MermaidProcessor(); + + @override + Future> apply(Page page, List nodes) async => + _processNodes(nodes); + + List _processNodes(List nodes) { + return [ + for (final node in nodes) + if (node case ElementNode( + tag: 'div', + attributes: {'class': 'mermaid-container'}, + children: [ + ElementNode(attributes: {'data-source': final diagram}), + ..., + ], + )) + ComponentNode(MermaidViewer(diagram: diagram)) + else if (node is ElementNode) + ElementNode( + node.tag, + node.attributes, + node.children != null ? _processNodes(node.children!) : null, + ) + else + node, + ]; + } +} diff --git a/packages/site_shared/lib/src/markdown/markdown_parser.dart b/packages/site_shared/lib/src/markdown/markdown_parser.dart index 60165d98478..dc15c277bc4 100644 --- a/packages/site_shared/lib/src/markdown/markdown_parser.dart +++ b/packages/site_shared/lib/src/markdown/markdown_parser.dart @@ -18,10 +18,12 @@ import 'alert_syntax.dart'; import 'attribute_syntax.dart'; import 'fenced_code_block_syntax.dart'; import 'header_syntax.dart'; +import 'mermaid_syntax.dart'; /// The `package:markdown` block syntaxes to apply when parsing Markdown. const List _blockSyntaxes = [ JasprHtmlBlockSyntax(), + MermaidBlockSyntax(), CustomFencedCodeBlockSyntax(), HeaderWithAttributesSyntax(), AttributeBlockSyntax(), diff --git a/packages/site_shared/lib/src/markdown/mermaid_syntax.dart b/packages/site_shared/lib/src/markdown/mermaid_syntax.dart new file mode 100644 index 00000000000..b4c62535a5b --- /dev/null +++ b/packages/site_shared/lib/src/markdown/mermaid_syntax.dart @@ -0,0 +1,44 @@ +import 'package:markdown/markdown.dart' as md; + +final class MermaidBlockSyntax extends md.BlockSyntax { + const MermaidBlockSyntax(); + + // Matches opening fence: ```mermaid (with optional trailing whitespace/config) + @override + RegExp get pattern => RegExp(r'^\s{0,3}`{3,}mermaid(?:\s.*)?$'); + + static final _closingFencePattern = RegExp(r'^\s{0,3}`{3,}\s*$'); + + @override + bool canParse(md.BlockParser parser) { + return pattern.hasMatch(parser.current.content); + } + + @override + md.Node? parse(md.BlockParser parser) { + // Advance past the opening ```mermaid line + parser.advance(); + + final lines = []; + + // Collect diagram definition until the closing ``` + while (!parser.isDone) { + final line = parser.current.content; + if (_closingFencePattern.hasMatch(line)) { + parser.advance(); // Consume closing fence + break; + } + lines.add(line); + parser.advance(); + } + + final rawContent = lines.join('\n'); + + // Return HTML AST node for the diagram container + final pre = md.Element.text('pre', rawContent) + ..attributes['class'] = 'mermaid' + ..attributes['data-source'] = rawContent; + + return md.Element('div', [pre])..attributes['class'] = 'mermaid-container'; + } +} diff --git a/sites/docs/lib/main.client.options.dart b/sites/docs/lib/main.client.options.dart index 23c1b571e81..ac296cdcf47 100644 --- a/sites/docs/lib/main.client.options.dart +++ b/sites/docs/lib/main.client.options.dart @@ -30,6 +30,8 @@ import 'package:site_shared/components/common/client/download_button.dart' deferred as _download_button; import 'package:site_shared/components/common/client/feedback.dart' deferred as _feedback; +import 'package:site_shared/components/common/client/mermaid_diagram.dart' + deferred as _mermaid_diagram; import 'package:site_shared/components/common/client/on_this_page_button.dart' deferred as _on_this_page_button; import 'package:site_shared/components/common/client/page_header_options.dart' @@ -140,6 +142,10 @@ ClientOptions get defaultClientOptions => ClientOptions( (p) => _feedback.FeedbackComponent(issueUrl: p['issueUrl'] as String), loader: _feedback.loadLibrary, ), + 'site_shared:mermaid_diagram': ClientLoader( + (p) => _mermaid_diagram.MermaidViewer(diagram: p['diagram'] as String), + loader: _mermaid_diagram.loadLibrary, + ), 'site_shared:on_this_page_button': ClientLoader( (p) => _on_this_page_button.OnThisPageButton(), loader: _on_this_page_button.loadLibrary, diff --git a/sites/docs/lib/main.server.options.dart b/sites/docs/lib/main.server.options.dart index fcbd5e0a090..03bb51110df 100644 --- a/sites/docs/lib/main.server.options.dart +++ b/sites/docs/lib/main.server.options.dart @@ -30,6 +30,8 @@ import 'package:site_shared/components/common/client/download_button.dart' as _download_button; import 'package:site_shared/components/common/client/feedback.dart' as _feedback; +import 'package:site_shared/components/common/client/mermaid_diagram.dart' + as _mermaid_diagram; import 'package:site_shared/components/common/client/on_this_page_button.dart' as _on_this_page_button; import 'package:site_shared/components/common/client/page_header_options.dart' @@ -114,6 +116,11 @@ ServerOptions get defaultServerOptions => ServerOptions( 'site_shared:feedback', params: __feedbackFeedbackComponent, ), + _mermaid_diagram.MermaidViewer: + ClientTarget<_mermaid_diagram.MermaidViewer>( + 'site_shared:mermaid_diagram', + params: __mermaid_diagramMermaidViewer, + ), _on_this_page_button.OnThisPageButton: ClientTarget<_on_this_page_button.OnThisPageButton>( 'site_shared:on_this_page_button', @@ -180,6 +187,9 @@ Map __download_buttonDownloadButton( Map __feedbackFeedbackComponent( _feedback.FeedbackComponent c, ) => {'issueUrl': c.issueUrl}; +Map __mermaid_diagramMermaidViewer( + _mermaid_diagram.MermaidViewer c, +) => {'diagram': c.diagram}; Map __page_header_optionsPageHeaderOptions( _page_header_options.PageHeaderOptions c, ) => { diff --git a/sites/docs/lib/src/extensions/registry.dart b/sites/docs/lib/src/extensions/registry.dart index 158f0a3a9c9..51993e1576c 100644 --- a/sites/docs/lib/src/extensions/registry.dart +++ b/sites/docs/lib/src/extensions/registry.dart @@ -16,6 +16,7 @@ const List allNodeProcessingExtensions = [ HeaderExtractorExtension(), HeaderWrapperExtension(), TableWrapperExtension(), + MermaidProcessor(), CodeBlockProcessor(defaultTitle: 'Runnable Flutter example'), GlossaryLinkProcessor(), TutorialNavigationExtension(), From 54f30b0cb37f435d5537e6d5410e4171c523093f Mon Sep 17 00:00:00 2001 From: Eric Windmill Date: Tue, 18 Aug 2026 14:41:15 -0700 Subject: [PATCH 2/8] Add initial mermaid scss files --- .../lib/_sass/components/_mermaid.scss | 55 +++++++++++++++++ .../common/client/mermaid_diagram.dart | 2 + .../lib/src/markdown/mermaid_syntax.dart | 15 +++++ sites/docs/lib/_sass/_site.scss | 1 + sites/docs/src/content/ai/evals.md | 17 ++++++ .../src/content/contribute/docs/markdown.md | 59 +++++++++++++++++++ sites/www/lib/styles/styles.scss | 1 + 7 files changed, 150 insertions(+) create mode 100644 packages/site_shared/lib/_sass/components/_mermaid.scss diff --git a/packages/site_shared/lib/_sass/components/_mermaid.scss b/packages/site_shared/lib/_sass/components/_mermaid.scss new file mode 100644 index 00000000000..eebefd8372a --- /dev/null +++ b/packages/site_shared/lib/_sass/components/_mermaid.scss @@ -0,0 +1,55 @@ +// Copyright 2026 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +.mermaid-container { + display: flex; + justify-content: center; + margin: 1.75rem 0; + padding: 1.5rem 1rem; + overflow-x: auto; + background-color: var(--site-raised-bgColor-translucent); + border: 1px solid var(--site-outline-variant); + border-radius: var(--site-radius); + + // Fallback while loading or during SSR + pre.mermaid { + margin: 0; + padding: 0; + background: transparent; + border: none; + font-family: var(--site-code-fontFamily); + } + + // Rendered SVG styling + // You must use !important to override styles for specific elements within + // the rendered SVG. For styling individual graphs, use Mermaid's built-in + // classRef system + svg { + max-width: 100%; + height: auto; + + // Use Google Sans Flex for diagram labels + text, + .label, + .nodeLabel, + .edgeLabel { + font-family: var(--site-ui-fontFamily), sans-serif !important; + } + + // Sharper node outlines matching site borders + .node rect, + .node circle, + .node ellipse, + .node polygon, + .node path { + stroke-width: 1.5px; + fill: var(--site-secondaryContainer-bgColor) !important; + } + + // Edge lines & arrows + .edgePath .path { + stroke-width: 1.5px; + } + } +} diff --git a/packages/site_shared/lib/components/common/client/mermaid_diagram.dart b/packages/site_shared/lib/components/common/client/mermaid_diagram.dart index de0cf0a9284..da4c207f1ba 100644 --- a/packages/site_shared/lib/components/common/client/mermaid_diagram.dart +++ b/packages/site_shared/lib/components/common/client/mermaid_diagram.dart @@ -79,6 +79,7 @@ final class _MermaidViewerState extends State { startOnLoad: false.toJS, theme: theme.toJS, securityLevel: 'loose'.toJS, + fontFamily: 'Google Sans Flex, sans-serif'.toJS, ), ); @@ -148,6 +149,7 @@ extension type MermaidConfig._(JSObject _) implements JSObject { JSBoolean? startOnLoad, JSString? theme, JSString? securityLevel, + JSString? fontFamily, }); } diff --git a/packages/site_shared/lib/src/markdown/mermaid_syntax.dart b/packages/site_shared/lib/src/markdown/mermaid_syntax.dart index b4c62535a5b..e476e33b059 100644 --- a/packages/site_shared/lib/src/markdown/mermaid_syntax.dart +++ b/packages/site_shared/lib/src/markdown/mermaid_syntax.dart @@ -1,5 +1,20 @@ import 'package:markdown/markdown.dart' as md; +/// A custom Markdown block syntax for diagrams authored +/// between ```mermaid code fences. +/// +/// Example: +/// +/// ````markdown +/// ```mermaid +/// flowchart TD +/// A --> B +/// ``` +/// ```` +/// +/// This renders as a `
` containing +/// a `
` element that hydrates on the client
+/// via [MermaidViewer].
 final class MermaidBlockSyntax extends md.BlockSyntax {
   const MermaidBlockSyntax();
 
diff --git a/sites/docs/lib/_sass/_site.scss b/sites/docs/lib/_sass/_site.scss
index 99092c6a676..8160f161328 100644
--- a/sites/docs/lib/_sass/_site.scss
+++ b/sites/docs/lib/_sass/_site.scss
@@ -38,6 +38,7 @@
 @use 'package:site_shared/_sass/components/cookie-notice';
 @use 'package:site_shared/_sass/components/dropdown';
 @use 'package:site_shared/_sass/components/menu-toggle';
+@use 'package:site_shared/_sass/components/mermaid';
 @use 'package:site_shared/_sass/components/progress-ring';
 @use 'package:site_shared/_sass/components/quiz';
 @use 'package:site_shared/_sass/components/site-switcher';
diff --git a/sites/docs/src/content/ai/evals.md b/sites/docs/src/content/ai/evals.md
index e4985cc52b3..bc6767f2778 100644
--- a/sites/docs/src/content/ai/evals.md
+++ b/sites/docs/src/content/ai/evals.md
@@ -28,3 +28,20 @@ Evals measure both deterministic code correctness
 (compilation, lints, automated tests) and qualitative performance
 (reasoning, safety, and conciseness) using automated model judges
 and expert human grading.
+
+```mermaid
+flowchart TB
+    c1:::myStyle-->a2
+    subgraph one
+    a1-->a2
+    end
+    subgraph two
+    b1-->b2
+    end
+    subgraph three
+    c1-->c2
+    end
+    one --> two
+    three --> two
+    two --> c2
+```
\ No newline at end of file
diff --git a/sites/docs/src/content/contribute/docs/markdown.md b/sites/docs/src/content/contribute/docs/markdown.md
index fa62002c26b..ca9ac2939b5 100644
--- a/sites/docs/src/content/contribute/docs/markdown.md
+++ b/sites/docs/src/content/contribute/docs/markdown.md
@@ -47,3 +47,62 @@ To learn more about customizing code blocks,
 check out the dedicated documentation on [Code blocks][].
 
 [Code blocks]: /contribute/docs/code-blocks
+
+
+## Mermaid diagrams
+
+To render flowcharts, sequence diagrams, and other charts within a Markdown file,
+use a fenced code block with the `mermaid` language identifier:
+
+```markdown
+```mermaid
+flowchart LR
+    A[Start] --> B(Process)
+    B --> C{Decision}
+    C -->|Yes| D[Done]
+    C -->|No| B
+```
+```
+
+### Styling Mermaid diagrams
+
+Mermaid diagrams automatically adapt to the site's light and dark themes
+and inherit the default `Google Sans Flex` typography.
+
+#### In-diagram styling (Recommended)
+
+When you need custom colors for specific nodes,
+define them directly in the diagram using Mermaid's built-in
+[`classDef` and `:::styleName` syntax][]:
+
+```mermaid
+flowchart LR
+    A:::highlightNode --> B
+    classDef highlightNode fill:#f96,stroke:#333,stroke-width:2px;
+```
+
+#### SCSS styling
+
+To adjust the global diagram layout, border, or background across the site,
+edit [`_mermaid.scss`][]:
+
+```scss
+.mermaid-container {
+  // Container styling (padding, margins, background, border)
+
+  // Fallback while loading or during SSR
+  pre.mermaid {
+    // Pre-loading styles
+  }
+
+  svg {
+    // Custom SVG element overrides (requires !important)
+    .highlightNode rect {
+      fill: var(--site-primary-color) !important;
+    }
+  }
+}
+```
+
+[`classDef` and `:::styleName` syntax]: https://mermaid.js.org/syntax/flowchart.html#styling-and-classes
+[`_mermaid.scss`]: https://github.com/flutter/website/blob/main/packages/site_shared/lib/_sass/components/_mermaid.scss
diff --git a/sites/www/lib/styles/styles.scss b/sites/www/lib/styles/styles.scss
index f68b8bceeb2..163c61871d3 100644
--- a/sites/www/lib/styles/styles.scss
+++ b/sites/www/lib/styles/styles.scss
@@ -51,3 +51,4 @@
 @use 'package:site_shared/_sass/components/blog';
 @use 'package:site_shared/_sass/components/code';
 @use 'package:site_shared/_sass/components/dropdown';
+@use 'package:site_shared/_sass/components/mermaid';

From 9942358c9cd0f46bb62735f713a7545af3edfca7 Mon Sep 17 00:00:00 2001
From: Eric Windmill 
Date: Wed, 19 Aug 2026 07:57:19 -0700
Subject: [PATCH 3/8] use Dart package

---
 .../common/client/mermaid_diagram.dart        | 105 +++++-------------
 packages/site_shared/pubspec.yaml             |   6 +
 sites/www/lib/main.client.options.dart        |   6 +
 sites/www/lib/main.server.options.dart        |  10 ++
 4 files changed, 51 insertions(+), 76 deletions(-)

diff --git a/packages/site_shared/lib/components/common/client/mermaid_diagram.dart b/packages/site_shared/lib/components/common/client/mermaid_diagram.dart
index da4c207f1ba..1cfbd496578 100644
--- a/packages/site_shared/lib/components/common/client/mermaid_diagram.dart
+++ b/packages/site_shared/lib/components/common/client/mermaid_diagram.dart
@@ -1,7 +1,6 @@
-import 'dart:async';
-
 import 'package:jaspr/dom.dart';
 import 'package:jaspr/jaspr.dart';
+import 'package:mermaid_core/mermaid_core.dart';
 import 'package:universal_web/js_interop.dart';
 import 'package:universal_web/web.dart' as web;
 
@@ -16,20 +15,29 @@ final class MermaidViewer extends StatefulComponent {
 }
 
 final class _MermaidViewerState extends State {
-  static int _idCounter = 0;
-  final String _diagramId = 'mermaid-diagram-${_idCounter++}';
-
   String? _svg;
   web.MutationObserver? _themeObserver;
 
   @override
   void initState() {
     super.initState();
+    final isDark =
+        kIsWeb && (web.document.body?.classList.contains('dark-mode') ?? false);
+    _svg = _renderDiagram(isDark: isDark);
+
     if (kIsWeb) {
-      context.binding.addPostFrameCallback(() {
-        unawaited(_renderDiagram());
-        _observeTheme();
-      });
+      _observeTheme();
+    }
+  }
+
+  @override
+  void didUpdateComponent(MermaidViewer oldComponent) {
+    super.didUpdateComponent(oldComponent);
+    if (oldComponent.diagram != component.diagram) {
+      final isDark =
+          kIsWeb &&
+          (web.document.body?.classList.contains('dark-mode') ?? false);
+      _svg = _renderDiagram(isDark: isDark);
     }
   }
 
@@ -49,7 +57,9 @@ final class _MermaidViewerState extends State {
         final newIsDark = body.classList.contains('dark-mode');
         if (newIsDark != isDark) {
           isDark = newIsDark;
-          unawaited(_renderDiagram());
+          setState(() {
+            _svg = _renderDiagram(isDark: isDark);
+          });
         }
       }).toJS,
     );
@@ -63,39 +73,21 @@ final class _MermaidViewerState extends State {
     );
   }
 
-  Future _renderDiagram() async {
+  String? _renderDiagram({required bool isDark}) {
     try {
-      final isDark =
-          web.document.body?.classList.contains('dark-mode') ?? false;
-      final theme = isDark ? 'dark' : 'default';
-
-      // Dynamically import Mermaid ESM from CDN only when a
-      // diagram component mounts
-      final module = await _importMermaid();
-      final mermaid = module.defaultExport;
-
-      mermaid.initialize(
-        MermaidConfig(
-          startOnLoad: false.toJS,
-          theme: theme.toJS,
-          securityLevel: 'loose'.toJS,
-          fontFamily: 'Google Sans Flex, sans-serif'.toJS,
-        ),
+      final theme =
+          isDark ? MermaidTheme.darkTheme : MermaidTheme.defaultTheme;
+      final mermaid = Mermaid(
+        measurer: const ApproximateTextMeasurer(),
+        theme: theme,
       );
-
-      final result = await mermaid
-          .render(_diagramId.toJS, component.diagram.toJS)
-          .toDart;
-
-      if (mounted) {
-        setState(() {
-          _svg = result.svg.toDart;
-        });
-      }
+      final scene = mermaid.render(component.diagram);
+      return renderSceneToSvg(scene);
     } catch (e) {
       if (kDebugMode) {
         print('Failed to render Mermaid diagram: $e');
       }
+      return null;
     }
   }
 
@@ -117,42 +109,3 @@ final class _MermaidViewerState extends State {
     );
   }
 }
-
-// -----------------------------------------------------------------------------
-// JS Interop Bindings (Dart 3 Extension Types)
-// -----------------------------------------------------------------------------
-
-@JS('eval')
-external JSAny? _eval(JSString code);
-
-Future _importMermaid() {
-  final importFn = _eval('(url) => import(url)'.toJS) as JSFunction;
-  final promise = importFn.callAsFunction(
-    null,
-    'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'.toJS,
-  ) as JSPromise;
-  return promise.toDart;
-}
-
-extension type MermaidModule._(JSObject _) implements JSObject {
-  @JS('default')
-  external MermaidApi get defaultExport;
-}
-
-extension type MermaidApi._(JSObject _) implements JSObject {
-  external void initialize(MermaidConfig config);
-  external JSPromise render(JSString id, JSString text);
-}
-
-extension type MermaidConfig._(JSObject _) implements JSObject {
-  external factory MermaidConfig({
-    JSBoolean? startOnLoad,
-    JSString? theme,
-    JSString? securityLevel,
-    JSString? fontFamily,
-  });
-}
-
-extension type MermaidRenderResult._(JSObject _) implements JSObject {
-  external JSString get svg;
-}
diff --git a/packages/site_shared/pubspec.yaml b/packages/site_shared/pubspec.yaml
index 4251c74efae..d0a85ca7fcd 100644
--- a/packages/site_shared/pubspec.yaml
+++ b/packages/site_shared/pubspec.yaml
@@ -16,6 +16,11 @@ dependencies:
   jaspr_content: ^0.5.3
   markdown: ^7.3.1
   markdown_description_list: ^0.2.0
+  mermaid_core:
+    git:
+      url: https://github.com/orestesgaolin/mermaid.git
+      ref: round3-fixes-and-packaging
+      path: packages/mermaid_core
   meta: ^1.18.2
   nanoid2: ^2.0.1
   opal: ^0.2.4
@@ -26,3 +31,4 @@ dependencies:
 
 dev_dependencies:
   analysis_defaults: any
+  test: ^1.31.1
diff --git a/sites/www/lib/main.client.options.dart b/sites/www/lib/main.client.options.dart
index 27a2638f576..0372da80744 100644
--- a/sites/www/lib/main.client.options.dart
+++ b/sites/www/lib/main.client.options.dart
@@ -34,6 +34,8 @@ import 'package:site_shared/components/common/client/collapse_button.dart'
     deferred as _collapse_button;
 import 'package:site_shared/components/common/client/copy_button.dart'
     deferred as _copy_button;
+import 'package:site_shared/components/common/client/mermaid_diagram.dart'
+    deferred as _mermaid_diagram;
 import 'package:site_shared/components/dartpad/dartpad_injector.dart'
     deferred as _dartpad_injector;
 import 'package:site_shared/components/utils/component_ref.dart'
@@ -181,6 +183,10 @@ ClientOptions get defaultClientOptions => ClientOptions(
       ),
       loader: _copy_button.loadLibrary,
     ),
+    'site_shared:mermaid_diagram': ClientLoader(
+      (p) => _mermaid_diagram.MermaidViewer(diagram: p['diagram'] as String),
+      loader: _mermaid_diagram.loadLibrary,
+    ),
     'site_shared:dartpad_injector': ClientLoader(
       (p) => _dartpad_injector.DartPadInjector(
         title: p['title'] as String,
diff --git a/sites/www/lib/main.server.options.dart b/sites/www/lib/main.server.options.dart
index 55b5d4f6e2d..84a754ecf76 100644
--- a/sites/www/lib/main.server.options.dart
+++ b/sites/www/lib/main.server.options.dart
@@ -30,6 +30,8 @@ import 'package:site_shared/components/common/client/collapse_button.dart'
     as _collapse_button;
 import 'package:site_shared/components/common/client/copy_button.dart'
     as _copy_button;
+import 'package:site_shared/components/common/client/mermaid_diagram.dart'
+    as _mermaid_diagram;
 import 'package:site_shared/components/dartpad/dartpad_injector.dart'
     as _dartpad_injector;
 
@@ -103,6 +105,11 @@ ServerOptions get defaultServerOptions => ServerOptions(
       'site_shared:copy_button',
       params: __copy_buttonCopyButton,
     ),
+    _mermaid_diagram.MermaidViewer:
+        ClientTarget<_mermaid_diagram.MermaidViewer>(
+          'site_shared:mermaid_diagram',
+          params: __mermaid_diagramMermaidViewer,
+        ),
     _dartpad_injector.DartPadInjector:
         ClientTarget<_dartpad_injector.DartPadInjector>(
           'site_shared:dartpad_injector',
@@ -159,6 +166,9 @@ Map __copy_buttonCopyButton(_copy_button.CopyButton c) => {
   'classes': c.classes,
   'title': c.title,
 };
+Map __mermaid_diagramMermaidViewer(
+  _mermaid_diagram.MermaidViewer c,
+) => {'diagram': c.diagram};
 Map __dartpad_injectorDartPadInjector(
   _dartpad_injector.DartPadInjector c,
 ) => {

From ecd987c2d090d49c71c639f458b492d506a242e3 Mon Sep 17 00:00:00 2001
From: Eric Windmill 
Date: Wed, 19 Aug 2026 08:19:31 -0700
Subject: [PATCH 4/8] checkin

---
 packages/site_shared/pubspec.yaml  |  1 -
 sites/docs/src/content/ai/evals.md | 22 ++++++++++++++++++++++
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/packages/site_shared/pubspec.yaml b/packages/site_shared/pubspec.yaml
index d0a85ca7fcd..a43473195f7 100644
--- a/packages/site_shared/pubspec.yaml
+++ b/packages/site_shared/pubspec.yaml
@@ -31,4 +31,3 @@ dependencies:
 
 dev_dependencies:
   analysis_defaults: any
-  test: ^1.31.1
diff --git a/sites/docs/src/content/ai/evals.md b/sites/docs/src/content/ai/evals.md
index bc6767f2778..e8b56a5e769 100644
--- a/sites/docs/src/content/ai/evals.md
+++ b/sites/docs/src/content/ai/evals.md
@@ -44,4 +44,26 @@ flowchart TB
     one --> two
     three --> two
     two --> c2
+```
+
+More test
+
+```mermaid
+flowchart LR
+  subgraph tasks
+    direction TB
+    t1(Do first thing) --> t2(do second thing)
+  end 
+  User["`As an **App developer**`"] -->|i want to| Goal[fix overflow errors]
+  Goal-->tasks
+```
+
+
+```mermaid
+graph TD
+    A[Start] --> B{Is it working?}
+    B -->|Yes| C[Ship it]
+    B -->|No| D[Debug]
+    D --> B
+    C --> E[Celebrate]
 ```
\ No newline at end of file

From 038621faae12a4a725c97157252a16ee7eba646d Mon Sep 17 00:00:00 2001
From: Eric Windmill 
Date: Wed, 26 Aug 2026 11:21:58 -0700
Subject: [PATCH 5/8] address minor pr comments

---
 .../common/client/mermaid_diagram.dart         |  7 +++++--
 packages/site_shared/lib/markdown.dart         |  1 +
 .../lib/src/extensions/mermaid_processor.dart  |  4 ++++
 .../lib/src/markdown/mermaid_syntax.dart       |  4 ++++
 packages/site_shared/pubspec.yaml              |  2 +-
 sites/docs/lib/src/style_hash.dart             |  5 -----
 .../src/content/contribute/docs/markdown.md    |  6 +++---
 sites/www/lib/main.server.dart                 | 18 +++++++++++++++++-
 sites/www/pubspec.yaml                         |  1 +
 9 files changed, 36 insertions(+), 12 deletions(-)
 delete mode 100644 sites/docs/lib/src/style_hash.dart

diff --git a/packages/site_shared/lib/components/common/client/mermaid_diagram.dart b/packages/site_shared/lib/components/common/client/mermaid_diagram.dart
index 1cfbd496578..399e9680cac 100644
--- a/packages/site_shared/lib/components/common/client/mermaid_diagram.dart
+++ b/packages/site_shared/lib/components/common/client/mermaid_diagram.dart
@@ -1,3 +1,7 @@
+// Copyright 2026 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
 import 'package:jaspr/dom.dart';
 import 'package:jaspr/jaspr.dart';
 import 'package:mermaid_core/mermaid_core.dart';
@@ -75,8 +79,7 @@ final class _MermaidViewerState extends State {
 
   String? _renderDiagram({required bool isDark}) {
     try {
-      final theme =
-          isDark ? MermaidTheme.darkTheme : MermaidTheme.defaultTheme;
+      final theme = isDark ? MermaidTheme.darkTheme : MermaidTheme.defaultTheme;
       final mermaid = Mermaid(
         measurer: const ApproximateTextMeasurer(),
         theme: theme,
diff --git a/packages/site_shared/lib/markdown.dart b/packages/site_shared/lib/markdown.dart
index 23917e4faa7..28f3f7d22af 100644
--- a/packages/site_shared/lib/markdown.dart
+++ b/packages/site_shared/lib/markdown.dart
@@ -3,3 +3,4 @@
 // found in the LICENSE file.
 
 export 'src/markdown/markdown_parser.dart';
+export 'src/markdown/mermaid_syntax.dart';
diff --git a/packages/site_shared/lib/src/extensions/mermaid_processor.dart b/packages/site_shared/lib/src/extensions/mermaid_processor.dart
index b5c721037e0..6b507b9cba5 100644
--- a/packages/site_shared/lib/src/extensions/mermaid_processor.dart
+++ b/packages/site_shared/lib/src/extensions/mermaid_processor.dart
@@ -1,3 +1,7 @@
+// Copyright 2026 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
 import 'package:jaspr_content/jaspr_content.dart';
 
 import '../../components/common/client/mermaid_diagram.dart';
diff --git a/packages/site_shared/lib/src/markdown/mermaid_syntax.dart b/packages/site_shared/lib/src/markdown/mermaid_syntax.dart
index e476e33b059..3e014c97025 100644
--- a/packages/site_shared/lib/src/markdown/mermaid_syntax.dart
+++ b/packages/site_shared/lib/src/markdown/mermaid_syntax.dart
@@ -1,3 +1,7 @@
+// Copyright 2026 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
 import 'package:markdown/markdown.dart' as md;
 
 /// A custom Markdown block syntax for diagrams authored
diff --git a/packages/site_shared/pubspec.yaml b/packages/site_shared/pubspec.yaml
index a43473195f7..e80da1d3a26 100644
--- a/packages/site_shared/pubspec.yaml
+++ b/packages/site_shared/pubspec.yaml
@@ -19,7 +19,7 @@ dependencies:
   mermaid_core:
     git:
       url: https://github.com/orestesgaolin/mermaid.git
-      ref: round3-fixes-and-packaging
+      ref: 03759ce31539f87a1487e84d132d3ad1e8efa3cd
       path: packages/mermaid_core
   meta: ^1.18.2
   nanoid2: ^2.0.1
diff --git a/sites/docs/lib/src/style_hash.dart b/sites/docs/lib/src/style_hash.dart
deleted file mode 100644
index bd8e030c02b..00000000000
--- a/sites/docs/lib/src/style_hash.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-// Generated by site_shared|stylesHashBuilder. Do not edit.
-// dart format off
-
-/// The generated hash of the `main.css` file.
-const generatedStylesHash = '';
diff --git a/sites/docs/src/content/contribute/docs/markdown.md b/sites/docs/src/content/contribute/docs/markdown.md
index ca9ac2939b5..4aa20fe4030 100644
--- a/sites/docs/src/content/contribute/docs/markdown.md
+++ b/sites/docs/src/content/contribute/docs/markdown.md
@@ -54,7 +54,7 @@ check out the dedicated documentation on [Code blocks][].
 To render flowcharts, sequence diagrams, and other charts within a Markdown file,
 use a fenced code block with the `mermaid` language identifier:
 
-```markdown
+````markdown
 ```mermaid
 flowchart LR
     A[Start] --> B(Process)
@@ -62,9 +62,9 @@ flowchart LR
     C -->|Yes| D[Done]
     C -->|No| B
 ```
-```
+````
 
-### Styling Mermaid diagrams
+### Style Mermaid diagrams
 
 Mermaid diagrams automatically adapt to the site's light and dark themes
 and inherit the default `Google Sans Flex` typography.
diff --git a/sites/www/lib/main.server.dart b/sites/www/lib/main.server.dart
index e82c0b50a33..7ff40b2b175 100644
--- a/sites/www/lib/main.server.dart
+++ b/sites/www/lib/main.server.dart
@@ -6,10 +6,12 @@ import 'package:jaspr/server.dart';
 import 'package:jaspr_content/components/file_tree.dart';
 import 'package:jaspr_content/jaspr_content.dart' hide BlogLayout;
 import 'package:jaspr_content/theme.dart';
+import 'package:markdown/markdown.dart' as md;
 import 'package:site_shared/blog.dart';
 import 'package:site_shared/components/blog/blog_index.dart';
 import 'package:site_shared/components/common/youtube_embed.dart';
 import 'package:site_shared/components/utils/define_component.dart';
+import 'package:site_shared/markdown.dart';
 import 'package:site_shared/page_extensions.dart';
 
 import 'main.server.options.dart';
@@ -88,10 +90,13 @@ void main() async {
           const BlogPostDataProcessor(),
           assetManager.dataLoader,
         ],
-        parsers: [const MarkdownParser()],
+        parsers: [
+          const MarkdownParser(documentBuilder: _buildMarkdownDocument),
+        ],
         extensions: [
           ShowcaseStoryExtension(),
           const TableWrapperExtension(),
+          const MermaidProcessor(),
           const CodeBlockProcessor(defaultTitle: 'Runnable Flutter example'),
           assetManager.pageExtension,
         ],
@@ -141,3 +146,14 @@ void main() async {
     ),
   );
 }
+
+/// Builds the `package:markdown` document used to parse this site's content,
+/// adding [MermaidBlockSyntax] on top of the parser's default block syntaxes
+/// so `MermaidProcessor` has diagrams to transform.
+md.Document _buildMarkdownDocument(Page page) => md.Document(
+  blockSyntaxes: [
+    ...MarkdownParser.defaultBlockSyntaxes,
+    const MermaidBlockSyntax(),
+  ],
+  extensionSet: md.ExtensionSet.gitHubWeb,
+);
diff --git a/sites/www/pubspec.yaml b/sites/www/pubspec.yaml
index db08acfe9a6..5372b6bcd81 100644
--- a/sites/www/pubspec.yaml
+++ b/sites/www/pubspec.yaml
@@ -19,6 +19,7 @@ dependencies:
   intl: ^0.20.2
   jaspr: ^0.23.2
   jaspr_content: ^0.5.3
+  markdown: ^7.3.1
   path: ^1.9.1
   site_shared:
     path: ../../packages/site_shared

From 4fb208c8798900d822f6932592eb5a7d1377f834 Mon Sep 17 00:00:00 2001
From: Eric Windmill 
Date: Wed, 26 Aug 2026 11:31:16 -0700
Subject: [PATCH 6/8] remove unneeded file

---
 .../lib/_sass/components/_mermaid.scss        |  18 ++-
 .../common/client/mermaid_diagram.dart        | 114 ------------------
 .../components/common/mermaid_diagram.dart    |  64 ++++++++++
 .../lib/src/extensions/mermaid_processor.dart |   4 +-
 .../lib/src/markdown/mermaid_syntax.dart      |   4 +-
 sites/docs/lib/main.client.options.dart       |   6 -
 sites/docs/lib/main.server.options.dart       |  10 --
 sites/docs/lib/src/style_hash.dart            |   5 +
 .../src/content/contribute/docs/markdown.md   |  10 +-
 sites/www/lib/main.client.options.dart        |   6 -
 sites/www/lib/main.server.options.dart        |  10 --
 11 files changed, 96 insertions(+), 155 deletions(-)
 delete mode 100644 packages/site_shared/lib/components/common/client/mermaid_diagram.dart
 create mode 100644 packages/site_shared/lib/components/common/mermaid_diagram.dart
 create mode 100644 sites/docs/lib/src/style_hash.dart

diff --git a/packages/site_shared/lib/_sass/components/_mermaid.scss b/packages/site_shared/lib/_sass/components/_mermaid.scss
index eebefd8372a..680d3974c0e 100644
--- a/packages/site_shared/lib/_sass/components/_mermaid.scss
+++ b/packages/site_shared/lib/_sass/components/_mermaid.scss
@@ -12,7 +12,7 @@
   border: 1px solid var(--site-outline-variant);
   border-radius: var(--site-radius);
 
-  // Fallback while loading or during SSR
+  // Fallback shown only if server rendering the diagram failed
   pre.mermaid {
     margin: 0;
     padding: 0;
@@ -21,6 +21,22 @@
     font-family: var(--site-code-fontFamily);
   }
 
+  // Both theme variants are rendered on the server; show only the one
+  // matching the site's current theme.
+  .mermaid-theme-dark {
+    display: none;
+  }
+
+  @at-root body.dark-mode & {
+    .mermaid-theme-light {
+      display: none;
+    }
+
+    .mermaid-theme-dark {
+      display: block;
+    }
+  }
+
   // Rendered SVG styling
   // You must use !important to override styles for specific elements within 
   // the rendered SVG. For styling individual graphs, use Mermaid's built-in
diff --git a/packages/site_shared/lib/components/common/client/mermaid_diagram.dart b/packages/site_shared/lib/components/common/client/mermaid_diagram.dart
deleted file mode 100644
index 399e9680cac..00000000000
--- a/packages/site_shared/lib/components/common/client/mermaid_diagram.dart
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2026 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:jaspr/dom.dart';
-import 'package:jaspr/jaspr.dart';
-import 'package:mermaid_core/mermaid_core.dart';
-import 'package:universal_web/js_interop.dart';
-import 'package:universal_web/web.dart' as web;
-
-@client
-final class MermaidViewer extends StatefulComponent {
-  const MermaidViewer({required this.diagram, super.key});
-
-  final String diagram;
-
-  @override
-  State createState() => _MermaidViewerState();
-}
-
-final class _MermaidViewerState extends State {
-  String? _svg;
-  web.MutationObserver? _themeObserver;
-
-  @override
-  void initState() {
-    super.initState();
-    final isDark =
-        kIsWeb && (web.document.body?.classList.contains('dark-mode') ?? false);
-    _svg = _renderDiagram(isDark: isDark);
-
-    if (kIsWeb) {
-      _observeTheme();
-    }
-  }
-
-  @override
-  void didUpdateComponent(MermaidViewer oldComponent) {
-    super.didUpdateComponent(oldComponent);
-    if (oldComponent.diagram != component.diagram) {
-      final isDark =
-          kIsWeb &&
-          (web.document.body?.classList.contains('dark-mode') ?? false);
-      _svg = _renderDiagram(isDark: isDark);
-    }
-  }
-
-  @override
-  void dispose() {
-    _themeObserver?.disconnect();
-    super.dispose();
-  }
-
-  void _observeTheme() {
-    final body = web.document.body;
-    if (body == null) return;
-
-    var isDark = body.classList.contains('dark-mode');
-    _themeObserver = web.MutationObserver(
-      ((JSArray _, web.MutationObserver _) {
-        final newIsDark = body.classList.contains('dark-mode');
-        if (newIsDark != isDark) {
-          isDark = newIsDark;
-          setState(() {
-            _svg = _renderDiagram(isDark: isDark);
-          });
-        }
-      }).toJS,
-    );
-
-    _themeObserver?.observe(
-      body,
-      web.MutationObserverInit(
-        attributes: true,
-        attributeFilter: ['class'.toJS].toJS,
-      ),
-    );
-  }
-
-  String? _renderDiagram({required bool isDark}) {
-    try {
-      final theme = isDark ? MermaidTheme.darkTheme : MermaidTheme.defaultTheme;
-      final mermaid = Mermaid(
-        measurer: const ApproximateTextMeasurer(),
-        theme: theme,
-      );
-      final scene = mermaid.render(component.diagram);
-      return renderSceneToSvg(scene);
-    } catch (e) {
-      if (kDebugMode) {
-        print('Failed to render Mermaid diagram: $e');
-      }
-      return null;
-    }
-  }
-
-  @override
-  Component build(BuildContext context) {
-    return div(
-      classes: 'mermaid-container',
-      [
-        if (_svg case final svg?)
-          RawText(svg)
-        else
-          // Fallback during SSR or while loading
-          pre(
-            classes: 'mermaid',
-            attributes: {'data-source': component.diagram},
-            [.text(component.diagram)],
-          ),
-      ],
-    );
-  }
-}
diff --git a/packages/site_shared/lib/components/common/mermaid_diagram.dart b/packages/site_shared/lib/components/common/mermaid_diagram.dart
new file mode 100644
index 00000000000..e8a0db3bb7e
--- /dev/null
+++ b/packages/site_shared/lib/components/common/mermaid_diagram.dart
@@ -0,0 +1,64 @@
+// Copyright 2026 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:jaspr/dom.dart';
+import 'package:jaspr/jaspr.dart';
+import 'package:mermaid_core/mermaid_core.dart';
+
+/// Renders a Mermaid diagram as server-generated SVG.
+///
+/// Both light and dark variants are rendered at build time and included in
+/// the page, so CSS can show the variant matching the site's current theme
+/// without shipping a Mermaid renderer to the client.
+final class MermaidDiagram extends StatelessComponent {
+  const MermaidDiagram({required this.diagram, super.key});
+
+  /// The Mermaid diagram definition to render.
+  final String diagram;
+
+  @override
+  Component build(BuildContext context) {
+    final lightSvg = _renderDiagram(theme: MermaidTheme.defaultTheme);
+    final darkSvg = _renderDiagram(theme: MermaidTheme.darkTheme);
+
+    return div(
+      classes: 'mermaid-container',
+      [
+        if (lightSvg != null && darkSvg != null) ...[
+          div(classes: 'mermaid-theme mermaid-theme-light', [
+            RawText(lightSvg),
+          ]),
+          div(classes: 'mermaid-theme mermaid-theme-dark', [
+            RawText(darkSvg),
+          ]),
+        ] else
+          // If rendering fails, preserve the source in a fallback element.
+          pre(
+            classes: 'mermaid',
+            attributes: {'data-source': diagram},
+            [.text(diagram)],
+          ),
+      ],
+    );
+  }
+
+  /// Renders [diagram] as SVG using the specified [theme].
+  String? _renderDiagram({required MermaidTheme theme}) {
+    try {
+      // Server rendering doesn't have browser text metrics available, so use
+      // the library's deterministic approximation when laying out labels.
+      final mermaid = Mermaid(
+        measurer: const ApproximateTextMeasurer(),
+        theme: theme,
+      );
+      final scene = mermaid.render(diagram);
+      return renderSceneToSvg(scene);
+    } catch (error) {
+      if (kDebugMode) {
+        print('Failed to render Mermaid diagram: $error');
+      }
+      return null;
+    }
+  }
+}
diff --git a/packages/site_shared/lib/src/extensions/mermaid_processor.dart b/packages/site_shared/lib/src/extensions/mermaid_processor.dart
index 6b507b9cba5..9cb9dc904c1 100644
--- a/packages/site_shared/lib/src/extensions/mermaid_processor.dart
+++ b/packages/site_shared/lib/src/extensions/mermaid_processor.dart
@@ -4,7 +4,7 @@
 
 import 'package:jaspr_content/jaspr_content.dart';
 
-import '../../components/common/client/mermaid_diagram.dart';
+import '../../components/common/mermaid_diagram.dart';
 
 final class MermaidProcessor implements PageExtension {
   const MermaidProcessor();
@@ -24,7 +24,7 @@ final class MermaidProcessor implements PageExtension {
             ...,
           ],
         ))
-          ComponentNode(MermaidViewer(diagram: diagram))
+          ComponentNode(MermaidDiagram(diagram: diagram))
         else if (node is ElementNode)
           ElementNode(
             node.tag,
diff --git a/packages/site_shared/lib/src/markdown/mermaid_syntax.dart b/packages/site_shared/lib/src/markdown/mermaid_syntax.dart
index 3e014c97025..cb026a02a2c 100644
--- a/packages/site_shared/lib/src/markdown/mermaid_syntax.dart
+++ b/packages/site_shared/lib/src/markdown/mermaid_syntax.dart
@@ -17,8 +17,8 @@ import 'package:markdown/markdown.dart' as md;
 /// ````
 ///
 /// This renders as a `
` containing -/// a `
` element that hydrates on the client
-/// via [MermaidViewer].
+/// a `
` element with the diagram source, which
+/// `MermaidProcessor` replaces with a server-rendered `MermaidDiagram`.
 final class MermaidBlockSyntax extends md.BlockSyntax {
   const MermaidBlockSyntax();
 
diff --git a/sites/docs/lib/main.client.options.dart b/sites/docs/lib/main.client.options.dart
index ac296cdcf47..23c1b571e81 100644
--- a/sites/docs/lib/main.client.options.dart
+++ b/sites/docs/lib/main.client.options.dart
@@ -30,8 +30,6 @@ import 'package:site_shared/components/common/client/download_button.dart'
     deferred as _download_button;
 import 'package:site_shared/components/common/client/feedback.dart'
     deferred as _feedback;
-import 'package:site_shared/components/common/client/mermaid_diagram.dart'
-    deferred as _mermaid_diagram;
 import 'package:site_shared/components/common/client/on_this_page_button.dart'
     deferred as _on_this_page_button;
 import 'package:site_shared/components/common/client/page_header_options.dart'
@@ -142,10 +140,6 @@ ClientOptions get defaultClientOptions => ClientOptions(
       (p) => _feedback.FeedbackComponent(issueUrl: p['issueUrl'] as String),
       loader: _feedback.loadLibrary,
     ),
-    'site_shared:mermaid_diagram': ClientLoader(
-      (p) => _mermaid_diagram.MermaidViewer(diagram: p['diagram'] as String),
-      loader: _mermaid_diagram.loadLibrary,
-    ),
     'site_shared:on_this_page_button': ClientLoader(
       (p) => _on_this_page_button.OnThisPageButton(),
       loader: _on_this_page_button.loadLibrary,
diff --git a/sites/docs/lib/main.server.options.dart b/sites/docs/lib/main.server.options.dart
index 03bb51110df..fcbd5e0a090 100644
--- a/sites/docs/lib/main.server.options.dart
+++ b/sites/docs/lib/main.server.options.dart
@@ -30,8 +30,6 @@ import 'package:site_shared/components/common/client/download_button.dart'
     as _download_button;
 import 'package:site_shared/components/common/client/feedback.dart'
     as _feedback;
-import 'package:site_shared/components/common/client/mermaid_diagram.dart'
-    as _mermaid_diagram;
 import 'package:site_shared/components/common/client/on_this_page_button.dart'
     as _on_this_page_button;
 import 'package:site_shared/components/common/client/page_header_options.dart'
@@ -116,11 +114,6 @@ ServerOptions get defaultServerOptions => ServerOptions(
       'site_shared:feedback',
       params: __feedbackFeedbackComponent,
     ),
-    _mermaid_diagram.MermaidViewer:
-        ClientTarget<_mermaid_diagram.MermaidViewer>(
-          'site_shared:mermaid_diagram',
-          params: __mermaid_diagramMermaidViewer,
-        ),
     _on_this_page_button.OnThisPageButton:
         ClientTarget<_on_this_page_button.OnThisPageButton>(
           'site_shared:on_this_page_button',
@@ -187,9 +180,6 @@ Map __download_buttonDownloadButton(
 Map __feedbackFeedbackComponent(
   _feedback.FeedbackComponent c,
 ) => {'issueUrl': c.issueUrl};
-Map __mermaid_diagramMermaidViewer(
-  _mermaid_diagram.MermaidViewer c,
-) => {'diagram': c.diagram};
 Map __page_header_optionsPageHeaderOptions(
   _page_header_options.PageHeaderOptions c,
 ) => {
diff --git a/sites/docs/lib/src/style_hash.dart b/sites/docs/lib/src/style_hash.dart
new file mode 100644
index 00000000000..bd8e030c02b
--- /dev/null
+++ b/sites/docs/lib/src/style_hash.dart
@@ -0,0 +1,5 @@
+// Generated by site_shared|stylesHashBuilder. Do not edit.
+// dart format off
+
+/// The generated hash of the `main.css` file.
+const generatedStylesHash = '';
diff --git a/sites/docs/src/content/contribute/docs/markdown.md b/sites/docs/src/content/contribute/docs/markdown.md
index 4aa20fe4030..6db7ce3ffe4 100644
--- a/sites/docs/src/content/contribute/docs/markdown.md
+++ b/sites/docs/src/content/contribute/docs/markdown.md
@@ -66,8 +66,10 @@ flowchart LR
 
 ### Style Mermaid diagrams
 
-Mermaid diagrams automatically adapt to the site's light and dark themes
-and inherit the default `Google Sans Flex` typography.
+Mermaid diagrams are rendered to SVG on the server, once for each theme.
+Both variants ship in the page and CSS shows the one matching the site's
+current light or dark theme. Diagrams inherit the default
+`Google Sans Flex` typography.
 
 #### In-diagram styling (Recommended)
 
@@ -90,9 +92,9 @@ edit [`_mermaid.scss`][]:
 .mermaid-container {
   // Container styling (padding, margins, background, border)
 
-  // Fallback while loading or during SSR
+  // Fallback shown only if server rendering the diagram failed
   pre.mermaid {
-    // Pre-loading styles
+    // Fallback styles
   }
 
   svg {
diff --git a/sites/www/lib/main.client.options.dart b/sites/www/lib/main.client.options.dart
index 0372da80744..27a2638f576 100644
--- a/sites/www/lib/main.client.options.dart
+++ b/sites/www/lib/main.client.options.dart
@@ -34,8 +34,6 @@ import 'package:site_shared/components/common/client/collapse_button.dart'
     deferred as _collapse_button;
 import 'package:site_shared/components/common/client/copy_button.dart'
     deferred as _copy_button;
-import 'package:site_shared/components/common/client/mermaid_diagram.dart'
-    deferred as _mermaid_diagram;
 import 'package:site_shared/components/dartpad/dartpad_injector.dart'
     deferred as _dartpad_injector;
 import 'package:site_shared/components/utils/component_ref.dart'
@@ -183,10 +181,6 @@ ClientOptions get defaultClientOptions => ClientOptions(
       ),
       loader: _copy_button.loadLibrary,
     ),
-    'site_shared:mermaid_diagram': ClientLoader(
-      (p) => _mermaid_diagram.MermaidViewer(diagram: p['diagram'] as String),
-      loader: _mermaid_diagram.loadLibrary,
-    ),
     'site_shared:dartpad_injector': ClientLoader(
       (p) => _dartpad_injector.DartPadInjector(
         title: p['title'] as String,
diff --git a/sites/www/lib/main.server.options.dart b/sites/www/lib/main.server.options.dart
index 84a754ecf76..55b5d4f6e2d 100644
--- a/sites/www/lib/main.server.options.dart
+++ b/sites/www/lib/main.server.options.dart
@@ -30,8 +30,6 @@ import 'package:site_shared/components/common/client/collapse_button.dart'
     as _collapse_button;
 import 'package:site_shared/components/common/client/copy_button.dart'
     as _copy_button;
-import 'package:site_shared/components/common/client/mermaid_diagram.dart'
-    as _mermaid_diagram;
 import 'package:site_shared/components/dartpad/dartpad_injector.dart'
     as _dartpad_injector;
 
@@ -105,11 +103,6 @@ ServerOptions get defaultServerOptions => ServerOptions(
       'site_shared:copy_button',
       params: __copy_buttonCopyButton,
     ),
-    _mermaid_diagram.MermaidViewer:
-        ClientTarget<_mermaid_diagram.MermaidViewer>(
-          'site_shared:mermaid_diagram',
-          params: __mermaid_diagramMermaidViewer,
-        ),
     _dartpad_injector.DartPadInjector:
         ClientTarget<_dartpad_injector.DartPadInjector>(
           'site_shared:dartpad_injector',
@@ -166,9 +159,6 @@ Map __copy_buttonCopyButton(_copy_button.CopyButton c) => {
   'classes': c.classes,
   'title': c.title,
 };
-Map __mermaid_diagramMermaidViewer(
-  _mermaid_diagram.MermaidViewer c,
-) => {'diagram': c.diagram};
 Map __dartpad_injectorDartPadInjector(
   _dartpad_injector.DartPadInjector c,
 ) => {

From 48fb363446fabd1ce4f8b4b298ce2b59118f570c Mon Sep 17 00:00:00 2001
From: Eric Windmill 
Date: Thu, 27 Aug 2026 11:20:32 -0700
Subject: [PATCH 7/8] remove unneeded scss

---
 .../lib/_sass/components/_mermaid.scss        | 23 -------------------
 .../src/content/contribute/docs/markdown.md   |  9 +++++---
 2 files changed, 6 insertions(+), 26 deletions(-)

diff --git a/packages/site_shared/lib/_sass/components/_mermaid.scss b/packages/site_shared/lib/_sass/components/_mermaid.scss
index 680d3974c0e..3968a3218c5 100644
--- a/packages/site_shared/lib/_sass/components/_mermaid.scss
+++ b/packages/site_shared/lib/_sass/components/_mermaid.scss
@@ -44,28 +44,5 @@
   svg {
     max-width: 100%;
     height: auto;
-
-    // Use Google Sans Flex for diagram labels
-    text,
-    .label,
-    .nodeLabel,
-    .edgeLabel {
-      font-family: var(--site-ui-fontFamily), sans-serif !important;
-    }
-
-    // Sharper node outlines matching site borders
-    .node rect,
-    .node circle,
-    .node ellipse,
-    .node polygon,
-    .node path {
-      stroke-width: 1.5px;
-      fill:  var(--site-secondaryContainer-bgColor) !important;
-    }
-
-    // Edge lines & arrows
-    .edgePath .path {
-      stroke-width: 1.5px;
-    }
   }
 }
diff --git a/sites/docs/src/content/contribute/docs/markdown.md b/sites/docs/src/content/contribute/docs/markdown.md
index 6db7ce3ffe4..6864a022a99 100644
--- a/sites/docs/src/content/contribute/docs/markdown.md
+++ b/sites/docs/src/content/contribute/docs/markdown.md
@@ -96,12 +96,15 @@ edit [`_mermaid.scss`][]:
   pre.mermaid {
     // Fallback styles
   }
+  
+  .mermaid-theme {}
+
+  .mermaid-theme-light {}
+
+  .mermaid-theme-dark {}
 
   svg {
     // Custom SVG element overrides (requires !important)
-    .highlightNode rect {
-      fill: var(--site-primary-color) !important;
-    }
   }
 }
 ```

From bbddf766e2a29109c216dee10ea95d38acb31b01 Mon Sep 17 00:00:00 2001
From: Eric Windmill 
Date: Thu, 27 Aug 2026 11:21:10 -0700
Subject: [PATCH 8/8] remove unneeded tests

---
 sites/docs/src/content/ai/evals.md | 39 ------------------------------
 1 file changed, 39 deletions(-)

diff --git a/sites/docs/src/content/ai/evals.md b/sites/docs/src/content/ai/evals.md
index 50654d2fa21..b1d6b8b2d17 100644
--- a/sites/docs/src/content/ai/evals.md
+++ b/sites/docs/src/content/ai/evals.md
@@ -27,42 +27,3 @@ Evals measure both deterministic code correctness
 (compilation, lints, automated tests) and qualitative performance
 (reasoning, safety, and conciseness) using automated model judges
 and expert human grading.
-
-```mermaid
-flowchart TB
-    c1:::myStyle-->a2
-    subgraph one
-    a1-->a2
-    end
-    subgraph two
-    b1-->b2
-    end
-    subgraph three
-    c1-->c2
-    end
-    one --> two
-    three --> two
-    two --> c2
-```
-
-More test
-
-```mermaid
-flowchart LR
-  subgraph tasks
-    direction TB
-    t1(Do first thing) --> t2(do second thing)
-  end 
-  User["`As an **App developer**`"] -->|i want to| Goal[fix overflow errors]
-  Goal-->tasks
-```
-
-
-```mermaid
-graph TD
-    A[Start] --> B{Is it working?}
-    B -->|Yes| C[Ship it]
-    B -->|No| D[Debug]
-    D --> B
-    C --> E[Celebrate]
-```
\ No newline at end of file