<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Diagrams as Code — Beauty Diagram Blog]]></title><description><![CDATA[Weekly writing on Mermaid, PlantUML, draw.io, and AI diagram workflows. By the team building Beauty Diagram, a web editor for diagrams as code.
]]></description><link>https://beauty-diagram.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6a00b0ece3eebc2e209fcaba/4a0d5de3-92f9-4247-9a82-29d8afdd3b14.svg</url><title>Diagrams as Code — Beauty Diagram Blog</title><link>https://beauty-diagram.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 24 Jun 2026 16:44:02 GMT</lastBuildDate><atom:link href="https://beauty-diagram.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Beautify Every Diagram in Your Markdown Docs with One Command]]></title><description><![CDATA[Disclosure: I work on Beauty Diagram, the CLI used in the examples below. Everything in this post is doable with open-source tooling alone; the CLI is one way to make it ergonomic.


TL;DR A typical d]]></description><link>https://beauty-diagram.hashnode.dev/beautify-markdown-docs-with-cli</link><guid isPermaLink="true">https://beauty-diagram.hashnode.dev/beautify-markdown-docs-with-cli</guid><category><![CDATA[mermaid]]></category><category><![CDATA[documentation]]></category><category><![CDATA[cli]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Levi Liu]]></dc:creator><pubDate>Tue, 23 Jun 2026 15:11:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/ffe4da37-94d6-462a-b03a-64e2b4ae9073.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><em>Disclosure: I work on Beauty Diagram, the CLI used in the examples below. Everything in this post is doable with open-source tooling alone; the CLI is one way to make it ergonomic.</em></p>
</blockquote>
<blockquote>
<p><strong>TL;DR</strong> A typical docs repo has dozens of Mermaid blocks scattered across markdown files, each rendering with a different default theme. This post walks the two-command path — <code>bd extract</code> to pull every fenced block out of markdown, <code>bd batch</code> to render them all to themed SVGs, and a small markdown rewrite to embed the SVGs back via <code>&lt;picture&gt;</code>. The same pipeline drops into a GitHub Actions workflow at the end.</p>
</blockquote>
<h2>The state of diagrams in most docs repos</h2>
<p>Open any mid-sized open-source repo with substantial docs — <code>kubernetes/kubernetes</code>, <code>prometheus/prometheus</code>, <code>vercel/next.js</code>. Search the markdown tree for <code>mermaid</code> fenced blocks. You'll find 30 to 100 of them across docs, contributing guides, RFC archives, and stale design notes.</p>
<p>Then look at how those diagrams actually render. Three observations, every time:</p>
<ol>
<li><p><strong>Most diagrams use Mermaid's default theme</strong> — the pastel one with the muted blues and the soft drop-shadows. It's fine in isolation. Across 40 diagrams in a single docs site, it reads as "nobody owned this."</p>
</li>
<li><p><strong>Some diagrams are silently broken.</strong> A typo in a <code>flowchart</code> arrow, an unclosed <code>subgraph</code>, an <code>erDiagram</code> field that no longer parses on the current Mermaid version. The page renders, the diagram block becomes an error message, nobody notices for six months because nobody reads the contributing guide that often.</p>
</li>
<li><p><strong>The diagrams that DO look good are the recent ones.</strong> Whoever shipped them most recently set a theme in an <code>init</code> block at the top of the diagram. Older blocks don't have the directive. The site looks like geological strata of "whatever Mermaid theme was fashionable that quarter."</p>
</li>
</ol>
<p>You can fix all three by hand. Open every markdown file, paste each Mermaid block into a renderer, eyeball it, fix syntax errors, prepend an <code>init</code> directive with your preferred theme, save. For a 60-file docs repo that's maybe a day of mechanical work — and it'll drift again in three months because every new contributor adds blocks without the directive.</p>
<p>Or you can build a two-command pipeline that does this automatically and run it in CI.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/98fb9fbe-6091-4e26-9216-6deff5138303.svg" alt="" style="display:block;margin:0 auto" />

<p><em>The pipeline this post builds: extract every fenced diagram out of markdown, render them all in one batch, embed the SVGs back so the rendered page shows your themed version while the markdown source still holds the original code.</em></p>
<h2>Why bake the SVG into the page (instead of letting the site render Mermaid live)</h2>
<p>A reasonable counter-argument: "My doc site already renders Mermaid client-side via mermaid.js. Why pre-render at all?"</p>
<p>Three reasons the pre-render path wins for docs:</p>
<ul>
<li><p><strong>Consistency across surfaces.</strong> Your README on GitHub, your VS Code marketplace listing, your npm page, your Docusaurus site, your printed PDF export — all render Mermaid differently (or not at all). A pre-rendered SVG looks the same everywhere because there's no rendering left to do.</p>
</li>
<li><p><strong>Bundle weight.</strong> mermaid.js minified is around 600 KB. If the only reason your docs ship it is the four diagrams on your architecture page, you're paying a meaningful payload tax for every visitor of every other page.</p>
</li>
<li><p><strong>The diagram becomes a real asset.</strong> Once it's an SVG on disk you can review it in PRs (the diff is the SVG file, which renders in GitHub's PR view), you can run an <code>&lt;img&gt;</code> accessibility check on it, and you can link to it from outside the docs site.</p>
</li>
</ul>
<p>The pipeline below keeps the Mermaid source in the markdown — so contributors still edit Mermaid, not SVG — but renders to an SVG asset alongside it. The <code>&lt;picture&gt;</code> embed shows the SVG to readers; the source block becomes a collapsed <code>&lt;details&gt;</code> for anyone who wants to see the code.</p>
<h2>Step 1 — <code>bd extract</code> pulls every diagram out of markdown</h2>
<pre><code class="language-bash">npx @beauty-diagram/cli extract docs/**/*.md --assets-dir docs/_diagrams
</code></pre>
<p>This walks every markdown file matching the glob, finds every fenced <code>mermaid</code> (and <code>plantuml</code>) block, and writes each one to a standalone source file under the assets directory. Filename is derived from the markdown file path plus a stable hash of the block contents, so re-running on an unchanged docs tree is a no-op.</p>
<p>A typical output:</p>
<pre><code class="language-plaintext">docs/_diagrams/
  architecture/
    overview-a3f9c1.mmd
    overview-b8e240.mmd
    auth-flow-7c2105.mmd
  contributing/
    pr-lifecycle-91d8a2.mmd
  guides/
    request-path-4e1c80.mmd
</code></pre>
<p>A few flags worth knowing:</p>
<ul>
<li><p><code>--dry-run</code> lists what would be written without touching disk. Run this first to see how many blocks your repo actually has.</p>
</li>
<li><p><code>--clean</code> removes orphaned source files in the assets directory before extracting — useful when blocks get deleted from markdown and you don't want stale <code>.mmd</code> files lying around.</p>
</li>
<li><p><code>--concurrency &lt;n&gt;</code> controls parallel parsing for very large doc trees. The default is sensible; only touch it if you're indexing thousands of files.</p>
</li>
</ul>
<p>If a block has a syntax error, <code>bd extract</code> still writes the source file and prints a warning. The block is real even if it's broken; the next step decides what to do with broken blocks.</p>
<h2>Step 2 — <code>bd batch</code> renders them all to themed SVGs</h2>
<pre><code class="language-bash">npx @beauty-diagram/cli batch docs/_diagrams/**/*.mmd \
  --out-dir docs/_diagrams \
  --format svg
</code></pre>
<p>This renders every extracted source file to an SVG sibling. Same filename, different extension. Theme is whichever one you set globally — <code>BEAUTY_DIAGRAM_THEME=modern</code> in the environment, or passed per-file via the source's own <code>init</code> directive.</p>
<p>You'll get output like:</p>
<pre><code class="language-plaintext">✓ docs/_diagrams/architecture/overview-a3f9c1.svg (1142×680)
✓ docs/_diagrams/architecture/overview-b8e240.svg (980×420)
✓ docs/_diagrams/architecture/auth-flow-7c2105.svg (1240×910)
✗ docs/_diagrams/contributing/pr-lifecycle-91d8a2.mmd: parse error at line 14
✓ docs/_diagrams/guides/request-path-4e1c80.svg (860×540)

4 succeeded, 1 failed
</code></pre>
<p>The <code>--stop-on-error</code> flag flips the failure mode: by default <code>bd batch</code> keeps going so one bad diagram doesn't tank a 200-diagram run. In CI you'll usually want <code>--stop-on-error</code> so a broken block fails the build, which is the whole point of running this in CI to begin with.</p>
<p><code>--concurrency</code> defaults to a reasonable parallelism for your CPU; raise it on a big CI runner if the batch takes long enough to matter.</p>
<p>The "consistency" payoff lands here. The same theme renders every diagram across the entire docs tree.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/dfbd635c-1528-4d13-b74b-eeffdf682064.svg" alt="" style="display:block;margin:0 auto" />

<p><em>A typical architecture diagram rendered with a single chosen theme. The consistency story is that every one of your 40 docs diagrams comes out looking like this one — same stroke weight, same palette, same edge style.</em></p>
<h2>Step 3 — Embed the SVGs back via <code>&lt;picture&gt;</code></h2>
<p>The extract / batch pair gave you SVG files. The markdown still holds the original Mermaid source. Now you decide how the rendered page should reference the asset.</p>
<p>The cleanest pattern is <code>&lt;picture&gt;</code> with the SVG as the primary source and a collapsed <code>&lt;details&gt;</code> for the original code:</p>
<pre><code class="language-markdown">&lt;picture&gt;
  &lt;img
    src="/_diagrams/architecture/overview-a3f9c1.svg"
    alt="High-level system architecture: API gateway, auth service, app services, and the shared event bus"
  /&gt;
&lt;/picture&gt;

&lt;details&gt;
&lt;summary&gt;Mermaid source&lt;/summary&gt;

```mermaid
flowchart LR
  Gateway[API Gateway] --&gt; Auth[Auth Service]
  Gateway --&gt; App[App Services]
  App --&gt; Bus[(Event Bus)]
```

