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.