はじめに
このブログは、以下のワークフローで運用されています。
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ティア
それはさておき、私はダイアグラムを作る方法について詳しくなかったので、ox-hugo を使うという前提で Gemini 2.5 Pro に、Deep research してもらいました。 結果、以下のようなTier Listができました。1
表で示しているように、個人的には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を生成
準備
- mermaid-cli をインストール
- 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 を直接ブラウザ上でレンダリングします。 この方法の利点は、画像ファイルを生成する必要がなく、コードを直接書くだけでダイアグラムを表示できることです。
準備
- Hugo のドキュメント に従い、
layouts/_markup/render-codeblock-mermaid.html
を作成し、以下を記述<pre class="mermaid"> {{ .Inner | htmlEscape | safeHTML }} </pre>
layouts/partials/extend_head.html
に以下を追加もし、将来のライブラリの変更でサイトが壊れるのを防ぎたい場合は、URL を{{ 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 }}
'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
個人的には、
- ローカルにファイルが生成さない
- テーマをhugo に合わせられる (やり方は 補足 を参照)
- ハイパーリンクなどの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 –> B{Is it working?}B – Yes –> C[Great!]B – No –> 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 を使う方法について解説しました。
-
本当のことを言うと、Gemini 2.5 Pro は"Hugo Kroki"をSティアに、“mermaid.js"をAティアにしてきたんですが、コイツまじなんも分かってないので交換しました ↩︎