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) %>