I rewrote my blog and added syntax highlighting to it!

Created on 08/08/2025

Introduction

In my previous blog (here is a snippet in French, there is not much to see as I deleted the repo), I was using Hugo, so I had to write my articles using Markdown. It was okay, but I was constrained by Hugo and the blog template, and was more thinking about superficial features (internationalization, ways to add math formula, writing header and footer templates) rather than focusing on writing articles.

After discovering TapirMD (or TMD), I decided to give it a try. It is a markup language that adds improvements over Markdown, such as hidden texts, spoilers, dimmed texts, tabs and many more. The default CSS file delivered with it gives a very minimalist yet appealing theme. The syntax varies from Markdown when writing headers and italics, so I had to dig to demos several times to remember about that.

Everything seemed fine, but I was wondering if I could add colored code blocks, for a better reading.

Recoding

To add syntax highlighting, I’m using a JavaScript library, highlight.js, which generates HTML code (and many <span>s). And this is a problem. Even though you can add ids and classes for text blocks, you can’t insert raw HTML code inside TMD files.

Therefore I switched back to Markdown, but to make the conversion progressive and safe, I created a new branch just in case. So am I going back to Hugo? Nope, instead I am using Pandoc for Markdown-to-HTML conversions.

Switching to a different markup language means having a different syntax, so I had to edit my scripts. I even rewrote my rendering script in Nimscript instead of Bash, since I feel more comfortable with Nim in general, and Nimscript is cross-platform.

Coloring code blocks

The idea is simple. For each Markdown article this occurs:

  1. Read article content.
  2. Detect all the code blocks.
  3. For each code block, write it to a separate file, apply my script to generate highlighted code as HTML.
  4. Get the HTML code and add it to a new article copy.
  5. Pandoc the article copy.
  6. Delete the copy.

At first, the highlighting script expected directly the code as argument (e.g. node index.js "print('hello')" lua), but things were quickly unusable once the code contained double quotes. I was thinking about replacing every double quote with a barely used character (this one: ¤) and replace back with double quotes after generating the HTML code, but then strings wouldn’t be highlighted anymore. This is the reason why the highlighting script expects a file instead.

Once the code is generated and inserted, you need a CSS theme to color it. I’m using One Dark Pro.

Another constraint I met: since I want the code background to fit the text and not the whole screen, I set the code display to inline-block, so if I’m writing multiple code blocks, without text to separate them, they will be displayed side by side. So I have to use <br> between code blocks to fix that, but the space between code blocks seems too high for me.

Examples

Some code without any language assigned to it.
Instead of going in auto mode, the rendering script lets the code block as plain text.
I could use it to illustrate a list of commands for example.


// Hello world in Zig, because why not.
// Made me realize that the markdown plugin in Lite XL does not color zig code.
// Let me fix that.
const std = @import("std");

pub fn main() void {
  std.debug.print("Hello World!");
}


# A template in Nim, very useful for my scripts.
template expect(condition: bool, message: string) =
  if not condition:
    echo message
    return


# Some Python function.
# Before the raise IndexError I was using an assert False lol.
def index_type(line: str, substrings: tuple[str]) -> tuple[int, str]:
    for sub in substrings:
        if sub in line:
            return line.index(sub), sub
    raise IndexError("error with index type")


I still have a few tasks to do to improve my website (especially its appearance), but it’s definitely a good start.

Thanks for reading!