Fuzyll


Hopelessly passionate husband, engineer, hacker, gamer, artist, and tea addict.


Changing Markdown Output in Jekyll

Today, I rolled out some back-end updates to my website. Most of this was refactoring stylesheets, unifying post categories, and fixing a few messed up timestamps. Two features required me to extend an existing Markdown renderer in Jekyll, however, so I thought it'd be worthwhile to write up what I did.

Jekyll is the site generator I use to publish new posts and pages on this site. It works by taking files formatted in Markdown, converting them to HTML, and applying templates to the result. This is more-or-less what leading PHP-based solutions like Wordpress do, but Jekyll does all the work up-front. You just have to serve the resulting static pages.

What I wanted to do was:

  • Have a Lightbox effect when you click an image in one of my posts
  • Have posted images optionally have a caption (like Kramdown does, but with Redcarpet instead).

Creating a custom Redcarpet renderer is pretty easy. All you need to do is follow the advice in the README and create a new class that extends Redcarpet::Render::HTML and declares a new copy of the method(s) you want to modify. In my case, I wanted to change how images were output. That meant defining a new image method.

Doing this unfortunately made code highlighting no longer work in Jekyll. So, rather than specifying the highlighter in my _config.yml, I included Rouge, the package I use for syntax highlighting, directly. The result looks like this (I called my new, custom class Crimsoncarpet, but it can be anything):

require "redcarpet"
require "rouge"
require "rouge/plugins/redcarpet"

class Crimsoncarpet < Redcarpet::Render::HTML
    include Rouge::Plugins::Redcarpet
    def image(link, title, alt)
        name = File.basename(link, ".*")
        img = "<img src=\"#{link}\" alt=\"#{alt}\" title=\"#{title}\">"
        figure = "<a href=\"#{link}\" data-lightbox=\"#{name}\" data-title=\"#{alt}\">#{img}</a>"
        caption = title ? "<figcaption>#{title}</figcaption>" : ""
        return "<figure>#{figure}#{caption}</figure>"
    end
end

You can see above that, rather than outputting a simple <img> tag, I output a <figure> that contains the image, a link with data-lightbox and data-title elements, and an optional <figcaption>. This places a Lightbox link on every image I generate (with a caption set to the image's alt text) and, if I've set a title, generates a caption containing that title. Thus, output for Markdown like this...

![My Alt Text](/images/some_image.png "My Title Text")

...comes out looking something like this:

<figure>
    <a href="/images/some_image.png" data-lightbox="some_image" data-title="My Alt Text">
        <img src="/images/some_image.png" alt="My Alt Text" title="My Title Text">
    </a>
    <figcaption>My Title Text</figcaption>
</figure>

To get Jekyll to output all of this stuff when it's rendering my posts, I had to take the code above and place it in a file in my _plugins folder. I then had to add a Markdown converter in Jekyll to the end of the file like so:

class Jekyll::Converters::Markdown::Crimsoncarpet
    def initialize(config)
        @config = config
        options = {
            strikethrough: true,
            no_intra_emphasis: true,
            tables: true,
            space_after_headers: true,
            underline: true,
            footnotes: true,
            fenced_code_blocks: true
        }
        @renderer = Redcarpet::Markdown.new(Crimsoncarpet, options)
    end

    def convert(content)
        @renderer.render(content)
    end
end

To hook this up to my site, all I needed to do was change the markdown entry in my _config.yml to:

markdown: Crimsoncarpet

Now, if you click on images like the one below (which should have a caption under it), you'll get a Lightbox. That is, assuming you have JavaScript enabled, anyway.

Cereal Guy
You should probably have written code that didn't assume values aren't nil!