Mermaid Scoped Theming Implementation #
Date: 2025-10-08 #
Problem Statement #
All 6 Mermaid diagrams in content/archive/JavaBasic/_index.md repeated the same classDef declarations:
classDef site stroke: none, fill: none; classDef section fill: none; classDef group stroke: none, fill: none;
This caused:
- Code duplication: 3 lines × 6 diagrams = 18 redundant lines
- Maintenance burden: Changing styling requires editing 6 locations
- Inconsistency risk: Easy to update some diagrams but not others
Solution: 3-Tier Scoped Theming Architecture #
Mermaid’s Configuration Hierarchy #
Mermaid supports configuration at three levels:
Site-level (mermaid.initialize)
↓ overrides
Page-level (%%{init}%% directive)
↓ overrides
Diagram-level (classDef in diagram)
Key Insights from Mermaid Documentation:
mermaid.initialize()is called once and applies globally- Per-diagram config via
%%{init: {...}}%%directive overrides site config themeCSSin config applies custom CSS to all diagramsclassDefin diagram source is most granular control
Hugo Integration: 3-Tier Cascade #
hugo.yaml (Site defaults)
↓ inherited
Front Matter (Page/Section overrides)
↓ inherited
%%{init}%% (Diagram overrides)
Implementation #
Tier 1: Site-Level Defaults #
File: hugo.yaml
params:
mermaid:
theme: neutral
# Optional site-wide themeCSS or themeVariables
Tier 2: Page-Level Configuration #
File: layouts/_default/baseof.html
<!-- Load Mermaid, if necessary. -->
{{ if .Page.Store.Get "hasMermaid" }}
{{- $mermaidJS := resources.Get "lib/mermaid/mermaid.min.js" | fingerprint -}}
<script defer src="{{ $mermaidJS.RelPermalink }}" integrity="{{ $mermaidJS.Data.Integrity }}"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
// Site-level defaults
const config = {
startOnLoad: true,
theme: {{ .Site.Params.mermaid.theme | default "neutral" | jsonify }},
{{ with .Site.Params.mermaid.themeVariables }}
themeVariables: {{ . | jsonify }},
{{ end }}
{{ with .Site.Params.mermaid.themeCSS }}
themeCSS: {{ . | jsonify }}
{{ end }}
};
// Page-level overrides
{{ with .Params.mermaid }}
const pageConfig = {{ . | jsonify }};
Object.assign(config, pageConfig);
{{ end }}
mermaid.initialize(config);
});
</script>
{{ end }}
How it works:
- Site defaults from
hugo.yamlvia.Site.Params.mermaid - Page overrides from front matter via
.Params.mermaid Object.assign()merges page config over site configjsonifyconverts Hugo data to JavaScript safely
Tier 3: Diagram-Level Overrides #
Usage in Markdown (optional, for exceptional cases):
\`\`\`mermaid
%%{init: {'theme':'dark'}}%%
flowchart LR
A --> B
\`\`\`
Applied Solution for JavaBasic #
Before: Repeated classDef #
Each diagram had:
flowchart TB
classDef site stroke: none, fill: none;
classDef section fill: none;
classDef group stroke: none, fill: none;
node1(Example)
class node1 siteAfter: Page-Level Configuration #
File: content/archive/JavaBasic/_index.md
---
title: Java Basic
type: book
layout: all
weight: 200
mermaid:
themeCSS: |
.site { stroke: none; fill: none; }
.section { fill: none; }
.group { stroke: none; fill: none; }
---
Diagrams simplified to:
flowchart TB
node1(Example)
class node1 site # Still need class assignmentResult:
- ✅ Removed 18 redundant lines (3 × 6 diagrams)
- ✅ Centralized styling in front matter
- ✅ Easier maintenance (change once, applies to all 6 diagrams)
- ✅ Keep
classassignments (required to apply styles)
Benefits of This Architecture #
1. Separation of Concerns #
| Level | Purpose | Location |
|---|---|---|
| Site | Default theme for all pages | hugo.yaml |
| Page/Section | Page-specific styles | Front matter |
| Diagram | Exceptional overrides | Inline %%{init}%% |
2. DRY Principle #
- No repetition across diagrams on same page
- Page-level
themeCSSapplies automatically - Site-level config inherited by all pages
3. Flexibility #
- Override at any level as needed
- Explicit cascade: site → page → diagram
- Can mix approaches (some pages use site defaults, others customize)
4. Maintainability #
- Change site theme in one place (
hugo.yaml) - Page-specific needs in front matter
- Diagram exceptions inline (rare)
Scoping Rules Summary #
When to use each level: #
Site-level (hugo.yaml):
- Default theme for entire site
- Base themeVariables
- Common global styles
Page-level (Front matter):
- Page or section-specific styling
- Multiple diagrams sharing same styles
- Current use case: All 6 diagrams in JavaBasic/_index.md
Diagram-level (%%{init}%%):
- One diagram needs different theme
- Exceptional styling requirements
- Temporary overrides for testing
Technical Details #
How themeCSS Works #
Mermaid’s themeCSS option injects custom CSS into diagram rendering:
mermaid.initialize({
themeCSS: `
.site { stroke: none; fill: none; }
.section { fill: none; }
`
});
This CSS applies to all diagrams on the page after class assignment:
flowchart TB
node1(Example)
class node1 site # Applies themeCSS .site stylesHugo Template Functions Used #
.Site.Params.mermaid: Access site-level config.Params.mermaid: Access page-level config| default "neutral": Fallback if not specified| jsonify: Convert to valid JSON/JavaScriptwith: Conditional template rendering (only if value exists)Object.assign(): JavaScript object merging
Future Enhancements #
Section-Level Configuration #
Hugo doesn’t auto-inherit front matter, but we could add logic in baseof.html:
{{ $mermaidConfig := .Site.Params.mermaid }}
{{ with .CurrentSection.Params.mermaid }}
{{ $mermaidConfig = merge $mermaidConfig . }}
{{ end }}
{{ with .Params.mermaid }}
{{ $mermaidConfig = merge $mermaidConfig . }}
{{ end }}
This would create: site → section → page cascade.
Per-Diagram Class Definitions #
For truly unique diagram styles, keep using classDef inline:
flowchart TB
classDef special fill: red, stroke: blue;
node1(Special)
class node1 specialThis doesn’t conflict with page-level themeCSS.
Testing #
Hugo dev server running at http://localhost:1313/
Test URL: http://localhost:1313/archive/javabasic/
Expected Result:
- All 6 diagrams render correctly
- Styles from front matter
themeCSSapplied - No console errors
- Same visual appearance as before refactoring
Verification Steps:
- ✅ Hugo builds without errors (134ms build time)
- ✅ Server running at http://localhost:1313/
- Visit JavaBasic page to verify rendering
- Check browser console for Mermaid errors
- Verify all node styles match previous version
Files Modified #
layouts/_default/baseof.html(lines 15-41)- Added site and page-level config support
- JavaScript config object with cascade logic
content/archive/JavaBasic/_index.md(lines 1-11)- Added
mermaid.themeCSSto front matter - Removed 18 lines of redundant
classDeffrom diagrams
- Added
Maintenance Notes #
To change styles site-wide: #
# hugo.yaml
params:
mermaid:
theme: neutral
themeCSS: |
.site { stroke: red; fill: yellow; }
To override for one page: #
# content/some-page/_index.md
---
title: My Page
mermaid:
theme: dark
themeCSS: |
.custom { fill: blue; }
---
To override one diagram: #
%%{init: {'theme':'forest'}}%%
flowchart LR
A --> BAdding new class styles: #
- Add to page front matter:
mermaid:
themeCSS: |
.newclass { fill: green; stroke: black; }
- Use in diagram:
flowchart TB
node1(Example)
class node1 newclassReferences #
- Mermaid Configuration Documentation
- Mermaid Directives (deprecated)
- Hugo Page Variables
- Hugo Template Functions
Related Documentation #
- docs/mermaid-implementation.md - CDN vs bundled approach analysis