Skip to content

ADR-007: Hand-Crafted SVGs over Mermaid for Architecture Diagrams

ADR-007: Hand-Crafted SVGs over Mermaid for Architecture Diagrams

Section titled “ADR-007: Hand-Crafted SVGs over Mermaid for Architecture Diagrams”

Status: Accepted Date: 2026-02-14 Author: Spencer Fuller

The portfolio site (spencerfuller.dev) runs on Astro Starlight, deployed to Cloudflare Pages. Architecture diagrams are critical — they communicate system design at a glance and are often the first thing a reviewer looks at on a project page.

The initial implementation used Mermaid.js for all diagrams: flowcharts, sequence diagrams, layer stacks. Mermaid is appealing because it’s code-defined (version-controllable, diffable) and renders automatically from text descriptions. Astro has Mermaid integration via astro-mermaid.

The problem became apparent once content was deployed. Mermaid renders diagrams at a fixed internal width that doesn’t respect the page’s content column. On a site with --sl-content-width: 75rem and no right sidebar, diagrams rendered at roughly 40-50% of available width — too small to read without squinting. Specific issues:

  • Fixed rendering width. Mermaid’s SVG output uses hardcoded viewBox dimensions based on node count and layout algorithm. There’s no straightforward way to say “fill the available width.” The %%{init: {'theme': 'dark', 'themeVariables': {...}}}%% directives control colors, not layout dimensions.
  • Text truncation in complex diagrams. Architecture diagrams with long labels (service names, descriptions, protocol details) get truncated or wrapped awkwardly. Mermaid’s auto-layout optimizes for compactness, not readability.
  • No click-to-zoom. Mermaid renders inline. For dense architecture diagrams, readers need to zoom in on subsections. There’s no built-in interaction — it’s a static inline SVG.
  • Dark theme inconsistency. Despite using the dark theme preset, Mermaid’s colors didn’t match the site’s custom dark palette (Starlight’s slate/blue scheme). Every diagram needed theme variable overrides, and the results still looked like “a different application embedded in the page.”
  • Layout algorithm fights intent. Mermaid’s Dagre/ELK layout engines decide where nodes go. For architecture diagrams, spatial arrangement carries meaning — infrastructure at the bottom, applications in the middle, agents at the top. Mermaid rearranges to minimize edge crossings, which sometimes puts things in nonsensical positions.

15 diagrams across 9 pages were affected: system architecture overviews, trust layer stacks, communication sequences, tool ecosystems, pipeline flows, and cluster topologies.

Replace all Mermaid diagrams with hand-crafted SVGs stored in public/diagrams/. Add a CSS + inline JS lightbox for click-to-maximize viewing. Remove the Mermaid rendering dependency.

Each SVG is authored directly (not exported from a GUI tool), using a consistent design language:

  • Dark background (#1e293b base, matching the site’s slate palette)
  • Consistent color coding (teal for agents, amber for tools, emerald for infrastructure, rose for security)
  • Rounded rectangles with subtle gradients for depth
  • Dashed borders for external/optional components
  • Arrow markers for data flow direction
  • viewBox-based scaling that fills available width responsively
  1. Full layout control. SVGs give complete control over where every element sits. Spatial arrangement is intentional — infrastructure at the bottom, application layer in the middle, agents at the top. This isn’t decoration; it’s how engineers read architecture diagrams. Mermaid’s auto-layout actively fights this by rearranging elements to minimize edge crossings.

  2. Responsive width. SVGs with viewBox and width="100%" fill whatever container they’re in. On a wide monitor, the diagram stretches to use available space. On mobile, it scales down proportionally. Mermaid’s fixed-width output required CSS hacks that never worked reliably across breakpoints.

  3. Visual consistency with the site. Hand-crafted SVGs use the exact same color tokens as the site’s CSS custom properties. They look like part of the page, not an embedded third-party widget. The dark theme matches perfectly because it’s the same palette.

  4. Click-to-maximize for dense diagrams. The lightbox implementation (pure CSS + minimal inline JS, zero dependencies) lets readers click any diagram to view it full-viewport. For a 15-node architecture diagram, this is the difference between “I can sort of see the boxes” and “I can read every label and trace every connection.” Implementation: CSS overlay with position: fixed, triggered by click handler, dismissed by clicking the overlay or pressing Escape.

  5. AI-assisted authoring mitigates effort. The traditional argument against hand-crafted SVGs is that they’re time-intensive to create and maintain. With AI-assisted authoring, the effort equation changes substantially. Describing the desired layout in natural language and iterating on the SVG output is faster than fighting Mermaid’s layout algorithm to produce a specific arrangement. 15 diagrams were created and refined in a single session.

AlternativeWhy Not
Mermaid with CSS overridesAttempted first. Applied max-width: 100%, container scaling, font-size bumps. Mermaid’s internal SVG dimensions are set before CSS applies — the SVG viewBox is calculated from content, and CSS scaling introduces blurriness or misaligned text. Spent more time fighting the tool than it would take to just draw the diagrams.
Excalidraw / draw.io exportsGUI tools produce SVGs, but they’re bloated (draw.io exports carry XML namespaces, embedded fonts, and style blocks that triple file size) and not easily diffable. Excalidraw’s hand-drawn aesthetic doesn’t match a professional portfolio. Neither integrates with the site’s color system without post-processing.
D3.js / ObservableProgrammatic SVG generation with full control. Powerful but overkill — these are static architecture diagrams, not interactive data visualizations. Adding a D3 runtime dependency for diagrams that don’t change after deployment adds bundle size and complexity for zero benefit.
Image exports (PNG/WebP)Raster images from any diagramming tool. Lose resolution on zoom, don’t scale responsively, can’t be searched/selected, and file sizes are 5-10x larger than equivalent SVGs. Not acceptable for a technical portfolio where readers expect crisp diagrams at any zoom level.
Keep Mermaid for simple diagrams, SVG for complex onesConsidered a hybrid approach. Rejected because visual inconsistency between Mermaid-styled and hand-crafted diagrams is jarring — two different color schemes, two different layout philosophies, two different interaction models on the same site. Consistency matters more than saving effort on simple diagrams.
  • All 15 diagrams render at full content width, are readable without zooming, and scale responsively across viewport sizes
  • Click-to-maximize lightbox works on all diagrams with zero JavaScript dependencies (inline event handlers + CSS)
  • Diagrams are visually indistinguishable from the site’s design system — same colors, same rounded-corner aesthetic, same dark theme
  • SVG files are version-controlled, diffable, and average 4-8KB each (total: ~90KB for 15 diagrams)
  • AI-assisted authoring workflow established: describe layout → generate SVG → iterate on specifics → commit. Reusable for future diagrams
  • Manual maintenance burden. When architecture changes, diagrams must be manually updated. There’s no “regenerate from code” — each SVG must be edited directly. Mitigated by the AI-assisted workflow (describe the change, get an updated SVG) but it’s still more friction than editing a Mermaid code block
  • No single source of truth. Mermaid diagrams are derived from a text description — the description is the diagram. SVGs are the final artifact with no generating source. If the intent behind a layout choice isn’t documented elsewhere, it’s lost. Mitigated by keeping SVG markup clean and commented
  • Skill requirement for manual edits. Direct SVG editing requires understanding viewBox, coordinate systems, path syntax, and transform attributes. Not as accessible as editing Mermaid’s human-readable syntax. For this project, the author (AI-assisted) handles all diagram work, so this is a non-issue in practice
  • astro-mermaid still imported. The Mermaid integration package remains in astro.config.mjs as of this writing — a cleanup candidate now that no diagrams use it. Removing it will reduce build dependencies