Elementcodes: post-process markdown generated elements via shortcode-like templates

I currently use JavaScript to add a CSS class to paragraphs starting with a quote character in order to lay it out as a “hanging paragraph”.

It would be better if I could do that at site build time to avoid a “jump” at page-load and as the JavaScript kicks in. What is lacking, however, is a way to post-process the markdown generated HTML in Hugo.

One method would be to have a set of special (optional) shortcodes, each that map directly to the HTML element being emitted by the Markdown-to-HTML generator. These might be called elementcodes to make them distinct from shortcodes. In each elementcode template:

  • the .Inner variable would be the innerHTML of the current rendered element
  • the template is responsible for writing out the open/close tag; allowing the template to change the element to something else completely, nest the element in another, or add attributes (.e.g CSS class)

These elementcodes might live in layouts/elementcodes, adjacent to where shortcodes live.


I think I’m missing something. How are you not able to do this already by creating your own shortcodes?

I want to apply it to every element generated by markdown, without having to place shortcodes everywhere. Think of elementcodes as a way to extend the intrinsic markup that markdown can output, consistently across all generated elements, without having to pollute the markdown source.

For example, the following markdown,

This is a paragraph.

"This is a hanging paragraph."

generates the following HTML:

<p>This is a paragraph.</p>

<p>"This is a hanging paragraph."</p>

I’d like to post-process all generated <p> elements without changing the above markdown source so that the output becomes:

<p>This is a paragraph.</p>

<p class="hanging">"This is a hanging paragraph."</p>

The same markdown with shortcodes,

This is a paragraph.

{{< hanging-par >}}
"This is a hanging paragraph."
{{< /hanging-par >}}

is going to pollute the source quite a bit the more it is used.

But that’s not about replacing every instance of an element as much as it’s about replacing every instance of an element by checking for a specific character; that is, the quotes. I guess I’m wondering about more than this very specific use case…

The proposed elementcodes, where defined, would replace every instance of the element.

In this case, I would write the elementcode (elementcodes/p.html) to conditionally add the CSS class when the .Inner text starts with a quote, and otherwise produce HTML that matches the intrinsic element.

The elementcode template above might look something like:

{{ $firstchar := substr .Inner 0 1 }}
{{ if $firstchar == "\"" }}
<p class="hanging">{{ .Inner }}</p>
{{ else }}
<p>{{ .Inner }}</p>
{{ end }} 

Have you taken a look at MMark?


Mmark is fantastic! Nevertheless, unless I’ve missed something, it isn’t extensible in the manner desired.

The elementcodes described here could apply to Mmark, or any markdown processor, as it is essentially a post-processing step.

The only other workable solution, aside from JavaScript at page-load, is to post-process the Hugo-generated HTML before uploading to the web-server. The only (big!) downside is that it is not visible when running Hugo’s server with live-reload.

I just started using MathJax and these elementcodes could help here too.

In the Hugo MathJax documentation, it is recommended to enclose inline math with backticks so Markdown acts as pass-through on the LaTeX markup.

The official recommendation then uses page-load JavaScript that adds a has-jax CSS class to generated HTML elements that contain the LaTeX content above. With the CSS class, the math content of the page can be targeted by to undo the <code> default formatting and add further styling.

This could be better done at site generation time instead with a elementcodes/code.html template that checks for the $ MathJax delimiters and statically inserts the CSS class when required, thus avoiding a page-load content jump.

The above is focused on in-line math; for display-style math, a shortcode can be created that creates a div with the required CSS class. However with elementcodes, we could more elegantly unify math markup by using a fenced code block, where the elementcode template checks for the $$ math markup delimiter.

In the new docs concept, just as an FYI, we mention that MMark works well with MathJax and Katex: