Staying Semantic When Rendering Markdown

15 Oct 2017

When dropping Markdown onto a page that already has a heading, by default we'll render two sequential <h1> tags, which will look funny and is unsemantic. Here's how to fix it:

We have MyArticle.markdown which looks like:

Here are some things.

Thing 1
=======

Something about thing 1.

Thing 2
=======

Something about thing 2.

Then we render it with a page template that looks like:

<h1>Article Title</h1>
... Article body, rendered from markdown ...

Now you have:

<h1>My Article</h1>
...
<h1>Thing 1</h1>
...

Which is no good, because Thing 1 should be a subheading of My Article.

We could 'fix' this using css typography classes - something along the lines of:

<h1>My Article</h1>
<div class='article-body'>
<h1>Thing 1</h1>

...

.article-body {
    h1 {
        @extend 'heading-2';
        ...

But that's messy, and broken for screenreaders / TOC-generators / &c.

Instead, we can rig RedCarpet so that it demotes all headings by one level, by writing our own renderer:

class ArticleMarkdown < Redcarpet::Render::HTML

    def header(text, header_level)
        new_header_level = header_level + 1
        "<h#{new_header_level}>#{text}</h#{new_header_level}"
    end

end

...

@markdown = Redcarpet::Markdown.new(ArticleMarkdown.new(render_options = {}), ArticleMarkdown::PREFERRED_OPTIONS)

...

<%= raw @markdown.render(@note.content) %>