はじめに

このブログは、以下のワークフローで運用されています。

  graph LR
  org-mode -- <a href="https://ox-hugo.scripter.co/">ox-hugo</a> --> markdown -- <a href="https://gohugo.io/">Hugo</a> --> HTML

上のようなフローチャートなどのダイアグラムを載せようとすると、どのような方法が思い浮かぶでしょうか?

まさか、パワポ等で作ったダイアグラムを画像としてエクスポートして…なんて方法は考えていませんよね?

それ、Dティアです

図1: パワポはまじでDティア

図1: パワポはまじでDティア

それはさておき、私はダイアグラムを作る方法について詳しくなかったので、ox-hugo を使うという前提で Gemini 2.5 Pro に、Deep research してもらいました。 結果、以下のようなTier Listができました。1

表 1: ox-hugo でのダイアグラム作成方法のTier List by Gemini 2.5 Pro & me
S
HugoレンダーフックによるMermaid.js
A
Org Babel + Hugo Kroki
純粋なOrg Babel実行 (PlantUML/Ditaa)
B
GUIツール (Excalidraw等) で作成し、SVG/PNG画像として挿入
レガシーなHugoショートコード
C
iFrame埋め込み
アスキーアート (ditaa, GoAT)
D
🤮 M社製品 (PowerPoint, Visio)で作成+バイナリ挿入 🤮

表で示しているように、個人的にはmermaid.js を使うのがベストです。 しかし、方法がそれほどシンプルではなく、 少し面倒だったので、書き残しておきます。

mermaid.js を使う2つの方法

  flowchart TD
    B{君はマーメイダー?} -- Yes --> C{ブラウザ上でレンダリングしたい?}
    B -- No --> D[<a href="https://www.microsoft.com/ja-jp/microsoft-365/powerpoint">Dティアです</a>]
    C -- Yes --> E[<a href="#方法2-hugoのレンダーフックでmermaid-dot-jsを使う">方法2</a>]
    C -- No --> F[<a href="#方法1-org-babel-でpng-svgを生成">方法1</a>]

方法1: org-babel でpng/svgを生成

準備

  1. mermaid-cli をインストール
  2. emacs にob-mermaid をインストールし、READMEに従って設定

使い方

以下のようなorg-babelのブロックでmermaid のコードを実行すると、出力された画像ファイルがhugoのコンテンツに挿入されます。

ソース (org-mode)
#+begin_src mermaid :file ./images-in-content/mermaid-sample.svg :results file :exports results
graph LR;
    A--> B & C & D
    B--> A & E
    C--> A & E
    D--> A & E
    E--> B & C & D
<#+end_src

#+RESULTS:
[[file:./images-in-content/mermaid-sample.svg]]
結果 (hugo)

方法2: Hugoのレンダーフックでmermaid.jsを使う

方法2では、レンダーフックを使って、mermaid.js を直接ブラウザ上でレンダリングします。 この方法の利点は、画像ファイルを生成する必要がなく、コードを直接書くだけでダイアグラムを表示できることです。

準備

  1. Hugo のドキュメント に従い、 layouts/_markup/render-codeblock-mermaid.html を作成し、以下を記述
          <pre class="mermaid">
          {{ .Inner | htmlEscape | safeHTML }}
          </pre>
    
  2. layouts/partials/extend_head.html に以下を追加
          {{ if .Store.Get "hasMermaid" }}
            <script type="module">
              import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs';
              mermaid.initialize({ startOnLoad: true });
            </script>
          {{ end }}
    
    もし、将来のライブラリの変更でサイトが壊れるのを防ぎたい場合は、URL を 'https://cdn.jsdelivr.net/npm/[email protected]/dist/mermaid.esm.min.mjs' のようにバージョンを指定することもできます。

使い方

ソース (org-mode)
#+begin_src mermaid :exports code
graph LR;
    A--> B & C & D
    B--> A & E
    C--> A & E
    D--> A & E
    E--> B & C & D
<#+end_src
結果 (hugo)
  graph LR;
    A--> B & C & D
    B--> A & E
    C--> A & E
    D--> A & E
    E--> B & C & D

個人的には、

  1. ローカルにファイルが生成さない
  2. テーマをhugo に合わせられる (やり方は 補足 を参照)
  3. ハイパーリンクなどのHTMLの機能が使える

という3つの理由から、方法2をおすすめします。

(補足)

(補足1) うまくいかなかった方法

ox-hugo のドキュメント にあるように

#+begin_mermaid
flowchart TD
    Start --> Stop
#+end_mermaid

のように書くと、markdownに変換される際に

<div class="mermaid">

flowchart TD

    Start --> Stop

</div>

のように変換されます。 これは、期待していたコードフェンスではありません。 結果的に以下のようなHTMLが生成され、 Syntax error in text というエラーが出てしまいます。

<div class="mermaid"></p><p>flowchart TDA &ndash;&gt; B{Is it working?}B &ndash; Yes &ndash;&gt; C[Great!]B &ndash; No &ndash;&gt; D[Check Console]</p></div>

#+begin_mermaid を使うやり方について、自分はこれ以上深入りしませんでしたが、もしうまくいった人がいれば、ぜひ教えてください。

(補足2) mermaid を真ん中に配置する

自分はデフォルトだと左寄せになってしまうので、以下のようにCSSを追加して中央寄せにしています。

#+begin_export hugo
<style>
.mermaid {
    text-align: center;
}
</style>
#+end_export

(補足3) light/dark モードの切り替えに応じてmermaid.jsのテーマを変更する

layouts/partials/extend_head.html のスクリプトを以下のように変更することで、dark/light モードに応じて mermaid.js のデフォルトテーマを変更できます。

{{ if .Store.Get "hasMermaid" }}
  <script type="module">
    import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs';
    function updateMermaidTheme() {
      let mermaidTheme = 'default';
      if (localStorage.getItem("pref-theme") === "dark" ||
          (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches &&
           !localStorage.getItem("pref-theme"))) {
        mermaidTheme = 'dark';
      }
      mermaid.initialize({
        startOnLoad: true,
        theme: mermaidTheme
      });
    }
    updateMermaidTheme();
  </script>
{{ end }}

まとめ

本記事では、ox-hugo で mermaid.js を使う2つの方法について解説しました。

どちらの方法も、ox-hugo のコンテンツにダイアグラムを組み込むことができますが、個人的にはレンダーフックを使う方法をおすすめします。

以上、ox-hugo で mermaid.js を使う方法について解説しました。


  1. 本当のことを言うと、Gemini 2.5 Pro は"Hugo Kroki"をSティアに、“mermaid.js"をAティアにしてきたんですが、コイツまじなんも分かってないので交換しました ↩︎