My work frequently involves reading and writing text formatted in hanging paragraphs. I’m a lawyer by trade, and hanging paragraphs at varying levels of indentation are useful tools for exposing the organizational scheme of laws, contracts, and memos.
A hanging paragraph is a paragraph with a first line that begins to the left of the left margin of the remaining lines of the paragraph. Each of the subparagraphs in the image at the top of this article is a hanging paragraph.
Creating hanging paragraphs in word processing software like Microsoft Word is trivial. MS Word’s paragraph format dialog box has a specific setting for hanging paragraphs. Set the left margin then click a button to set the hanging offset and you’re done.
Although I use MS Word for most of my professional writing, I often use Jekyll for information presentation and other projects (like this blog). Jekyll uses a variant of the Markdown markup language that converts simple text files to HTML. I’m writing this article in plain text using the Visual Studio Code editor. Jekyll then converts this file to HTML, which is then served to readers over the internet.
Hanging Paragraphs Natively Supported in Markdown
HTML natively supports two types of hanging paragraphs: bulleted lists and numbered lists. Creating those types of hanging paragraphs in Markdown is trivially easy. For a bulleted list, precede the text with an asterisk, a plus sign, or a hyphen. For example, this text in the editor:
* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
- Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
produces this result in HTML;
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
-
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
-
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Similarly, precede the text with a number followed by a period and space:
1. Commodo nulla facilisi nullam vehicula ipsum a arcu cursus. Semper feugiat nibh sed pulvinar proin gravida hendrerit lectus.
2. Aliquam id diam maecenas ultricies. Cursus euismod quis viverra nibh cras pulvinar. Nam at lectus urna duis convallis convallis tellus.
to produce a numbered list:
-
Commodo nulla facilisi nullam vehicula ipsum a arcu cursus. Semper feugiat nibh sed pulvinar proin gravida hendrerit lectus.
-
Aliquam id diam maecenas ultricies. Cursus euismod quis viverra nibh cras pulvinar. Nam at lectus urna duis convallis convallis tellus.
Unfortunately, if you want to specify a hanging paragraph in Markdown in any format other than a bulleted or numbered list, things quickly become complicated. Any solution necessarily requires CSS to define the format, and CSS and Markdown have an uneasy relationship at best.
My Requirements for Hanging Paragraphs
Before continuing, I should note that I have two requirements for my hanging paragraphs as they ultimately appear in a web browser. One, the paragraph designator, or what I will call the ordinal—for example, the (a) and (i) in the image above—must appear by itself at the left margin of the paragraph, with the text of the paragraph in a block beginning a specified distance from the left margin. In other words, there needs to be the functional equivalent of a tab between the ordinal and the rest of the first line of text. HTML does not support tabs in the typesetting sense of the word. Two, copying the ordinal and text from the web browser to paste into another document should not be unreasonably troublesome.
I first looked at the code behind the text of the image at the top of this article. It satisfies both of my requirements. However, I decided against the approach used there for two reasons. First, it relies on HTML tables to control the formatting. While it works, using HTML to control formatting is frowned on, not least in this case because the information being presented is not tabular data but text. Second, while it is possible to create tables in Markdown, it is spectacularly inefficient. I would like a solution that involves a minimum of fuss.
First Solution - Flexbox
My first solution used CSS’s flexbox feature. A hanging paragraph can be thought of as having two parts: a left part containing the ordinal and a right part for the text. The key is to specify the starting position of the ordinal and the width of the ordinal’s box, because the box for the text can simply take up the remaining width to the right margin. Here is the CSS for the flexbox solution:
:root {
--tab_size: 3rem; /* adjust the size of the "tab" here */
}
/* generic flex container that will surround each hanging paragraph */
.hang-p {
display: flex;
flex-flow: row nowrap;
justify-content: flex-start;
align-items: baseline;
}
/* style for the text of each hanging paragraph */
.hang-t {
flex: 1 1 auto;
}
/* first level hanging indent */
.hang-1 {
padding-left: calc( var(--tab_size) * 1 );
flex: 0 0 calc( var(--tab_size) * 2 );
}
/* second level hanging indent */
.hang-2 {
padding-left: calc( var(--tab_size) * 2 );
flex: 0 0 calc( var(--tab_size) * 3 );
}
Markdown passes HTML without change, so to specify two hanging paragraphs like 13(a) and 13(a)(i) from the image above, I only needed to include the following HTML in my Markdown document:
<div class="hang-p">
<p class="hang-1">(a)</p><p class="hang-t">in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:</p>
</div>
<div class="hang-p">
<p class="hang-2">(i)</p><p class="hang-t">an account issuance service that is not solely incidental to the conduct of any deposit‑taking business or banking business;</p>
</div>
This code uses a CSS variable (--tab-size
) so that I can easily change the ‘tab size’ if I need to. The .hang-p
style defines the flexbox that will hold each paragraph of text. The .hang-1
and .hang-2
styles implement the first and second level indents. The .hang-t
style implements the variable width box for the text. With this approach I can easily implement other levels of indentation.
Here is the resulting output:
(a)
in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:
(i)
an account issuance service that is not solely incidental to the conduct of any deposit‑taking business or banking business;
While it looks good, there are few problems with this approach. My primary concern is that it results in a Markdown file that looks more like an HTML file, which is contrary to the philosophy of Markdown. It also requires a lot of complex hand coding, which means that creating the file is time-consuming and asking someone who is not technically inclined to create or maintain the file would be asking a lot. Finally, while copying a paragraph with the ordinal from the web page is not a problem, pasting it into another document results in the ordinal appearing as a separate paragraph because each box in the flex box is its own block-level paragraph.
Second Solution - Text-Indent With Embedded Content
My second solution relied on CSS’s text-indent property, which indents the first line of a paragraph of text. It would typically be used like this:
<p style="text-indent: 2em;">(a) in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:</p>
which results in:
(a) in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:
However, it can also be used with a negative parameter which, when paired with a padded left margin, results in a hanging indent. For example, this:
<p style="padding-left: 2em; text-indent: -2em;">(a) in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:</p>
produces:
(a) in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:
This would be perfect and simple if there was a tab instead of a space between the ordinal and the rest of the text. That could be fixed by assigning the ordinal and the following white space a fixed width equal to the desired ‘tab’ size, but it seemed that inserting code for a span would still leave the file looking more like an HTML file than a Markdown file. To avoid that, the technique used to create unique bullets for bulleted paragraphs can be used:
:root {
--tab_size: 3rem; /* adjust the size of the "tab" here */
}
/* paragraph formatting with hanging indent */
.h-1a {
padding-left: calc( var(--tab_size) * 2);
text-indent: -(calc( var(--tab_size) * 1));
}
/* insert the specified content in a block with a fixed width */
.h-1a::before {
content: "(a)"; /* here is the ordinal */
display: inline-block;
text-indent: 0;
width: calc( var(--tab_size) * 1);
}
<p class="h-1a">(a) in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:</p>
which produces:
in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:
This looks perfect and requires very little HTML code to be mixed in with the Markdown. However, the ordinal can neither be copied nor pasted, which is a non-starter (try it). Also, this would have required a different style for every ordinal because the content of the ordinal is hard-coded in the style.
Preferred Solution - Text-Indent With Styled Ordinal
My preferred solution builds on the text-indent technique of the previous solution but acknowledges that the content of the ordinal must be ordinary text. Here is the CSS:
:root {
--tab_size: 3rem; /* adjust the size of the "tab" here */
}
/* span style for the ordinal */
.h-h {
display: inline-block;
text-indent: 0;
width: calc( var(--tab_size) * 1);
}
/* first level hanging indent */
.h-1 {
padding-left: calc( var(--tab_size) * 2);
text-indent: (calc( var(--tab_size) * -1));
}
/* second level hanging indent */
.h-2 {
padding-left: calc( var(--tab_size) * 3);
text-indent: (calc( var(--tab_size) * -1));
}
.h-1 em, .h-2 em {
font-style: normal; /* undoes the emphasis needed to define the span element */
}
and the HTML:
<p class="h-1"><span class="h-h">(a)</span>in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:</p>
<p class="h-2"><span class="h-h">(i)</span>an account issuance service that is not solely incidental to the conduct of any deposit‑taking business or banking business;</p>
which produces:
(a)in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:
(i)an account issuance service that is not solely incidental to the conduct of any deposit‑taking business or banking business;
From a requirements perspective, this is almost perfect. The formatting on the web page is correct and the ordinal can be copied along with the text. However, when the content is pasted there is no tab or space between the ordinal and the text. I can live with that deficiency for now.
Refined Preferred Solution - Kramdown IALs
The final refinement is to abstract the HTML so that the text file can revert to pure Markdown form. This is possible because Jekyll incorporates a variant of Markdown called kramdown. Kramdown has a feature called inline attribute lists that allows attributes to be attached to block and span level elements. The syntax is straightforward. For a block level element like a paragraph, include the attribute on the line following the block level paragraph, like this:
(a) in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:
{: .h-1 }
The span level element is more challenging because it is necessary first to define the span level element (without reverting to the HTML <span>
tag, of course). The least painful way I’ve found to do that is to mark it up to be displayed with emphasis (ie, italics), like this:
_(a) _{:.h-h}in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:
{: .h-1 }
_(i) _{:.h-h}an account issuance service that is not solely incidental to the conduct of any deposit‑taking business or banking business;
{: .h-2 }
That markup produces the same result as above:
(a) in relation to an exempt payment service provider mentioned in subsection (1)(a), means any of the following payment services:
(i) an account issuance service that is not solely incidental to the conduct of any deposit‑taking business or banking business;
Note that there are no spaces in the span level tag but there are spaces around the attribute in the block level tag. See the kramdown documentation for more information. Note also that the final style in the CSS set out above is needed to remove the italics that the underscores surrounding the ordinals would otherwise produce.
Conclusion
This solution is not perfect but it does keep the Markdown file as pure as possible. Also, with the addition of a non-breaking space between the ordinal and the closing underscore, it even solves the issue I mentioned above regarding the lack of a space between the ordinal and the text when pasted. This solution thus satisfies my two key requirements for hanging paragraphs.
If you have refinements to this solution or a different solution that better meets my requirements, I’d love to hear from you.