The coal mine

A personal blog for devtime stories.


Site maintained by Ayowel RSS

Adding mermaids to GitHub Pages

Or how to add graphs to a technical blog on GitHub Pages

Considerations for graphs in GitHub Pages’ Jekyll

I’ve contemplated creating a technical blog for years and finally decided that I would do it two weeks ago. Due to the fact that I already used it for other projects, the absence of need for a hosting plan, and the ease of the transition to a self-hosted website should I elect to do so, I created it as a Jekyll site on GitHub Pages. The only issue was the generation of graphs, which are numerous in technical posts, as I wanted to easily see what they contained and be able to update them from the command-line, thus making raw images unsuitable.

The PlantUML alternative

The graph generation tool I often use in projects is PlantUML, a well-known server-side graph generation tool with integrations in most markup languages and tools. If you’re interested in it, you may want to play with the live online editor. Here is a quick example where Bob says “hello” to Alice:

@startuml
Bob -> Alice : hello
@enduml

Bob says hello to Alice in PlantUML

Adding PlantUML support to Jekyll is pretty easy on administered servers thanks to the jekyll-plantuml rubygem, which may be added as a dependency, however this gem is not part of the gem whitelist that may be used on GitHub Pages Jekyll servers. Additionally, the use of the online editor to generate images on-the-fly requires to encode the source in base-64, which is not possible on-the-fly without using Jekyll plugins.

An option might have been to statically encode the source graph and use the online editor to generate the images. An implementation of this setup for GitHub pages would consist in the creation of a file named _includes/plantuml.html that would be included with the base-64 encoded version of the graph to display everywhere a graph was needed. The most obvious limitation of this method is that it makes the graph unreadable in Markdown1. The following is an example where the PlantUML code example above would be included in a page:

<!-- _includes/plantuml.html -->
<img src="https://www.plantuml.com/plantuml/png/{{ include.source }}" />
Here is a quick example of the format where Bob says "hello" to Alice:
{% include plantuml.html source="SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80" %}

This last option worked, but was unsatisfactory and I looked for an other way to support graphs.

The Mermaid alternative

I recently discovered Mermaid, a well-known client-side graph generation tool with integrations in most markup languages and tools. If you’re interested in it, you may want to play with the live online editor. Here is a quick example where Bob says “hello” to Alice:

sequenceDiagram
    Bob->>+Alice: hello

Bob says hello to Alice in Mermaid

Much like PlantUML, Mermaid integration in Jekyll on administered servers is pretty easy thank to rubygems (see jekyll-mermaid or jekyll-mermaid-diagrams). Much like PlantUML, none of Mermaid’s rubygem are part of the GitHub Pages gem whitelist, hence another way to use it is required.

The most straightforward way to add Mermaid to Jekyll without using plugins is to replicate what plugins do: load the mermaid JavaScript2 and encapsulate the mermaid code in a HTML element with the class mermaid3.

How I integrated mermaid

To add support for Mermaid, I created the following include file:

<!-- _includes/mermaid.html -->
<script async src="{{ site.mermaid.src }}"></script>
<div class="mermaid">
{{ include.source | xml_escape }}
</div>

This file is heavily based on the code you may find in plugins. Still, there are a few things to note:

Using this include file works as follows:

{% capture demo_diagram %}
sequenceDiagram
    Bob->>+Alice: hello
{% endcapture %}
{% include mermaid.html source=demo_diagram %}

One important ‘detail’ is the use of the capture tag, which allows to store a multiline string in a variable. Jekyll plugins use custom tags to accomodate the multiline configuration of graphs ; this is not possible when using include, hence the use of an intermediate capture tag.

And here is the result:

sequenceDiagram Bob->>+Alice: hello

Afterword

I’m pretty happy with how mermaid can work in the browser even in a restricted environment such as GitHub Pages. I’m a bit annoyed by the fact that I will have to use a capture tag each time I want to insert a graph, but that’s an acceptable limitation to me.

  1. It also makes the graph a pain to maintain as an external editor is required to know what changed if anything did, and binds the validity of a graph to the implementation provided by an uncontrolled server which may introduce breaking format changes someday. 

  2. Plugins do it wherever a mermaid graph is used in a document’s body, and I do the same at the time of writing, however loading it in the header in a layout file is also an option. 

  3. Mermaid automatically scans a document and looks for elements with the mermaid class to generate the matching graphs when it is loaded. 

  4. At the time of writing, the file loaded is the version 9.3.0 of mermaid available on the Mermaid CDN.