&lt;/details&gt;
</code></pre>
<p>Why this shape:</p>
<ul>
<li><p><strong>The reader sees the SVG by default</strong> — themed, crisp, no client-side rendering cost.</p>
</li>
<li><p><strong>The source is one click away</strong> for anyone who wants to copy it into their own diagram, file a fix, or learn from the syntax.</p>
</li>
<li><p><strong>The markdown file remains the source of truth.</strong> A contributor editing the doc edits the Mermaid block, runs the pipeline, commits both the updated source and the regenerated SVG.</p>
</li>
</ul>
<p>Most static site generators (Docusaurus, VitePress, MkDocs, Hugo, Astro) render <code>&lt;picture&gt;</code> and <code>&lt;details&gt;</code> natively. GitHub's markdown renderer does too, including in PR descriptions. The pattern is portable.</p>
<p>If you'd rather not maintain the dual source + asset embed by hand, the extract step has a <code>--rewrite</code> mode that updates the markdown file in place — collapsing the fenced block into the <code>&lt;picture&gt;</code> + <code>&lt;details&gt;</code> pair on first run, leaving it alone on subsequent runs.</p>
<h2>Step 4 — Wire it into CI so it stays beautified</h2>
<p>A small GitHub Actions workflow that fails any PR introducing a broken diagram or skipping the regeneration:</p>
<pre><code class="language-yaml">name: Beautify docs

on:
  pull_request:
    paths:
      - 'docs/**/*.md'
      - 'docs/_diagrams/**'

jobs:
  beautify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - name: Extract diagrams from markdown
        run: |
          npx @beauty-diagram/cli extract 'docs/**/*.md' \
            --assets-dir docs/_diagrams \
            --clean
      - name: Render every diagram
        run: |
          npx @beauty-diagram/cli batch 'docs/_diagrams/**/*.mmd' \
            --out-dir docs/_diagrams \
            --format svg \
            --stop-on-error
        env:
          BEAUTY_DIAGRAM_THEME: modern
      - name: Fail if anything changed
        run: |
          git diff --exit-code docs/_diagrams || (
            echo "::error::Diagrams are out of date. Run the beautify pipeline locally and commit the result."
            exit 1
          )
</code></pre>
<p>The <code>git diff --exit-code</code> at the end is the trick. The job regenerates everything from scratch on every PR; if the output differs from what's checked in, the PR failed to run the pipeline before pushing, and the build fails. Contributors learn the workflow once and it stays automatic forever.</p>
<p>For a <code>Makefile</code>-driven repo, the same pattern collapses to two targets:</p>
<pre><code class="language-makefile">diagrams:
	npx @beauty-diagram/cli extract 'docs/**/*.md' --assets-dir docs/_diagrams --clean
	npx @beauty-diagram/cli batch 'docs/_diagrams/**/*.mmd' --out-dir docs/_diagrams --format svg --stop-on-error

verify-diagrams: diagrams
	git diff --exit-code docs/_diagrams
</code></pre>
<p><code>make diagrams</code> regenerates locally; <code>make verify-diagrams</code> is what CI calls.</p>
<hr />
<blockquote>
<p><strong>From one file to a whole repo: themed SVGs for every diagram, in one batch</strong></p>
<p><a href="https://www.beauty-diagram.com">Beauty Diagram</a>'s CLI ships extract + batch as the docs-scale pair. Pick a theme once, apply it across the whole repo, let the git-diff check keep contributors honest.</p>
<p><a href="https://www.beauty-diagram.com/editor"><strong>→ Try one diagram in the editor</strong></a></p>
</blockquote>
<hr />
<pre><code class="language-bash"># Install once
npm install -D @beauty-diagram/cli

# Set the theme for the whole repo
export BEAUTY_DIAGRAM_THEME=modern

