Advanced formatting with Markdown using Jekyll and Includes
Conversation about Markdown and limitations
There’s somewhat of an interesting conversation going on about Markdown, constraints, extensibility, and limits. Eric Holscher started the conversation with Why You Shouldn’t Use “Markdown” for Documentation. Mark Baker followed it up with Why we need constrainable lightweight markup languages.
I want to add to the discussion by showing how Jekyll can be used to expand beyond Markdown’s limitations, primarily through the use of includes and page frontmatter.
Parameters with Includes
Jekyll let’s you create files in an _includes folder that you can include anywhere in your doc. For example, create a file called myfile.html. Store it in your _includes folder, and then you can embed the file anywhere by using this include
syntax:
{% include myfile.html %}
You can also pass parameters to the include, and this is where things get interesting. Suppose in myfile.html, you added this:
This is some {{include.stuff}}.
Now stuff
becomes a parameter that you can populate when you call the include. Here’s an example:
{% include myfile.html stuff="special text" %}
See how that works? The result will be:
This is some special text.
There’s a great Jekyll screencast on includes at Jekyll Tips.
I use includes to create templates for alerts, callouts, and images in my Jekyll Documentation theme.
Sponsored content
Essentially you can create sophisticated HTML formatting, and only require the user to pass certain parameters in an include using the basic include code.
Parameters in Frontmatter
You can also populate your page’s frontmatter with specific properties and then pass those properties into HTML stored in a layout. This is the approach I took with the workflow maps that I wrote about here and here.
For example, the frontmatter for one of the user map pages contains the following:
---
title: Sample 1 Topic
keywords: sample
description: "This is just a sample topic..."
sidebar: product2_sidebar
permalink: /p2_sample1/
map: true
map_name: usermap
box_number: 1
---
The layout.html file, where this content gets pushed, contains the following:
{% if page.map == true %}
<script>
$(document).ready ( function(){
$('.box{{page.box_number}}').addClass('active');
});
</script>
{% include custom/{{page.map_name}}.html %}
{% endif %}
The values for the map
, map_name
, and box_number
get passed into the template. Hopefully you can see how these parameters stored in the page’s frontmatter populate the HTML code.
What’s cool about this approach is the ability to separate the content from the formatting. You don’t want your authors worrying about whether they have the syntax right — you just want them to supply the right values to a template.
Overall, you can create sophisticated HTML templates and then simply make each custom value a variable that you populate either through an include parameter or frontmatter parameter.
In this way, you use Jekyll to get past the limitations of Markdown syntax. For example, with the images, it would be a pain to type this syntax every time:
<figure><a class="no_icon" target="_blank" href="http://jekyllrb.com"><img class="docimage" src="https://idratherbewriting.com/documentation-theme-jekyll/images/jekyll.png" alt="Jekyll" /></a><figcaption>This is a sample caption</figcaption></figure>
With the include approach, the syntax is much simpler:
{% include image.html file="jekyll.png" url="http://jekyllrb.com" alt="Jekyll" caption="This is a sample caption" %"}
I like this approach because it gives me a lot more flexibility. As long as I can express the code in HTML, I can extract out the customizable values into a template for the writer to populate, keeping everything simple.
For more advanced templates, I can leverage Liquid syntax to create loops and other logic.
Conclusion
Markdown syntax is limiting. Its simplicity is also its strength, since it allows larger numbers of people to easily learn and implement the syntax. However, when you need more advanced syntax, rather than trying to come up with a new Markdown variant, consider trying to create includes or other templates in your code, and then pass parameters into those templates.