Skip to main content

Labarum: A Mermaid's Tale

Mermaid daddy

One of the changes that version .117 of Hugo that has been allowed onto Micro.blog is the ability to use diagrams in your markdown. The documentation on diagrams highlights two different kinds of diagrams that you can include; GoAT and Mermaid.

When you make a diagram using GoAT, Hugo translates this into a SVG on the server side and then delivers it with the HTML for that particular post. You can find some really complicated examples of them on the GitHub page for GoAT and I’ve included one below to demonstrate.

P S I R T N O A P C R U E T T S S E N D A C P H R O O I C C E E S S B C P O R M O X P C L E E S X S P R E P A R A T I O N X

Mermaid is a diagramming and charting tool that uses javascript and will load different libraries as needed to create the final product. I believe that it can be used to make static deliverables that you could save, but -according to my understanding- that’s a separate module and outside what Micro.blog and most installations require.

In addition, Hugo does not include the connections to Mermaid, and it’s up to the theme developers to make sure this works.

When you get it working, you can have diagrams like the one below.

sequenceDiagram
    participant Alice
    participant Bob
    Alice->>John: Hello John, how are you?
    loop Healthcheck
        John->>John: Fight against hypochondria
    end
    Note right of John: Rational thoughts 
prevail! John-->>Alice: Great! John->>Bob: How about you? Bob-->>John: Jolly good!

The Mystery of the Mermaid

I had to ask myself why am I putting the time into getting this feature working in my theme. At first glance, the goals of accessibility and speed do not seem to align with adding diagrams built with third party JavaScript framework. I’ve run this site for years and have used tools to create static images that I serve with alt text to illustrate my point.

Ultimately, it’s because I love DIAGRAMS!

I like being able to have a picture that goes with my posts. I don’t feel that I will be using a lot of these, but I want to help those who do.

And with all of my labarum posts, feel free to take what you want to make your themes better.

Splashing around with the Mermaid

I want to start off this section by stating that a lot of what I’m going to write about is duplication of the documentation found online. This is me documenting my process of applying it and my thought process at the time.

My first stop on implementing this is to open up a browser window to duckduckgo and then opening the first 3-5 search results of Hugo mermaid diagram from DuckDuckGo before reading the Hugo Mermaid documentation. I do this to get a feel of what developer roadblocks and experiences that I can note before reading the documentation. It’s similar to where I would read the discussion questions the teacher gives for assigned reading back in school.

The step of creating the layouts/_default/_markup/render-codeblock-mermaid.html is straight forward1.

<pre class="mermaid">
{{ .Inner | safeHTML }}
</pre>
{{ .Page.Store.Set "hasMermaid" true }}

The first line will go to the resultant HTML, and mermaid will look for items that have the mermaid class to get instructions on what kind of image needs to create.

The second line passes whatever was in the code block. This is typically the definition of the diagram but if you are more familiar or more adventurous you can pass mermaid directives or configurations as well.

The next step is to add something similar to the template.

{{ if .Page.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 }}

I wasn’t sure why this needed to go on the actual page versus on the footer that would be used on every single page of the website. Most of the examples that I looked at had it on the single.html and this worked for individual pages but not for the index.html.

I placed it in article_footer.html until I get a deeper understanding of how Hugo wants to manage a flag that would toggle the loading of JavaScript.

Luckily, the browsers that I tested with only load the JavaScript once. I’m not a JavaScript expert, but I think multiple loads of the 7k line file might be frowned upon.

Colors of the Mermaid

After I got it working for the index and standalone pages, I tested the diagram with the dark mode of the theme. Unfortunately, the default text color does not work in all situations.

I checked the documentation on theming the diagrams and saw that I can change the theme by giving it as a parameter during the initialization of mermaid.

mermaid.initialize( { startOnLoad: true, theme: 'dark' });

This allows for the diagrams to look good in dark mode.

Dark diagram no background but in dark mode

This looked good in dark mode but not light mode.

Dark diagram no background lightmode

I then experimented with changing the background for div.mermaid and then having the diagrams render using the default theme for both light and dark mode.

Light diagram on light background while in dark mode

Good but not ultimately it just didn’t look good enough. I showed it to my wife and some people in a discord and they agreed as well.

I needed to find a way to make it toggle from default and dark.

Searching for the Key

Luckily, when I did my first third group of searches, I came across a Hugo forum thread on the “Correct way to embed Mermaid.JS2. And it had a link to this article to add Mermaid to Hugo with Dark Mode which is very informative.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{{- if .Page.Store.Get "hasMermaid" }}
<script type="module">
  import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs';
  let isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  let mermaidTheme = (isDark) ? 'dark' : 'default';
  let mermaidConfig = {
    theme: mermaidTheme,
    startOnLoad: true }
  mermaid.initialize(mermaidConfig);
</script>
{{ end }}

On line 4, we set a property based on the weather the browser is in dark mode.

Line 5 is a ternary statement that chooses between the dark or default themes.

Line 6 is the beginning of a JavaScript Object. I feel that the way it’s in the theme will make it easier to maintain and extend in the future.

It’s all about that base

Mermaid does have a base theme that allows for you to define the colors for the diagram. Yes, I could have taken the time to learn how to pull colors from the theme or use the toggle from light and dark mode to set the diagram colors. But, that could be a long process to customize something that I may not use.

If you want to take this theme and approach, please do, and please have no hesitation to send me a link of your endeavors.

Limitations

Although this implementation does consider if the user is in light or dark mode, it does not switch dynamically. If you are on a system that changes the theme depending on time, you will have to reload to get the different theme. There are themes and walk throughs that show how to do this, but I feel that this iteration and my skill level are not at the appropriate level to implement it now.

Another limitation is that the diagram definition might find it’s way into the .Summary for the individual post.

Mermaid Treasure

Thank you for getting this far into my post. Here are some resources if you want to know more about Mermaid.js.

Accessibility Options
If you took the time to make the diagram, take a little more time to allow other people to get something from it.
Tutorials
Lots of examples on how to make diagrams
Live Editor
A quick way to make and test your diagrams before putting them in your posts.

What’s next for Labarum?

I need to do an accessibility and validation audit of the theme. I’ve been making some adjustments and haven’t been testing to make sure that the site is as accessible as I had originally intended.


  1. I’ve found other templates that will have this call a short code that takes a set of parameters that control alignment and zoom of the diagram. I have no plans on duplicating that kind of functionality at this time. ↩︎

  2. There is no One way to do this. ↩︎