# Run the pipeline locally
npx bd extract 'docs/**/*.md' --assets-dir docs/_diagrams --clean
npx bd batch 'docs/_diagrams/**/*.mmd' --out-dir docs/_diagrams --format svg
</code></pre>
<p><code>bd beautify</code>, <code>bd export</code>, <code>bd extract</code>, and <code>bd batch</code> all work anonymously for rendering. The docs-pipeline use case lives entirely inside the free surface.</p>
<h2>When NOT to pre-render</h2>
<p>A few cases where the live-Mermaid-in-the-browser path is the better trade:</p>
<ol>
<li><p><strong>Your docs site already ships mermaid.js for other reasons.</strong> If the bundle cost is paid, you're not saving anything by pre-rendering. Use <code>init</code> directives in your Mermaid blocks to set a consistent theme and call it done.</p>
</li>
<li><p><strong>Diagrams change every commit.</strong> A README that auto-regenerates an architecture diagram from the codebase on every push has no use for pre-rendered assets — the SVG would be stale by the next push. Live rendering at view time is the right call.</p>
</li>
<li><p><strong>You only have three diagrams.</strong> The batch pipeline pays off when there are enough diagrams that consistency is the problem. Three diagrams you can keep coherent by hand.</p>
</li>
<li><p><strong>Your audience needs the source visible by default.</strong> A Mermaid tutorial, a "diagrams as code" RFC, a contributing guide. Pre-rendering hides the syntax behind a <code>&lt;details&gt;</code>; if the syntax IS the point, leave it as a fenced block.</p>
</li>
</ol>
<p>The rule of thumb: pre-render when the diagram is a doc asset (read, not edited, by most readers); live-render when the diagram is the doc.</p>
<h2>Wrap-up</h2>
<p>Three takeaways:</p>
<ul>
<li><p><strong>Diagrams in docs repos drift the same way docs drift.</strong> Different themes, broken blocks, no review process. The fix is the same fix you applied to docs: a build step + a CI gate.</p>
</li>
<li><p><strong>The two-command pipeline is</strong> <code>bd extract</code> <strong>+</strong> <code>bd batch</code><strong>.</strong> Extract pulls every fenced block to standalone source files; batch renders all of them to themed SVGs. The git-diff CI check keeps the output in sync.</p>
</li>
<li><p><strong>Embed via</strong> <code>&lt;picture&gt;</code> <strong>+ collapsed</strong> <code>&lt;details&gt;</code><strong>.</strong> Reader sees the themed SVG, contributor still edits Mermaid, source of truth stays in markdown.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Generate Mermaid from Plain English with AI (CLI Walkthrough)]]></title><description><![CDATA[TL;DR You already know flowchart TD cold. You don't know sequenceDiagram participant aliases, erDiagram cardinality glyphs, or which state diagram dialect Mermaid is on this year. This post is a walkt]]></description><link>https://beauty-diagram.hashnode.dev/mermaid-from-prompt-with-ai</link><guid isPermaLink="true">https://beauty-diagram.hashnode.dev/mermaid-from-prompt-with-ai</guid><category><![CDATA[mermaid]]></category><category><![CDATA[AI]]></category><category><![CDATA[cli]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[pkm]]></category><dc:creator><![CDATA[Levi Liu]]></dc:creator><pubDate>Fri, 12 Jun 2026 17:34:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/dae40ea2-ebf6-494f-ab3a-2862f265a8e5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR</strong> You already know <code>flowchart TD</code> cold. You don't know <code>sequenceDiagram</code> participant aliases, <code>erDiagram</code> cardinality glyphs, or which state diagram dialect Mermaid is on this year. This post is a walkthrough of five prompts → five diagrams using <code>bd ai generate</code>, plus when the AI-first path is wrong.</p>
</blockquote>
<blockquote>
<p><strong>Disclosure</strong>: I work on a small web editor and CLI that's mentioned in the second half. The first half — the prompting workflow and the five diagram types — works with any AI that produces Mermaid; the tool just packages the pipeline.</p>
</blockquote>
<h2>The friction isn't drawing — it's remembering</h2>
<p>I've shipped probably 200 flowcharts. I can sketch a <code>flowchart TD</code> with arrows and labels without thinking. The syntax has lived in my hands long enough that it's muscle memory.</p>
<p>Then someone asks for a sequence diagram of the OAuth flow, and I open the Mermaid docs. Again. For the seventh time this year.</p>
<p>That's the actual friction with diagrams-as-code: most teams write one diagram type 90% of the time and four other types maybe twice a year each. The rare types never make it into your fingers — you re-read the docs every time, hit a syntax snag, and lose ten minutes. Multiply by every infrequent diagram across a docs repo and that's a real tax.</p>
<p>AI removes the activation energy. You describe the diagram in plain English, get back syntactically valid Mermaid, and skip straight to "does this say what I want." That's a different kind of editing — refining a draft instead of looking up glyphs.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/ce841db8-e5c4-4c7a-b3b9-d46acc175267.svg" alt="" style="display:block;margin:0 auto" />

<p><em>The path this post walks: prompt → AI → Mermaid → rendered diagram.</em></p>
<h2>The shape of the workflow</h2>
<p>Two paths, same engine underneath:</p>
<ol>
<li><p><strong>In an editor</strong> — paste your prompt into an AI panel that writes Mermaid for you, tweak the source, pick a theme, export.</p>
</li>
<li><p><strong>In the terminal</strong> — <code>bd ai generate "&lt;prompt&gt;"</code> prints the Mermaid to stdout. Pipe it into a renderer, redirect to a file, or paste into your README.</p>
</li>
</ol>
<p>The editor path is the right entry point if you're iterating — you can see the rendered diagram live as you refine the source. The CLI is the right surface once you know what you want and you're scripting it into a docs build, a <code>package.json</code> task, or a CI job.</p>
<p>The five examples below all use the CLI form so the prompts and outputs are easy to copy.</p>
<h2>Prompt 1 — OAuth login (sequence diagram)</h2>
<p>Sequence diagrams are the textbook case for "I know what I want, I don't remember the syntax." Participants, arrows, dotted return lines, <code>loop</code> blocks — there's just enough surface to forget between uses.</p>
<pre><code class="language-bash">bd ai generate "user signs in with Google OAuth: web app redirects \
to Google's authorize endpoint, user approves, Google returns an \
authorization code, web app exchanges the code at our API for a \
session token"
</code></pre>
<p>The AI returns something like:</p>
<pre><code class="language-mermaid">sequenceDiagram
  participant U as User
  participant W as Web App
  participant G as Google OAuth
  participant A as App API
  U-&gt;&gt;W: Click "Sign in with Google"
  W-&gt;&gt;G: Redirect to /authorize
  G-&gt;&gt;U: Consent prompt
  U-&gt;&gt;G: Approve scopes
  G-&gt;&gt;W: Authorization code
  W-&gt;&gt;A: Exchange code
  A-&gt;&gt;W: Session token
  W-&gt;&gt;U: Logged in
</code></pre>
<p>Render it:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/6e50de8e-a847-4127-b950-bcef21e30852.svg" alt="" style="display:block;margin:0 auto" />

<p><em>OAuth authorization-code flow, generated from the prompt above and rendered with the</em> <code>atlas</code> <em>theme.</em></p>
<p>The participants are aliased (<code>U as User</code>, <code>W as Web App</code>) without me having to remember the syntax. The arrows pick <code>-&gt;&gt;</code> for solid calls. Two minutes from prompt to rendered diagram, of which ten seconds was renaming "App API" to "Backend" to match the codebase.</p>
<h2>Prompt 2 — Multi-tenant SaaS schema (ER diagram)</h2>
<p>ER diagrams are the worst offender for syntax recall. Cardinality glyphs — <code>||--o{</code>, <code>}o--||</code>, <code>|o--o|</code> — never live in anyone's head.</p>
<pre><code class="language-bash">bd ai generate "multi-tenant SaaS database: users belong to many \
workspaces through memberships; workspaces own projects; projects \
contain tasks; users are also assigned to tasks"
</code></pre>
<p>Output:</p>
<pre><code class="language-mermaid">erDiagram
  USERS ||--o{ MEMBERSHIPS : has
  WORKSPACES ||--o{ MEMBERSHIPS : has
  WORKSPACES ||--o{ PROJECTS : owns
  PROJECTS ||--o{ TASKS : contains
  USERS ||--o{ TASKS : assigned
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/955712af-2d7b-4033-994d-57255c1a29a0.svg" alt="" style="display:block;margin:0 auto" />

<p><em>A multi-tenant SaaS schema, generated from a one-sentence prompt and rendered with the</em> <code>blueprint</code> <em>theme.</em></p>
<p>The model picked the right cardinality glyphs without you typing them. It also picked an entity-naming convention (uppercase, plural) and stuck with it across all five entities. That's the kind of consistency tax you'd otherwise pay manually.</p>
<p>When this matters: the diagram is wrong if <code>MEMBERSHIPS</code> should have been an attribute on <code>USERS</code> instead of a join entity. The AI guessed "many-to-many through membership", which is the right call for most multi-tenant SaaS — but if you're modeling something else, you'll edit. That's the workflow: get a draft fast, refine the parts that don't match your domain.</p>
<h2>Prompt 3 — CI/CD pipeline (flowchart)</h2>
<p>Yes, even the flowchart you can write blindfolded benefits from a prompt when the diagram has six branches and a manual gate.</p>
<pre><code class="language-bash">bd ai generate "GitHub Actions pipeline: PR merged to main triggers \
tests; if tests pass build a container image, deploy to staging, \
require manual approval, then deploy to production. If tests fail, \
notify the PR author."
</code></pre>
<p>Output:</p>
<pre><code class="language-mermaid">flowchart TD
  PR[PR merged to main] --&gt; Test[Run tests]
  Test --&gt;|pass| Build[Build image]
  Test --&gt;|fail| Notify[Notify author]
  Build --&gt; Stage[Deploy to staging]
  Stage --&gt; Gate{Manual approval}
  Gate --&gt;|approve| Prod[Deploy to prod]
  Gate --&gt;|reject| Hold[Hold release]
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/e0b6a804-9041-4ae4-bf66-b53b32bdadf0.svg" alt="" style="display:block;margin:0 auto" />

<p><em>The CI/CD pipeline, with the failure branches the prompt asked for. Rendered with the</em> <code>modern</code> <em>theme.</em></p>
<p>Two details worth noting. First, the AI chose <code>{Manual approval}</code> (decision diamond) instead of <code>[Manual approval]</code> (rectangle) for the gate — the right shape for a yes/no branch. Second, it used <code>--&gt;|pass|</code> for labelled edges instead of breaking the label onto a separate line, which is the cleaner Mermaid idiom.</p>
<p>You could have written this. The question is whether you want to spend three minutes typing it or thirty seconds prompting it. For a diagram that's going in your team's onboarding doc and won't change for six months, the prompt path is the better trade.</p>
<h2>Prompt 4 — Order lifecycle (state diagram)</h2>
<p>State machines are another "syntax-rare" category. <code>[*] --&gt; X</code>, transitions with labels, the difference between <code>stateDiagram</code> and <code>stateDiagram-v2</code> (the v2 syntax is what Mermaid currently maintains).</p>
<pre><code class="language-bash">bd ai generate "order lifecycle state machine: starts in cart, \
becomes placed on checkout, paid when payment succeeds, shipped \
when fulfilled, delivered when the carrier confirms. From paid, \
the customer can cancel to refunded."
</code></pre>
<p>Output:</p>
<pre><code class="language-mermaid">stateDiagram-v2
  [*] --&gt; Cart
  Cart --&gt; Placed: Checkout
  Placed --&gt; Paid: Payment OK
  Paid --&gt; Shipped: Fulfilled
  Shipped --&gt; Delivered: Carrier confirms
  Delivered --&gt; [*]
  Paid --&gt; Refunded: Customer cancels
  Refunded --&gt; [*]
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/49bef08c-4f5d-4048-9095-7db7f57bdadd.svg" alt="" style="display:block;margin:0 auto" />

<p><em>Order lifecycle states, with the refund branch the prompt described. Rendered with the</em> <code>slate</code> <em>theme.</em></p>
<p>The AI picked <code>stateDiagram-v2</code> (the current syntax), used <code>[*]</code> for entry and exit, and put transition labels after the colon. All things I'd otherwise look up.</p>
<p>There's a subtler thing happening: the prompt described "from paid, the customer can cancel to refunded" — a side branch from the main happy path. The AI laid it out as a separate transition rather than nesting it inside a <code>state Paid { ... }</code> block. That's a layout decision the model made for you. If you wanted the refund flow nested, you'd add "group Paid → Refunded into a Paid composite state" to the prompt.</p>
<h2>Prompt 5 — Notification class hierarchy (class diagram)</h2>
<p>Class diagrams are the format devs hate most. The syntax is verbose, the inheritance arrows are easy to mix up, and you write maybe two per year.</p>
<pre><code class="language-bash">bd ai generate "class hierarchy for a polymorphic notification \
system: base Notification with channel and deliver(), then \
EmailNotification with htmlBody and fromAddress, SmsNotification \
with shortMessage and senderId, PushNotification with deeplink \
and iconUrl"
</code></pre>
<p>Output:</p>
<pre><code class="language-mermaid">classDiagram
  class Notification {
    +String channel
    +deliver()
  }
  class EmailNotification {
    +String htmlBody
    +String fromAddress
  }
  class SmsNotification {
    +String shortMessage
    +String senderId
  }
  class PushNotification {
    +String deeplink
    +String iconUrl
  }
  Notification &lt;|-- EmailNotification
  Notification &lt;|-- SmsNotification
  Notification &lt;|-- PushNotification
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/4afddd9d-e965-4f61-b562-817bdebbcb2a.svg" alt="" style="display:block;margin:0 auto" />

<p><em>Three-channel notification class hierarchy. Rendered with the</em> <code>atelier</code> <em>theme.</em></p>
<p><code>&lt;|--</code> (empty triangle, hollow head, line) is the inheritance arrow. Reversed direction (<code>--|&gt;</code>) means the same relationship from the other end. Knowing that one glyph fluently is the difference between a 30-second class diagram and a 5-minute one. The AI knows it; you don't have to.</p>
<h2>When NOT to use AI</h2>
<p>The prompt-to-diagram path is great for drafts. It's a worse fit for these cases:</p>
<ol>
<li><p><strong>The diagram already exists, you're refining it.</strong> Editing five nodes by hand is faster than prompting "the same diagram but with X changed." Refinement is what a source pane is for.</p>
</li>
<li><p><strong>You need precise layout.</strong> AI controls the <em>content</em> (nodes, edges, labels). It doesn't control left-vs-right ordering, column widths, where the diamond lands. If a stakeholder needs the "approve" branch on the right specifically, you'll be editing the source anyway.</p>
</li>
<li><p><strong>The diagram encodes ground truth.</strong> Database schemas pulled from <code>\dt</code>, API call sequences pulled from logs, dependency graphs from your build system — pull these from the source of truth, don't have an AI guess. The AI is useful when <em>you're</em> the source of truth and the friction is just getting it into Mermaid syntax.</p>
</li>
<li><p><strong>You're learning Mermaid.</strong> If your goal is to internalise the syntax for a diagram type you'll need monthly, hand-write the first five. After that, automation is fine. Skipping the learning round costs you forever.</p>
</li>
</ol>
<p>The rule of thumb: use AI for "I know what I want and I don't want to look up the syntax." Don't use it for "I don't know what the diagram should show yet" or "this diagram needs to be load-bearing for an audit."</p>
<hr />
<blockquote>
<p><strong>A small recommendation (disclosed at the top)</strong></p>
<p>The tool I work on, <a href="https://www.beauty-diagram.com">Beauty Diagram</a>, bundles prompt-to-Mermaid generation, a fixed set of production themes (<code>classic</code>, <code>modern</code>, <code>atlas</code>, <code>blueprint</code>, <code>memphis</code>, <code>obsidian</code>, <code>slate</code>, <code>brutalist</code>, <code>atelier</code>), and SVG/PNG export into one editor — useful if you want the prompt → rendered diagram loop without juggling separate tools.</p>
<p>The same pipeline runs from a CLI:</p>
<pre><code class="language-bash"># Prompt → rendered SVG, one pipe
npx @beauty-diagram/cli ai generate "user signup with email verification" \
  | npx @beauty-diagram/cli beautify - --theme modern --out signup.svg
</code></pre>
<p><code>bd ai generate</code> is paid-plan only (each call costs us a model invocation); <code>bd auth login</code> saves a key locally. The renderer half (<code>bd beautify</code>, <code>bd export</code>) works anonymously if you only need rendering. <a href="https://www.beauty-diagram.com/editor">→ Try the editor</a> if you want to see the prompt loop without installing anything.</p>
</blockquote>
<hr />
<h2>Wrap-up</h2>
<p>The three takeaways:</p>
<ul>
<li><p><strong>Use AI for the diagram types you don't write often.</strong> Sequence, ER, class, state — anything where you'd otherwise re-read the docs. Flowcharts you already write fluently; skip the AI if you don't need the speed.</p>
</li>
<li><p><strong>Treat the output as a draft.</strong> Rename, reorder, retheme. The prompt got you past the syntax wall; you still own the diagram.</p>
</li>
<li><p><strong>Two surfaces, one engine.</strong> Editor for iteration, CLI for scripts. The CLI's <code>bd ai generate | bd beautify -</code> pipe is the fastest "prompt to SVG on disk" path.</p>
</li>
</ul>
<p>Most teams I've watched adopt this end up using AI for the 20% of diagrams that aren't flowcharts, and keep hand-writing flowcharts because the muscle memory is faster than a prompt. That split happens organically — you don't have to enforce it.</p>
]]></content:encoded></item><item><title><![CDATA[Embed Mermaid in Notion: The 4 Working Approaches in 2026]]></title><description><![CDATA[-->

TL;DR
Notion has native Mermaid in code blocks now, but the default look is the same pastel render you've seen everywhere, and there's no theme override. This post walks through the four approach]]></description><link>https://beauty-diagram.hashnode.dev/embed-mermaid-in-notion</link><guid isPermaLink="true">https://beauty-diagram.hashnode.dev/embed-mermaid-in-notion</guid><category><![CDATA[mermaid]]></category><category><![CDATA[notion]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[documentation]]></category><category><![CDATA[pkm]]></category><dc:creator><![CDATA[Levi Liu]]></dc:creator><pubDate>Fri, 05 Jun 2026 14:32:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/5b4d98b0-dbd1-4c14-a14b-6be069cc316a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>--&gt;</p>
<blockquote>
<p><strong>TL;DR</strong>
Notion has native Mermaid in code blocks now, but the default look is the same pastel render you've seen everywhere, and there's no theme override. This post walks through the four approaches that actually work in 2026 — native code block, static image upload, public hot-linkable image, and live-updating embed — with a comparison table at the end so you can pick by trade-off.</p>
</blockquote>
<blockquote>
<p><strong>Disclosure</strong>: I work on a small web editor that's mentioned briefly toward the end. The bulk of the article is about the open-source options — the four approaches stand on their own, no tool needed.</p>
</blockquote>
<h2>Why this article exists</h2>
<p>Two years ago, "embed Mermaid in Notion" was a search query that returned twenty Stack Overflow threads and zero working answers. Then Notion shipped native <code>mermaid</code> code-block rendering, and the question went quiet.</p>
<p>But the question didn't go away — it shifted. The native renderer works, but it gives you exactly one look: Mermaid's default theme, no overrides, no <code>themeVariables</code> honoured. The moment you want your team's Notion docs to feel like a finished product instead of a hackathon scratchpad, you're back to needing a workaround.</p>
<p>There are four of them. Each makes a different trade between live-updating, aesthetic control, and how much pipeline you're willing to maintain.</p>
<h2>The four approaches at a glance</h2>
<table>
<thead>
<tr>
<th>Approach</th>
<th>Live updates</th>
<th>Theme control</th>
<th>Maintenance</th>
</tr>
</thead>
<tbody><tr>
<td>1. Native <code>mermaid</code> code block</td>
<td>✅</td>
<td>❌</td>
<td>None</td>
</tr>
<tr>
<td>2. Static image upload</td>
<td>❌</td>
<td>✅</td>
<td>Manual re-upload</td>
</tr>
<tr>
<td>3. Public hot-linkable image</td>
<td>✅*</td>
<td>✅</td>
<td>Host the image</td>
</tr>
<tr>
<td>4. Live embed via <code>/embed</code></td>
<td>✅</td>
<td>✅</td>
<td>Host the endpoint</td>
</tr>
</tbody></table>
<p>* Live updates only if the source URL serves a fresh render each time.</p>
<p>The rest of this post is just the details under each row.</p>
<h2>Approach 1: Native <code>mermaid</code> code block</h2>
<p>The cheapest path. Type <code>/code</code>, set the language to <strong>Mermaid</strong>, paste your source.</p>
<pre><code class="language-mermaid">flowchart LR
  A[Client] --&gt; B{Auth?}
  B --&gt;|Yes| C[API]
  B --&gt;|No| D[Login]
</code></pre>
<p>Notion renders it live. You can edit the source, the diagram re-renders. Free, zero pipeline.</p>
<p>What you give up:</p>
<ul>
<li><strong>No theme override.</strong> The <code>%%{init: {...}}%%</code> directive is silently stripped in Notion's renderer. You get default Mermaid pastels whether you want them or not.</li>
<li><strong>No <code>&lt;picture&gt;</code> for dark mode.</strong> Notion's dark/light switch doesn't tell Mermaid anything; the diagram stays one fixed palette.</li>
<li><strong>No PDF export fidelity.</strong> When you export the Notion page to PDF, the Mermaid block renders as a raster snapshot, often at a lower resolution than the page expects.</li>
</ul>
<p>Use this for: internal scratch docs, RFCs, anything where "the diagram exists" is the bar.</p>
<p>Don't use this for: public-facing docs, customer-shared Notion pages, anything you'd want to look intentional.</p>
<h2>Approach 2: Static image upload</h2>
<p>Render the diagram to SVG or PNG offline, then drag the file into Notion. It becomes an Image block; Notion hosts it on its own CDN.</p>
<pre><code class="language-bash"># Official Mermaid CLI
npx @mermaid-js/mermaid-cli -i flow.mmd -o flow.svg -t base
</code></pre>
<p>Drag <code>flow.svg</code> into the Notion page. Done.</p>
<p>Pros: full theme control (you're rendering it yourself), works in PDF export, looks identical light and dark.</p>
<p>Cons: <strong>no live updates.</strong> Edit the Mermaid source, you re-render the file, you re-upload it. For a doc with five diagrams that change quarterly, this is fine. For a system architecture page that drifts weekly, it's a paper cut.</p>
<p>A subtle gotcha: Notion <strong>strips SVG <code>&lt;script&gt;</code> tags and external font references</strong> on upload. If your SVG depends on a webfont link in <code>&lt;defs&gt;</code>, the text falls back to Notion's default font. Inline the font with <code>font-family: -apple-system, system-ui, sans-serif</code> to avoid the mismatch.</p>
<h2>Approach 3: Public hot-linkable image</h2>
<p>Host the SVG somewhere public, then in Notion: <code>/image</code> → <strong>Embed link</strong> → paste the URL. Notion fetches it on every page load.</p>
<p>This is the sweet spot for repos that already commit their diagrams as SVG. The pattern:</p>
<ol>
<li>Render the diagram into your repo: <code>diagrams/auth-flow.svg</code></li>
<li>Commit and push.</li>
<li>In Notion, embed: <code>https://github.com/&lt;org&gt;/&lt;repo&gt;/raw/main/diagrams/auth-flow.svg</code></li>
</ol>
<p>Notion will fetch the URL each time the page loads. Push a new SVG to <code>main</code>, the embed updates within a few minutes (GitHub's raw CDN has a short TTL).</p>
<p>The catch: GitHub's <code>raw.githubusercontent.com</code> serves with <code>Content-Type: text/plain</code>, which some browsers refuse to render as an image. The reliable trick is to route through <code>https://github.com/&lt;org&gt;/&lt;repo&gt;/raw/main/...</code> (no <code>raw.</code> prefix), which redirects to a properly typed CDN response. Notion handles this redirect correctly.</p>
<p>Other hosts work too — Cloudflare R2, S3 with a public bucket, any static CDN. The constraint is just: serve <code>image/svg+xml</code>, allow Notion's user-agent, and don't gate behind auth.</p>
<h2>Approach 4: Live embed via <code>/embed</code> + hosted SVG endpoint</h2>
<p>The most flexible — and the most pipeline. Use Notion's <code>/embed</code> block (which accepts any URL) to point at an endpoint that renders the Mermaid source on demand.</p>
<pre><code>/embed → https://your-domain.com/render?source=&lt;encoded-mermaid&gt;&amp;theme=atlas
</code></pre>
<p>The endpoint does what Mermaid CLI does, but server-side, and returns <code>image/svg+xml</code>. You can pass theme parameters in the query string, regenerate on every request, and the diagram in Notion always reflects whatever the endpoint serves right now.</p>
<p>A barebones version in 50 lines of Node:</p>
<pre><code class="language-typescript">import express from "express";
import { run } from "@mermaid-js/mermaid-cli";
import fs from "fs/promises";

const app = express();

app.get("/render", async (req, res) =&gt; {
  const source = Buffer.from(req.query.source as string, "base64").toString("utf8");
  const tmpIn = `/tmp/${Date.now()}.mmd`;
  const tmpOut = tmpIn.replace(".mmd", ".svg");

  await fs.writeFile(tmpIn, source);
  await run(tmpIn, tmpOut, { puppeteerConfig: { args: ["--no-sandbox"] } });

  const svg = await fs.readFile(tmpOut, "utf8");
  res.type("image/svg+xml").send(svg);
});

app.listen(3000);
</code></pre>
<p>This is the "I want full control" path. You're now running a renderer. That means:</p>
<ul>
<li>A box (Lambda, Cloud Run, a Fly machine) running Chromium for Puppeteer</li>
<li>A cache layer so identical sources don't re-render on every Notion poll</li>
<li>A timeout policy so a malformed source doesn't hang</li>
<li>Some auth or rate limiting so the endpoint doesn't become someone else's free render farm</li>
</ul>
<p>For a team that already has infra, the 80% solution above is a weekend project. For a solo dev, it's the moment you ask whether the time is worth it.</p>
<h2>When DIY stops being worth it</h2>
<p>Three signals it's time to stop hand-rolling the Notion pipeline:</p>
<ol>
<li>You have more than ~10 diagrams across your Notion workspace and you're losing track of which ones are stale.</li>
<li>You want a theme that matches your company's docs site, not Mermaid's defaults.</li>
<li>The team's non-engineers want to make and edit diagrams without learning Mermaid or your render endpoint.</li>
</ol>
<hr />
<blockquote>
<p><strong>A small recommendation (disclosed at the top)</strong></p>
<p>The tool I work on, <a href="https://www.beauty-diagram.com">Beauty Diagram</a>, is a web editor for Mermaid, PlantUML, and draw.io. You paste your source, pick from a fixed set of production themes (<code>classic</code>, <code>modern</code>, <code>atlas</code>, <code>blueprint</code>, <code>memphis</code>, <code>obsidian</code>, <code>slate</code>, <code>brutalist</code>, <code>atelier</code>), then Save &amp; Share to get a hot-linkable URL. Drop the URL into Notion's <code>/embed</code> block and it stays live; edits to the share propagate within ~5 minutes.</p>
<p>The same pipeline runs from a CLI for CI use:</p>
<pre><code class="language-bash">npx @beauty-diagram/cli embed-url flow.mmd --theme atlas --share
# → https://api.beauty-diagram.com/v1/share/&lt;token&gt;.svg
</code></pre>
<p>Re-running on the same file replaces the share contents in place, so the URL stays stable and the embed picks up the update automatically. The CLI exposes the same nine themes (<code>bd themes</code> lists them) and no per-property overrides — the philosophy is that the theme is the choice; you pick a theme, not properties.</p>
</blockquote>
<hr />
<h2>Picking the right approach for your page</h2>
<p>A rough decision tree:</p>
<ul>
<li><strong>Throwaway internal doc?</strong> Native code block. Five seconds, zero pipeline.</li>
<li><strong>Quarterly architecture review that gets exported to PDF?</strong> Static SVG upload. Manual, but PDF-safe.</li>
<li><strong>Diagrams that already live in your repo?</strong> GitHub raw URL embed via <code>/image</code>. Free, auto-updates on push.</li>
<li><strong>Customer-facing Notion page, or a team doc that drifts weekly?</strong> Live embed against a hosted endpoint. Either roll your own, or use a service.</li>
</ul>
<p>The trap to avoid: picking the most powerful approach for every diagram. Native code blocks are fine when "the diagram exists" is the bar — don't over-engineer them.</p>
<h2>Wrap-up</h2>
<p>The four approaches, recapped:</p>
<ul>
<li><strong>Native <code>mermaid</code> code block</strong> — free, live, but locked to Mermaid defaults.</li>
<li><strong>Static image upload</strong> — full control, but no live updates.</li>
<li><strong>Public hot-linkable image</strong> — live and themed, but you host the SVG.</li>
<li><strong>Live embed via <code>/embed</code></strong> — full pipeline, full control, the most work.</li>
</ul>
<p>Most Notion workspaces will use a mix — native blocks for scratch pages, hot-linked SVGs for stable architecture docs, and a live endpoint only for the handful of pages where the cost is justified.</p>
]]></content:encoded></item><item><title><![CDATA[Mermaid vs PlantUML in 2026: Which to Pick for Engineering Docs]]></title><description><![CDATA[TL;DR Mermaid won the README and the markdown ecosystem; PlantUML won the engineering whiteboard and the IDE. In 2026 they're still the two serious "diagram as code" formats, and which one you should ]]></description><link>https://beauty-diagram.hashnode.dev/mermaid-vs-plantuml-2026</link><guid isPermaLink="true">https://beauty-diagram.hashnode.dev/mermaid-vs-plantuml-2026</guid><category><![CDATA[plantuml]]></category><category><![CDATA[mermaid]]></category><category><![CDATA[documentation]]></category><category><![CDATA[pkm]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Levi Liu]]></dc:creator><pubDate>Fri, 05 Jun 2026 03:40:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/54098eff-591c-447d-9629-d2e146709a19.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR</strong> Mermaid won the README and the markdown ecosystem; PlantUML won the engineering whiteboard and the IDE. In 2026 they're still the two serious "diagram as code" formats, and which one you should pick depends on where the diagram lives more than what it shows. This post compares them on syntax, layout, diagram coverage, and ecosystem, ending with a decision rule you can apply in 60 seconds.</p>
</blockquote>
<blockquote>
<p><strong>Disclosure</strong>: I work on a small web editor that's mentioned briefly toward the end. The bulk of the article is about the two open-source formats — the comparison stands on its own, no tool needed.</p>
</blockquote>
<h2>The honest framing</h2>
<p>I've shipped engineering docs with both. Every six months someone on the team asks "should we standardise on Mermaid or PlantUML?" and every six months the answer is "it depends on where the diagram lives."</p>
<p>That answer used to be a cop-out. In 2026 it's the truth. The two formats have converged on the same problem — render diagrams from plain text — and diverged on everything around it: who writes them, where they render, what they look like, and how much patience they expect from you.</p>
<p>This article is the side-by-side I wish someone had handed me three years ago.</p>
<h2>At a glance</h2>
<p>The table you came here for:</p>
<table>
<thead>
<tr>
<th>Dimension</th>
<th>Mermaid</th>
<th>PlantUML</th>
</tr>
</thead>
<tbody><tr>
<td>Native rendering on GitHub</td>
<td>✅ since 2022</td>
<td>❌ (proxied images only)</td>
</tr>
<tr>
<td>Native rendering on Notion</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>Native rendering on GitLab</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>IDE / editor extensions</td>
<td>Broad (VS Code, JetBrains, Obsidian)</td>
<td>Broad, with deeper JetBrains roots</td>
</tr>
<tr>
<td>Layout engine</td>
<td>Dagre (flowcharts), ELK opt-in</td>
<td>Graphviz (<code>dot</code>)</td>
</tr>
<tr>
<td>Supported diagram types</td>
<td>~16</td>
<td>~20+</td>
</tr>
<tr>
<td>Syntax style</td>
<td>Markdown-adjacent</td>
<td>Annotation-heavy</td>
</tr>
<tr>
<td>Server-side renderer</td>
<td>Headless Chromium</td>
<td>Java + Graphviz</td>
</tr>
<tr>
<td>Best at</td>
<td>Flowcharts, sequence, journey</td>
<td>Class, component, deployment, ERD</td>
</tr>
<tr>
<td>Worst at</td>
<td>Dense ERDs, large class diagrams</td>
<td>Anything that needs to live on GitHub</td>
</tr>
</tbody></table>
<p>The rest of this post is the detail under each row.</p>
<h2>Round 1 — Syntax ergonomics</h2>
<p>The same flow, written two ways. Pick which one reads more naturally to you.</p>
<p><strong>Mermaid:</strong></p>
<pre><code class="language-mermaid">flowchart TD
  A[Client] --&gt; B{Token valid?}
  B --&gt;|Yes| C[Fetch user]
  B --&gt;|No| D[Redirect to login]
  C --&gt; E[Return 200]
  D --&gt; F[Return 302]
</code></pre>
<p><strong>PlantUML:</strong></p>
<pre><code class="language-plantuml">@startuml
start
:Client;
if (Token valid?) then (yes)
  :Fetch user;
  :Return 200;
else (no)
  :Redirect to login;
  :Return 302;
endif
@enduml
</code></pre>
<p>Mermaid's syntax leans on <strong>nodes and edges as the primitive</strong>. You say "A points to B with this label" and the layout follows. It reads like a constraint list.</p>
<p>PlantUML's syntax leans on <strong>statements as the primitive</strong>. You say "do this, then this, with this branch" and the layout follows. It reads like pseudocode.</p>
<p>For people who think in flow ("first this happens, then this"), PlantUML's <code>start/if/endif</code> is intuitive. For people who think in graphs ("these are the nodes, these are the edges"), Mermaid is the lower-friction path.</p>
<p>There's no objectively-better here. Pick the one that matches how your team already whiteboards.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/a593beea-e2a3-452a-899f-a28a9262fbf1.svg" alt="" style="display:block;margin:0 auto" />

<p><em>Same auth flow, Mermaid source, rendered into a finished SVG.</em></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/4185e425-515d-4b85-b7a3-1126a84c4674.svg" alt="" style="display:block;margin:0 auto" />

<p><em>Same flow expressed in PlantUML — different syntax, same end shape.</em></p>
<h2>Round 2 — Layout quality</h2>
<p>Layout is where the two diverge the most, and it's the thing nobody talks about until they've shipped a dozen diagrams and realised one of the formats keeps making nodes overlap.</p>
<p><strong>Mermaid uses Dagre</strong> by default. Dagre is a hierarchical layout algorithm — fast, deterministic, and great on flow charts with 5 to 30 nodes. Past that it starts producing the visual you've seen on a thousand READMEs: tall, narrow columns with edges crossing in ways no human would draw.</p>
<p>Mermaid has an <code>elk</code> opt-in (Eclipse Layout Kernel) that's much better at dense graphs. As of 2026 it's still flagged as experimental in most distributions, but it's stable enough to use in production.</p>
<p><strong>PlantUML uses Graphviz</strong> (the <code>dot</code> engine), which has been the gold standard for hierarchical graph layout for 30 years. The output reflects that — PlantUML diagrams routinely look right out of the box, especially as the graph grows past 20 nodes.</p>
<p>For a 6-node flow chart, both produce something readable. For a 60-node class diagram, PlantUML wins by a noticeable margin.</p>
<p>The catch: Graphviz is a native binary. Running PlantUML in a CI job or a serverless function means shipping a Java + Graphviz runtime. Mermaid is JavaScript end-to-end, which is the trade.</p>
<h2>Round 3 — Supported diagram types</h2>
<p>Both cover the basics; PlantUML covers more obscure ones.</p>
<p><strong>Mermaid (2026, native pack):</strong></p>
<ul>
<li><p>Flowchart</p>
</li>
<li><p>Sequence diagram</p>
</li>
<li><p>Class diagram</p>
</li>
<li><p>State diagram</p>
</li>
<li><p>ER diagram</p>
</li>
<li><p>Gantt</p>
</li>
<li><p>Pie</p>
</li>
<li><p>User journey</p>
</li>
<li><p>Quadrant chart</p>
</li>
<li><p>XY chart</p>
</li>
<li><p>Mindmap</p>
</li>
<li><p>Timeline</p>
</li>
<li><p>Sankey</p>
</li>
<li><p>Block diagram</p>
</li>
<li><p>Git graph</p>
</li>
<li><p>C4 (experimental)</p>
</li>
</ul>
<p><strong>PlantUML (mature set):</strong></p>
<ul>
<li><p>All of the above, plus:</p>
</li>
<li><p>Component diagram</p>
</li>
<li><p>Deployment diagram</p>
</li>
<li><p>Activity diagram (legacy + beta)</p>
</li>
<li><p>Use case</p>
</li>
<li><p>Object diagram</p>
</li>
<li><p>Network diagram (<code>nwdiag</code>)</p>
</li>
<li><p>Wireframe (Salt UI mockup)</p>
</li>
<li><p>Archimate</p>
</li>
<li><p>BPMN-like activity flows</p>
</li>
<li><p>Bytefield diagrams</p>
</li>
</ul>
<p>If you're writing a software architecture doc that needs <strong>deployment diagrams or component diagrams</strong> — the boxes-inside-boxes-with-interfaces pattern — PlantUML's coverage is meaningfully wider. Mermaid's <code>block</code> diagram type covers a chunk of this, but it's not at parity.</p>
<p>If you're writing a feature spec with <strong>flowcharts, sequence diagrams, and ER diagrams</strong> — the 80% case for product engineering — both are fine.</p>
<h2>Round 4 — Where it renders without help</h2>
<p>This is the single biggest practical difference, and the reason the answer in 2026 is "it depends on where the diagram lives".</p>
<p><strong>Mermaid renders natively on:</strong></p>
<ul>
<li><p>GitHub READMEs and markdown files (since 2022)</p>
</li>
<li><p>GitLab</p>
</li>
<li><p>Notion code blocks</p>
</li>
<li><p>VS Code markdown preview</p>
</li>
<li><p>Obsidian</p>
</li>
<li><p>Bitbucket Cloud</p>
</li>
<li><p>Most static site generators with a plugin (Docusaurus, VitePress, MkDocs Material, Astro)</p>
</li>
</ul>
<p><strong>PlantUML renders natively on:</strong></p>
<ul>
<li><p>GitLab (snippets + wiki)</p>
</li>
<li><p>JetBrains IDEs (with the official plugin)</p>
</li>
<li><p>Confluence (with a plugin)</p>
</li>
</ul>
<p>Everywhere else, PlantUML rendering means either:</p>
<ol>
<li><p>Pre-rendering to SVG/PNG and committing the image, or</p>
</li>
<li><p>Pointing at a public PlantUML server (<code>www.plantuml.com/plantuml/svg/&lt;encoded&gt;</code>) and accepting that your diagram source is encoded into a third-party URL.</p>
</li>
</ol>
<p>For a GitHub README in 2026, Mermaid is the only format that renders inline without a build step. That's not a small thing — it's the entire reason Mermaid has more momentum.</p>
<p>For an internal Confluence-driven engineering org, PlantUML's class / component / deployment coverage and Graphviz layout makes it the better default.</p>
<h2>Round 5 — Ecosystem and tooling</h2>
<p>Both ecosystems are healthy. They optimise for different surfaces.</p>
<p><strong>Mermaid:</strong></p>
<ul>
<li><p>Maintained by a foundation; weekly releases</p>
</li>
<li><p>Live editor at <code>mermaid.live</code></p>
</li>
<li><p>Official CLI: <code>@mermaid-js/mermaid-cli</code> (uses Puppeteer under the hood)</p>
</li>
<li><p>VS Code extension by the maintainers</p>
</li>
<li><p>Obsidian native support</p>
</li>
<li><p>AI tooling generates Mermaid by default — most LLMs output Mermaid when asked for a diagram in markdown</p>
</li>
</ul>
<p><strong>PlantUML:</strong></p>
<ul>
<li><p>Maintained primarily by Arnaud Roques and a small team; releases are slower but steady</p>
</li>
<li><p>Live editor at <code>www.plantuml.com/plantuml</code></p>
</li>
<li><p>Official tool: <code>plantuml.jar</code> (Java)</p>
</li>
<li><p>JetBrains plugin is best-in-class for the JVM crowd</p>
</li>
<li><p>Confluence integration is a paid plugin but widely deployed</p>
</li>
<li><p>AI tooling support is improving but still trails Mermaid</p>
</li>
</ul>
<p>There's a quieter consequence here. When you ask an LLM for "a diagram of X", you almost always get Mermaid back. That's mostly habit and training-set bias, not a quality call — but it does mean that if your team is leaning on AI-assisted doc writing, Mermaid will show up in your repo whether you picked it or not.</p>
<h2>Round 6 — Learning curve</h2>
<p>The bottom 20% of each language takes about an hour. The remaining 80% is what differs.</p>
<p><strong>Mermaid</strong> has a flat learning curve — the syntax is markdown-adjacent and most people write a working flowchart on their first try. The wall comes when you want to customise — <code>themeVariables</code>, <code>init</code> directives, <code>classDef</code>, link styling — because each diagram type has its own subset of the customisation surface, and the documentation is scattered.</p>
<p><strong>PlantUML</strong> has a steeper start — <code>@startuml</code> / <code>@enduml</code>, statement-based syntax, <code>skinparam</code> overrides — but a flatter middle, because once you've learned <code>skinparam</code> you've learned the customisation surface for every diagram type. The wall comes later, around legacy syntax (the original <code>activity</code> grammar vs the newer <code>start/stop</code> beta).</p>
<p>For a team that ships occasional diagrams, Mermaid's flat start wins. For a team where someone "owns" the diagram system and is going to push it to its limits, PlantUML's consistency wins.</p>
<h2>The decision rule</h2>
<p>Three questions, in order:</p>
<ol>
<li><p><strong>Does the diagram need to render on GitHub or in a markdown file consumed by tools you don't control?</strong> → Mermaid. Stop here.</p>
</li>
<li><p><strong>Does it need to be a deployment, component, or detailed class diagram with 30+ nodes?</strong> → PlantUML. Stop here.</p>
</li>
<li><p><strong>Anything else?</strong> → Mermaid for the lower install footprint and broader AI tooling; PlantUML if your team is already on JetBrains + Confluence.</p>
</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/a87edd33-8b48-4808-8622-6b77450c96c0.svg" alt="" style="display:block;margin:0 auto" />

<p><em>The 60-second decision rule.</em></p>
<p>Most engineering teams in 2026 end up using <strong>Mermaid for repo-level docs and PlantUML for architecture documents that live in Confluence or a wiki.</strong> That's not a compromise — it's the right answer. The cost of standardising on one format and forcing the wrong fit is higher than the cost of two file extensions in your repo.</p>
<hr />
<blockquote>
<p><strong>A small recommendation (disclosed at the top)</strong></p>
<p>The tool I work on, <a href="https://www.beauty-diagram.com">Beauty Diagram</a>, parses Mermaid, PlantUML, and draw.io with the same theme set. Paste either format, pick from a fixed set of production themes (<code>classic</code>, <code>modern</code>, <code>atlas</code>, <code>blueprint</code>, <code>memphis</code>, <code>obsidian</code>, <code>slate</code>, <code>brutalist</code>, <code>atelier</code>), then export SVG or PNG — handy when your docs repo has both formats and you want them to look consistent.</p>
<p>The same pipeline runs from a CLI for CI use:</p>
<pre><code class="language-bash"># Works on both Mermaid and PlantUML files
npx @beauty-diagram/cli beautify auth.mmd --theme atlas --out auth.svg
npx @beauty-diagram/cli beautify components.puml --theme atlas --out components.svg
</code></pre>
<p><code>bd themes</code> lists the available themes and there's no per-property style override — the theme is the choice; you pick a different one if you don't like what you see. <a href="https://www.beauty-diagram.com/editor">→ Try the editor</a> if you want to compare both formats side by side.</p>
</blockquote>
<hr />
<h2>Wrap-up</h2>
<p>The 60-second version:</p>
<ul>
<li><p><strong>Mermaid wins</strong> if the diagram lives in a README, a markdown file, a Notion page, or anything you don't fully control. It also wins if you're heavy on AI-assisted doc writing.</p>
</li>
<li><p><strong>PlantUML wins</strong> if the diagram is a deployment / component / detailed class diagram, or if your team is centred on JetBrains + Confluence.</p>
</li>
<li><p><strong>Both is fine.</strong> Pick the format per-diagram, not per-org.</p>
</li>
</ul>
<p>Most engineering teams in 2026 end up with a mix — Mermaid for repo-level docs that need to render anywhere, PlantUML for the architecture documents that already live in Confluence. The cost of running two formats is lower than the cost of forcing the wrong fit.</p>
]]></content:encoded></item><item><title><![CDATA[Why Your GitHub README Diagrams Look Amateur (and the 4 Fixes That Take 10 Minutes)]]></title><description><![CDATA[TL;DR
If your README diagrams look like every other open-source repo's, that's because they all use the same four Mermaid defaults. Switching to theme: 'base' plus tuning four properties takes under t]]></description><link>https://beauty-diagram.hashnode.dev/why-your-github-readme-diagrams-look-amateur</link><guid isPermaLink="true">https://beauty-diagram.hashnode.dev/why-your-github-readme-diagrams-look-amateur</guid><category><![CDATA[mermaid]]></category><category><![CDATA[opinion]]></category><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[Levi Liu]]></dc:creator><pubDate>Thu, 21 May 2026 18:26:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/3f7fb435-1e7d-4026-818a-747ed9a801f6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR</strong>
If your README diagrams look like every other open-source repo's, that's because they all use the same four Mermaid defaults. Switching to <code>theme: 'base'</code> plus tuning four properties takes under ten minutes and produces a diagram that looks intentional. This is a follow-up to last week's <a href="https://www.beauty-diagram.com/blog/render-mermaid-as-svg">SVG rendering deep-dive</a>.</p>
</blockquote>
<blockquote>
<p><strong>Disclosure</strong>: I work on a small web editor that's mentioned briefly toward the end. The bulk of the article is open-source-focused — the four fixes work standalone, no tool needed.</p>
</blockquote>
<h2>The four defaults that ruin your diagrams</h2>
<ol>
<li><strong>Pastel pink/blue palette</strong> — the <code>default</code> theme is from a 2014 Bootstrap-era moodboard. It signals "I copy-pasted from the docs."</li>
<li><strong>Curved edges (<code>basis</code>)</strong> — works for organic flows, terrible for technical diagrams. Lines that should be crisp end up wavy.</li>
<li><strong>Default font</strong> — varies wildly by viewer. Looks fine in the GitHub editor preview, looks like Times New Roman in your CI artifact.</li>
<li><strong>Hardcoded background</strong> — light theme on a dark page, or vice versa. The diagram becomes a rectangle.</li>
</ol>
<p>Each takes one config change to fix.</p>
<h2>Fix 1: Switch to base theme</h2>
<pre><code class="language-mermaid">%%{init: {'theme':'base'}}%%
flowchart LR
  A --&gt; B --&gt; C
</code></pre>
<p><code>base</code> is the only theme that exposes <code>themeVariables</code> for full control.</p>
<h2>Fix 2: Set six theme variables</h2>
<pre><code>%%{init: {
  'theme':'base',
  'themeVariables': {
    'primaryColor': '#1e293b',
    'primaryTextColor': '#f8fafc',
    'primaryBorderColor': '#475569',
    'lineColor': '#94a3b8',
    'fontFamily': 'system-ui, sans-serif',
    'fontSize': '14px'
  }
}}%%
</code></pre>
<p>Six variables. That's the entire customisation surface you actually need. Mermaid exposes around fifty in total, but the ones above cover 90% of the visual identity of a diagram. Ignore the rest unless you have a specific reason.</p>
<h2>Fix 3: Linear edges</h2>
<pre><code>%%{init: {'flowchart': {'curve': 'linear'}}}%%
</code></pre>
<p>For hierarchies, use <code>step</code>. For organic flows, keep <code>basis</code>. For everything else, <code>linear</code>.</p>
<h2>Fix 4: Light/dark adaptive embed</h2>
<pre><code class="language-html">&lt;picture&gt;
  &lt;source media="(prefers-color-scheme: dark)" srcset="./diagrams/auth-flow-dark.svg"&gt;
  &lt;img alt="Auth flow" src="./diagrams/auth-flow-light.svg"&gt;
&lt;/picture&gt;
</code></pre>
<p>GitHub READMEs respect <code>prefers-color-scheme</code>. Ship two SVGs and let GitHub pick.</p>
<p>A caveat worth flagging: this only works for <strong>pre-rendered SVGs</strong> that you commit to the repo. Inline <code>mermaid</code> code blocks in your README can't use <code>&lt;picture&gt;</code> — they're rendered by GitHub's own Mermaid runtime at view time, with no hook for swapping based on theme. If you want adaptive diagrams in a README, you have to render to SVG first.</p>
<h2>Putting all four together</h2>
<p>Here's the minimum viable beautified README diagram, end to end:</p>
<pre><code class="language-mermaid">---
config:
  theme: base
  themeVariables:
    primaryColor: '#1e293b'
    primaryTextColor: '#f8fafc'
    primaryBorderColor: '#475569'
    lineColor: '#94a3b8'
    fontFamily: 'system-ui, sans-serif'
    fontSize: '14px'
  flowchart:
    curve: linear
---
flowchart LR
  A[Client] --&gt; B{Auth?}
  B --&gt;|Yes| C[API]
  B --&gt;|No| D[Login]
  C --&gt; E[(Database)]
</code></pre>
<p>The frontmatter form (<code>---\nconfig:\n  ...</code>) is Mermaid v11's preferred syntax. The older <code>%%{init: {...}}%%</code> directive still works and is what you'll see in most existing blog posts — pick whichever your renderer is happy with.</p>
<h2>When DIY stops being worth the time</h2>
<p>Everything above works. I've shipped this exact pipeline.</p>
<p>The moment you have more than a handful of diagrams, though — or you want a different look for slides vs. docs vs. PRs — you're maintaining a config file. And every new contributor to the repo needs to know about it. That's where I personally tap out.</p>
<p>Three signals it's time to graduate from hand-tuned themes:</p>
<ol>
<li>You're copy-pasting the same <code>themeVariables</code> block across multiple repos.</li>
<li>You want a different look for a slide deck than for the README, but the diagram source stays the same.</li>
<li>Someone non-technical on the team needs to make a diagram and you don't want to teach them YAML frontmatter.</li>
</ol>
<hr />
<blockquote>
<p><strong>A small recommendation (disclosed at the top)</strong></p>
<p>The tool I work on, <a href="https://www.beauty-diagram.com">Beauty Diagram</a>, is a web editor for Mermaid, PlantUML, and draw.io. You paste your source, pick from a fixed set of production themes (<code>classic</code>, <code>modern</code>, <code>atlas</code>, <code>blueprint</code>, <code>memphis</code>, <code>obsidian</code>, <code>slate</code>, <code>brutalist</code>, <code>atelier</code>), and export SVG or PNG. No <code>themeVariables</code> JSON to maintain.</p>
<p>If you'd rather render light + dark from the same source for a <code>&lt;picture&gt;</code> embed:</p>
<pre><code class="language-bash">npx @beauty-diagram/cli beautify flow.mmd --theme modern   --out flow-light.svg
npx @beauty-diagram/cli beautify flow.mmd --theme obsidian --out flow-dark.svg
</code></pre>
<p>That's the entire surface area for theme tuning — you pick a theme, not properties. Some people will find that limiting; most will find it freeing.</p>
</blockquote>
<hr />
<h2>Wrap-up</h2>
<p>The README-diagram embarrassment problem is fixable in ten minutes. The four fixes, recapped:</p>
<ul>
<li><code>theme: 'base'</code> — only theme that accepts overrides.</li>
<li>Six <code>themeVariables</code> — <code>primaryColor</code>, <code>primaryTextColor</code>, <code>primaryBorderColor</code>, <code>lineColor</code>, <code>fontFamily</code>, <code>fontSize</code>.</li>
<li><code>flowchart.curve: linear</code> — sharp edges for technical diagrams.</li>
<li><code>&lt;picture&gt;</code> + <code>prefers-color-scheme</code> — adaptive light/dark, GitHub-native.</li>
</ul>
<p>Most repos haven't done it because nobody told them where to look.</p>
]]></content:encoded></item><item><title><![CDATA[How to Render Mermaid Diagrams as SVG (and Stop Looking Like Every Other README)]]></title><description><![CDATA[TL;DR
Mermaid renders to SVG natively — but the default output usually isn't what you want in a README, slide, or doc. This post walks through the official mmdc CLI, the four theming knobs that actual]]></description><link>https://beauty-diagram.hashnode.dev/how-to-render-mermaid-diagrams-as-svg-and-stop-looking-like-every-other-readme</link><guid isPermaLink="true">https://beauty-diagram.hashnode.dev/how-to-render-mermaid-diagrams-as-svg-and-stop-looking-like-every-other-readme</guid><category><![CDATA[mermaid]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[documentation]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Levi Liu]]></dc:creator><pubDate>Thu, 14 May 2026 06:42:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a00b0ece3eebc2e209fcaba/367b91cc-20a7-4fb8-a521-f8845034d130.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR</strong>
Mermaid renders to SVG natively — but the default output usually isn't what you want in a README, slide, or doc. This post walks through the official <code>mmdc</code> CLI, the four theming knobs that actually matter, the three rendering pitfalls everyone hits (fonts, viewBox, dark mode), and the fastest path when you don't want to fight with config.</p>
</blockquote>
<blockquote>
<p><strong>Disclosure</strong>: I work on a small web editor that is mentioned briefly toward the end of this post. The bulk of the article is open-source-focused — feel free to skip the recommendation if it's not useful to you.</p>
</blockquote>
<h2>The problem nobody talks about</h2>
<p>Mermaid is the de-facto "diagrams as code" choice in 2026 — GitHub, Notion, Obsidian, Docusaurus, GitLab, every static site generator worth its salt renders it natively.</p>
<p>That's the good news.</p>
<p>The bad news: 95% of Mermaid diagrams in the wild look exactly the same. Pastel pinks and blues, Comic-Sans-adjacent fonts, edges crossing nodes, arrowheads that don't quite point at anything. You've seen them. You've probably shipped some.</p>
<p>It's not a Mermaid problem — it's a defaults problem. The renderer is optimised for "something showed up", not "this is presentable".</p>
<h2>Why SVG and not PNG</h2>
<table>
<thead>
<tr>
<th>Property</th>
<th>SVG</th>
<th>PNG</th>
</tr>
</thead>
<tbody><tr>
<td>Scales without blur</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>Accessible (text selectable)</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>Adapts to dark mode (<code>currentColor</code>)</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>Searchable in your repo</td>
<td>✅</td>
<td>❌</td>
</tr>
</tbody></table>
<h2>Approach 1: The official Mermaid CLI</h2>
<pre><code class="language-bash"># Run without installing
npx @mermaid-js/mermaid-cli -i diagram.mmd -o diagram.svg

# Or install globally
npm install -g @mermaid-js/mermaid-cli
mmdc -i diagram.mmd -o diagram.svg
</code></pre>
<h2>The four theming knobs that actually matter</h2>
<h3>1. <code>theme</code> (the cheapest win)</h3>
<p>Built-in Mermaid themes: <code>default</code>, <code>forest</code>, <code>dark</code>, <code>neutral</code>, <code>base</code>. Pick <code>base</code> if you want to customise from scratch.</p>
<h3>2. <code>themeVariables</code></h3>
<p>Six variables actually matter: <code>primaryColor</code>, <code>primaryTextColor</code>, <code>primaryBorderColor</code>, <code>lineColor</code>, <code>fontFamily</code>, <code>fontSize</code>. Set those well, ignore the other 50.</p>
<h3>3. Edge curves</h3>
<p>Change edge curves from the default to <code>linear</code> or <code>step</code>. The default <code>basis</code> everywhere is one of the top reasons README diagrams look amateur.</p>
<h3>4. Layout direction</h3>
<p>Pick based on reading flow. <code>LR</code> for pipelines, <code>TD</code> for hierarchies.</p>
<h2>Three rendering pitfalls</h2>
<h3>Fonts don't render in the saved SVG</h3>
<p>Use a system font stack: <code>-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif</code>.</p>
<h3>viewBox cropping</h3>
<p>Post-process to add padding:</p>
<pre><code class="language-javascript">function padViewBox(svgString, padding = 16) {
  return svgString.replace(
    /viewBox="(-?\d+\.?\d*) (-?\d+\.?\d*) (\d+\.?\d*) (\d+\.?\d*)"/,
    (_, x, y, w, h) =&gt; {
      const nx = parseFloat(x) - padding;
      const ny = parseFloat(y) - padding;
      const nw = parseFloat(w) + padding * 2;
      const nh = parseFloat(h) + padding * 2;
      return `viewBox="\({nx} \){ny} \({nw} \){nh}"`;
    }
  );
}
</code></pre>
<h3>Dark mode is hardcoded</h3>
<p>Replace fixed colours with <code>currentColor</code>:</p>
<pre><code class="language-javascript">function makeAdaptive(svgString) {
  return svgString
    .replace(/stroke="#[0-9a-fA-F]{6}"/g, 'stroke="currentColor"')
    .replace(/fill="#[0-9a-fA-F]{6}"/g, 'fill="currentColor"');
}
</code></pre>
<h2>When DIY stops being worth the time</h2>
<p>Everything above works. I've shipped this exact pipeline.</p>
<p>It also takes about a day of fiddling to get right, and the result is one diagram style that you maintain forever. The moment you want a different style for a different context — a slide deck vs. a README vs. a postmortem — or someone non-technical on the team wants to make a diagram, the cost-benefit shifts.</p>
<p>Three signals it's time to stop hand-rolling:</p>
<ol>
<li>You're maintaining a "diagram theme" file across multiple repos.</li>
<li>You want a different look for slides vs. docs vs. PRs, but the diagram source stays the same.</li>
<li>Your inputs aren't just Mermaid — PlantUML, draw.io, AI prompts.</li>
</ol>
<hr />
<blockquote>
<p><strong>A small recommendation (disclosed at the top)</strong></p>
<p>The tool I work on, <a href="https://www.beauty-diagram.com">Beauty Diagram</a>, is a web editor for Mermaid, PlantUML, and draw.io. You paste your source, pick from a fixed set of production themes (<code>classic</code>, <code>modern</code>, <code>atlas</code>, <code>blueprint</code>, <code>memphis</code>, <code>obsidian</code>, <code>slate</code>, <code>brutalist</code>, <code>atelier</code>), and export SVG or PNG. No config files, no <code>themeVariables</code> JSON to maintain.</p>
<p>If you'd rather automate, the same renderer is exposed as a CLI:</p>
<pre><code class="language-bash">npx @beauty-diagram/cli beautify flow.mmd --theme modern --out flow.svg
</code></pre>
<p>That's the entire surface area for theme tuning — you pick a theme, not properties. Some people will find that limiting; most will find it freeing.</p>
</blockquote>
<hr />
<h2>Wrap-up</h2>
<p>Mermaid's defaults aren't bad — they're designed for "the diagram exists" rather than "the diagram is finished". The fixes break down into roughly three tiers:</p>
<ul>
<li><strong>Cheap and fine for one style</strong>: switch <code>theme: 'base'</code>, set six theme variables, change edge curves, run a viewBox-padding regex.</li>
<li><strong>Annoying past one repo</strong>: maintain that config across many surfaces.</li>
<li><strong>Use a tool</strong>: paste, pick, export.</li>
</ul>
<p>Where you land depends on how much you care about diagrams and how often you ship them.</p>
]]></content:encoded></item><item><title><![CDATA[A Few Things I Learned Drawing Diagrams in Mermaid and PlantUML]]></title><description><![CDATA[Quick field notes from a few years of writing engineering docs with both tools. Nothing groundbreaking — just things I wish I'd known earlier.

I write a lot of architecture and flow diagrams for engi]]></description><link>https://beauty-diagram.hashnode.dev/a-few-things-i-learned-drawing-diagrams-in-mermaid-and-plantuml</link><guid isPermaLink="true">https://beauty-diagram.hashnode.dev/a-few-things-i-learned-drawing-diagrams-in-mermaid-and-plantuml</guid><category><![CDATA[mermaid]]></category><category><![CDATA[plantuml]]></category><category><![CDATA[documentation]]></category><category><![CDATA[engineering]]></category><dc:creator><![CDATA[Levi Liu]]></dc:creator><pubDate>Wed, 13 May 2026 05:22:16 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>Quick field notes from a few years of writing engineering docs with both tools. Nothing groundbreaking — just things I wish I'd known earlier.</p>
</blockquote>
<p>I write a lot of architecture and flow diagrams for engineering docs. Most of them go into READMEs, design docs, and RFCs that nobody opens again after the first review pass — which is part of why getting them right the first time matters: there's rarely a second chance.</p>
<p>Over the past couple of years I've drifted between Mermaid and PlantUML enough times to develop opinions. None of these are universal truths — just what I've found useful.</p>
<h2>When I reach for which</h2>
<p><strong>Mermaid</strong> for anything that lives in a Markdown file. GitHub, GitLab, Notion, Obsidian, Docusaurus — every editor and static site generator I've used renders Mermaid natively. The syntax is friendlier for quick sketches. If I'm writing a doc and want a diagram inline, it's almost always Mermaid.</p>
<p><strong>PlantUML</strong> when I need an activity diagram with swim lanes, or a sequence diagram with detailed lifelines and notes. The grammar is more verbose but it handles dense, formal diagrams better than Mermaid does today. The catch: most Markdown editors don't render it natively — you usually need a plugin or a build step that turns the source into an image.</p>
<p>For everything else (flowcharts, state machines, simple sequence diagrams) either works. I default to Mermaid because of the rendering ubiquity.</p>
<h2>The defaults problem</h2>
<p>The thing both tools share is that their default rendering output is... <em>fine</em>. Functional. It exists. It also looks like every other diagram on the internet, because most people don't touch the defaults.</p>
<p>I don't think this is the tools' fault. It's a defaults problem — the rendering is optimised for "the diagram exists" rather than "the diagram is presentable." Once you notice it, you start seeing the same pastel-on-white look in every README you read.</p>
<p>Three defaults I now override almost reflexively:</p>
<h2>1. The pastel theme</h2>
<p>Mermaid's default theme is from a 2014-era moodboard. The pinks and blues read as "I copy-pasted from the docs" rather than "I made an editorial choice." Switching to <code>theme: 'base'</code> and setting a handful of theme variables gives you a clean technical look in about two minutes:</p>
<pre><code>%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#1e293b',
    'primaryTextColor': '#f8fafc',
    'lineColor': '#94a3b8',
    'fontFamily': 'system-ui'
  }
}}%%
</code></pre>
<p>PlantUML has the same trap — its default skin is a slightly more muted version of the same issue. <code>skinparam backgroundColor</code> and <code>skinparam defaultFontName</code> get you most of the way there.</p>
<h2>2. Curved edges everywhere</h2>
<p>Mermaid's flowchart edges default to a curve type called <code>basis</code>. It looks slightly organic, which is great for actual organic things like a mind map and bad for everything else. Most technical diagrams read cleaner with straight or right-angle edges:</p>
<pre><code>%%{init: {'flowchart': {'curve': 'linear'}}}%%
</code></pre>
<p>If you've ever looked at a README diagram and felt vaguely like it was from 2008, the wavy edges are usually why.</p>
<h2>3. The viewBox crop</h2>
<p>Both Mermaid and PlantUML occasionally clip an arrowhead or a long node label at the very edge of the SVG, because their layout calculation doesn't pad enough. This is particularly common when a node has a long title or when an edge label contains a comma.</p>
<p>For Mermaid, the quickest fix is a tiny post-processing step that bumps the SVG's <code>viewBox</code> outward by a few pixels:</p>
<pre><code class="language-javascript">svg.replace(
  /viewBox="(-?\d+) (-?\d+) (\d+) (\d+)"/,
  (_, x, y, w, h) =&gt;
    `viewBox="\({+x - 16} \){+y - 16} \({+w + 32} \){+h + 32}"`
);
</code></pre>
<p>For PlantUML you can usually adjust <code>skinparam padding</code> instead.</p>
<h2>Closing thoughts</h2>
<p>None of these are revelations — they're things any sufficiently determined reader of the docs would eventually find. But none of them are part of the default experience either, which means most diagrams in the wild never get them.</p>
<p>If you write a lot of diagrams, taking a few minutes to set up your own defaults — your theme variables, your preferred edge curve, your padding override — pays itself back the first time you ship a doc and someone says <em>"oh, that looks clean."</em></p>
<p>What's your own diagram default-override? Curious what other people standardise on.</p>
]]></content:encoded></item></channel></rss>