Captions in Markdown

Published on | Updated

The truth is that, as of now, captions are not part of the original Markdown specifications, nor are they part of the more modern CommonMark specifications. Thus, many developers end up using imperfect “hacks” to achieve captions in Markdown. This article will outline some possible caption methods, and their weaknesses.

The best solution, as explained below, is to write captions as inline HTML <figcaption> tags.

#Goals: The Ideal Captions

The idea for Markdown is to make it easy to read, write, and edit prose. HTML is a publishing format; Markdown is a writing format.

John Gruber

Ideally, a caption solution:

  1. Maintains Markdown portability: you should be able to take your Markdown to any Markdown processor and still get captions.
  2. Stays true to Markdown’s intent: Markdown has a clear set of rules that should be followed.
  3. Works with both images and code fences: images aren’t the only use of captions.

#Possible Solutions

#Hijacking alt or title attributes

Markdown’s syntax allows adding alt and title attributes to images like so:


My ![alt attribute goes here!](/path/to/train.jpg "This is a Title" )



<p>My <img src="/path/to/train.jpg" alt="alt attribute goes here!" title="This is a Title"/></p>

It would be all too easy to just fit a caption in either attribute. However, the reality is that neither the alt or title attributes were made to represent a caption:

The alt attribute provides alternative information for an image if a user for some reason cannot view it (because of slow connection, an error in the src attribute, or if the user uses a screen reader).

The title attribute specifies extra information about an element. The information is most often shown as a tooltip text when the mouse moves over the element.


Thus, this method does not stay true to Markdown’s intent. In addition, you would need to program your Markdown processor to extract these values from the image and display them as a caption, so this solution isn’t very portable, either.


To generate this site, I use the static site generator Hugo which processes my Markdown. Hugo, along with some other Markdown processors, supports a feature called shortcodes. These allow you to easily add features to your Markdown by calling a shortcode with some parameters.

For example, vanilla Markdown does not have syntax to display a YouTube video in content. However, you can use Hugo’s builtin shortcode for YouTube, which takes a video ID and renders it as an embedded video:


{{< youtube w7Ft2ymGmfc >}}

This shortcode syntax can be easily adapted to create captions, e.g. by creating a caption shortcode like the one below:


<figcaption>{{ .Inner | Markdownify }}</figcaption>

Captions can then be written like so, by calling the shortcode underneath an image:


![Amazon Rainforest](/path/to/image)

{{< caption >}}The [Amazon Rainforest](https://en.wikipedia.org/wiki/Amazon_rainforest) contains a multitude of species.{{< /caption >}}

Shortcodes are a way to create readable and parse-able Markdown, but sacrifice portability because they depend on specific Markdown processors (like Hugo).

#Targeting Captions with CSS

By using writing your caption with *emphasis* on the line immediately following an image, you can use CSS to target and style captions to look different from ordinary text:


![Amazon Rainforest](/path/to/image)
*The Amazon Rainforest contains a multitude of species.*

Would become in HTML:


	<img src="/path/to/image" alt="Amazon Rainforest">
	<em>The Amazon Rainforest contains a multitude of species.</em>

The <em> caption can then be targeted in CSS like so:


img + em {
	/* style your captions here */

Unfortunately, this syntax only works for images. If you try the same thing with code blocks:


int myInt = 5;
int yourInt = 10;
int ourInt = myInt + yourInt;
*This syntax adds two variables together.*

The <em> tag is put in a separate <p> tag:


	<!-- rendered code block here -->

	<em>This syntax adds two variables together.</em>

Thus, you can only target image captions using this method, and not code blocks. In addition, some Markdown processors will not produce the right HTML to use this method.

#The Best Solution: Inline HTML

The original Markdown specs are pretty convicting:

For any markup that is not covered by Markdown’s syntax, you simply use HTML itself.

John Gruber

I know what you’re saying.

But, HTML is so annoying to write…

Don’t worry, it’s not that bad. In order to stay true to Markdown’s intent, we must simply use HTML itself. So, how do we do that? What HTML tag and syntax should be used?

#Using <span> Tags

Unlike block-level HTML tags, Markdown syntax is processed within span-level tags.

Original Markdown Specs

You can write your captions inside a <span> tag with a specific class, like .caption, then use CSS to make those elements look like captions. For example:


![Amazon Rainforest](/path/to/image)

<span class="caption">The [Amazon Rainforest](https://en.wikipedia.org/wiki/Amazon_rainforest) contains a multitude of species.</span>

One perk of this method is that you can embed Markdown syntax right there inside the <span> tag and it gets processed properly. However, this solution doesn’t really follow HTML semantics: there’s a better tag that’s made specifically for captions.

#Introducing the figcaption Tag

The figcaption HTML tag is made for image or figure captions, and can easily be embedded into Markdown:


![Amazon Rainforest](/path/to/image)

<figcaption>The Amazon Rainforest contains a multitude of species.</figcaption>

Using this HTML tag makes your Markdown portable to any processor, it’s true to Markdown’s (and HTML’s) intent, and it works anywhere in your Markdown, thus making it the best solution thus far. If you want to embed Markdown inside your captions, keep reading: it’s still possible.

#Putting Markdown Inside the Caption

Note that Markdown formatting syntax is not processed within block-level HTML tags. E.g., you can’t use Markdown-style *emphasis* inside an HTML block.

When you write a line of HTML inside Markdown, the Markdown processor will process the entire line as HTML. Let’s say you wanted to embed hyperlinks and emphasis in your caption, like this:


![Big Red Train](/path/to/image)

<!-- this doesn't work! -->
<figcaption>The [ABC Train](https://example.com) is *very* big and red.</figcaption>

Since the line is HTML, the Markdown link and emphasis will not be processed. There are a couple fixes:

Method 1: Write in Pure HTML

Instead of using Markdown syntax inside the caption, you can just write it in pure HTML:


![Big Red Train](/path/to/image)

<figcaption>The <a href="https://example.com">ABC Train</a> is <em>very</em> big and red.</figcaption>

Method 2: Separate with Empty Line

Separate your Markdown and HTML with an empty line. This way, the “sandwiched” Markdown gets processed as Markdown, but is still encapsulated in the <figcaption> tag:


![Big Red Train](/path/to/image)


The [ABC Train](https://example.com) is *very* big and red.



Let’s recall our 3 goals for the perfect markdown caption method:

  1. Maintains Markdown portability
  2. Stays true to Markdown’s intent
  3. Works with both images and code fences

The only solution that fits all 3 of our goals is writing captions using inline HTML, and specifically, by using the <figcaption> tag. If you have your own way of writing Markdown captions that isn’t mentioned, let me know! There may be a better solution yet.

Meet the Author

John Allbritten

Nashville, TN

I love learning new technologies, and I have a passion for open source. As I learn things, my notes turn into articles to share.


byte-sized tech articles.

delivered straight to your inbox.

Every month, receive a curated selection of tech articles.

What's this?

Related Posts