We’ve been releasing some brand new products here at RightScale that are pretty technical in nature and really required us to take a hard look at our documentation layout and usability. The fact of the matter is, our old documentation site was from a different point in our business and didn’t provide the kinds of collaboration tools and freedom that we wanted for our new products.
After an analysis of different documentation platforms, we settled on Middleman. We were planning on engaging our engineering team much more for documentation content on our new docs site, so we needed something that fit in with our existing tools and processes and we didn’t mind trading added complexity for all of the benefits we would get from having a full open source platform at our disposal. Our UX team could handle the CSS and design aspect, while our engineering teams could use our standard engineering flow to submit pull requests which would eventually be reviewed by the product team and merged by our documentation team.
Once we had nailed down the implementation specifics, including the decision, for now, to host the site on Github pages, our design team got to work on creating a styleguide that we would use across the site. The styleguide would show conventions and approved UI elements, as well as describe how to use them all using markdown, which we decided would be our syntax of choice.
While most of the styles were pretty standard - how to do tables, syntax highlighting, etc - there were a couple that required the use of straight HTML to get the desired effect. Instead of forcing our document authors to copy and paste these large snippets of HTML, we decided to implement custom markdown syntax. Before we get into the details, let’s look at an example of one of those styles, what the HTML looked like, and what we can do with markdown now.
Custom Markdown example
Here’s the “content card” style from the styleguide, and the associated HTML needed to generate it:
And here’s the same card, using our custom markdown syntax:
You can see that for the above style, we’ve chosen to use 2 brackets as the beginning and end delimiter, post and prefixed by the title and footer content. The main card content can be any number of lines and can contain other markdown elements within it.
Implementing custom Markdown
Implementing custom markdown requires a few moving pieces: building a custom markdown renderer class, defining your custom markdown syntax parsing with regular expressions, and instructing Middleman to use the new renderer.
Creating a custom renderer
First we need to create a custom renderer class that will handle rendering the custom markdown. We’re using Redcarpet as our renderer in middleman, so we create a Ruby class that inherits from the redcarpet HTML renderer. Note that you could build this class directly in your middleman
config.rb file, but as it starts to get more complex, you’ll want to move it to its own file with specs/etc.
Declaring and initializing
Note above that we have to merge certain options into our custom class, the options listed for the
Renderer in the redcarpet page.
Overriding renderer methods
The redcarpet page also lists the methods that you can override, but since we’re trying to implement block-level markdown elements, we’re going to have to process the raw page before handing it over to the normal renderer, so we’ll implement the
As the comment in the code notes, if you want to render any markdown inside of your methods, you have to have a renderer class available to do that. You can’t simply use
super for various reasons described on this page, so you have to create a new class. And, you have to remember to use any options for the new class that you want the renderer to use (in our case, the same options as for middleman – see below).
Parsing the content for the new markdown syntax
As you can see above, our
preprocess method calls a method that does the work of locating text and generating HTML content. Let’s take a look at the method for the content cards that we saw above:
The above is fairly straightforward - we’re searching the text for our custom markdown syntax, using regex groups to get the content we need, and generating the surrounding HTML. One point to note is the use of the
renderer class that we declared above. Because we want the content of the card to be rendered like normal markdown, we explicitly use the renderer to render it so that when we return the resulting string to middleman, these code portions are all done.
Now that we have our custom renderer, we configure middleman with all of the markdown rendering options that we want to use and instruct it to use the customer renderer. Note that we also use middleman-syntax for code highlighting, which, in turn, uses Rouge. Here are the relevant parts of our middleman
Note the last line - this is how to instruct middleman to use our custom renderer class. One point to note is that all of the options listed in that block are from the
Markdown object, i.e. those listed here on the redcarpet page. The options listed farther down on the referenced redcarpet page are for the
Renderer classes (which we set above when creating our custom renderer class).
Other custom markdown in use
We also implemented a couple of other markdown syntaxes for other UI elements. The first is for “alert” boxes – the style and the markdown we use is shown below.
The syntax for the above is:
The final syle we implemented was to build tabsets using boot strap so that we could show examples in multiple languages. The style looks like this:
Which can be written in markdown like so:
When we set out to build our new docs site, we wanted to make it easy for people to focus on the content, and not the structure. For us, using a set of open source tools and customizing them has been a successful approach. By implementing custom markdown, we’re able to generate great looking pages with modern content while keeping authors out of HTML/style land. For a real-world example of all of these styles, check out this page in our docs site.