Juha-Matti Santala
Community Builder. Dreamer. Adventurer.


I've kept this original blog post here for history's sake but I've deleted the codeblocks repository and I do not recommend doing what I've done here. I have since fixed the problems on the source and learned that this approach caused more problems than it fixed and made the code examples on my blog inaccessible.

My blog is built using Ghost as a headless CMS and Eleventy as a static site generator. My main reason for switching the content side to Ghost was to gain access to a nice editing experience. And while I thoroughly enjoy writing again, adding code snippets into my blog posts is bit of a pain.

As an example, take a look at the following code snippet:

<nav class="menu">
    <li><a href="#">Home</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Contact</a></li>

On a surface, it looks simple and easy. But under the hood, there are a couple of things I need to do to make it look like that for you as a reader.

Adding classes for Prism

I use Prism.js on my website to make these code blocks nicer looking. It adds syntax highlighting and line numbers. It is a major improvement for readability and requires me to add pre and code tags with specific class names in the HTML block in Ghost.

For a single snippet, it's not much. But having multiple snippets like I had in my recent Python tuple blog post, manually crafting these blocks becomes a bit of a chore - a one that I'm not specifically excited about. It introduces friction to my writing process and as I'm not a great writer to begin with, any added friction makes writing a less enjoyable experience and I end up making less technical blog posts.

So to make the above snippet nice, my script adds <pre class="language-html"><code class="language-html"> in the beginning and matching closing tags at the end of the script to trigger Prism.

Converting special characters to HTML entities

To make sure that special characters used in HTML are displayed correctly, I need to convert them to HTML entities. That's even more of a chore than adding the surrounding tags and classes.

In the above example, there are 16 < characters and 16 > characters that need to be converted. Converting those manually is horrible, especially if you notice later you need to make a change on the code.

Introducing <code>blocks

So after mentioning this pain point to a friend in Slack as we discussed using Ghost, I figured that it's not that difficult to solve with my programming abilities. As a pythonista, I started a new Python project and wrote a script that gets the language name (used in class names) as a positional argument and reads the code snippet from standard input.

import sys

    language = sys.argv[1]
except IndexError:
    print('Usage: codeblock.py [language] < [input file]')

    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;'

codeblock = f'<pre class="language-{language}"><code class="language-{language}">'

for line in sys.stdin:
    for character, encoded in ENTITIES.items():
        line = line.replace(character, encoded)
    codeblock = f'{codeblock}{line}'

codeblock = f'{codeblock}</code></pre>'


I took the entity list from this MDN page of reserved HTML characters. In essence, the script just reads from standard input, goes through line-by-line and replaces all instances of HTML entities, wraps it in HTML tags and prints the result to standard output where it can be copied and pasted into the blog post in Ghost.

There are a couple of ways to use it. If your code is already in a file, you can do python codeblock.py python < code.py | pbcopy (leave out pbcopy if you're not using a MacOS) and you have the modified thing in your clipboard. If your code isn't in a file, you can just do python codeblock.py python | pbcopy and write or paste the code snippet into standard input and use CTRL-D to signify end of input.

Syntax Error

Sign up for Syntax Error, a monthly newsletter that helps developers turn a stressful debugging situation into a joyful exploration.