AsciiDoc Recommended Practices

This document catalogs a set of recommended practices for composing documents in AsciiDoc.

DRAFT

This document is only a rough draft. These recommendations are not doctrine and are subject to change.

AsciiDoc offers multiple ways to accomplish the same thing, and is often very lenient with the syntax. This guide helps bring consistency to your documents that will maximize efficiency, improve readability and minimize maintenance.

Document extension

AsciiDoc doesn’t care which extension you use. GitHub supports the extensions .asciidoc, .adoc and .asc.

.adoc seems to be the most often used extension and the one we prefer. It does not conflict with other well known applications, is distinct, and not excessively long (like .asciidoc). It also reads as “a doc”. Our recommendation, therefore, is to use .adoc.

We strongly recommend against using the .asc extension. The connection to AsciiDoc isn’t immediately obvious and it gets falsely identified as a PGP file on Linux.

One Sentence Per Line

Don’t wrap text at a fixed column width. Instead, put each sentence on its own line, a technique called sentence per line. This technique is similar to how you write and organize source code. The result can be spectacular.

Here are some of the advantages of using the sentence per line style:

  • It prevents reflows (meaning a change early in the paragraph won’t cause the remaining lines in the paragraph to reposition).

  • You can easily swap sentences.

  • You can easily separate or join paragraphs.

  • You can comment out sentences or add commentary to them.

  • You can spot sentences which are too long or sentences that vary widely in length.

  • You can spot redundant (and thus mundane) patterns in your writing.

We picked up this idea from the writing guide in the Neo4j documentation. However, it seems like the idea dates back a discovery by Buckminster Fuller in the 1930s, who called it ventilated prose. The technique was also recommended in 2009 by Brandon Rhodes in a blog post about Semantic Linefeeds.

It’s important to note that this technique works because AsciiDoc doesn’t treat wrapped lines in prose as hard line breaks. At least, it doesn’t show up that way to the reader. The line breaks between contiguous lines of prose will not be visible in the rendered document (i.e., as the reader sees it).

While a single line break doesn’t appear in the output, two consecutive line breaks starts a new paragraph (or other block).

Section Titles

There are three ways to define a section title. These variants can be classified into two types of styles, atx and setext (historical names). The asymmetric Atx-style is the style we recommend to define your section titles.

Atx-style

In the Atx-style, the section title is defined on a single line. The section title begins with one or more equal characters (i.e., =) followed by a space and the section title. The number of leading characters representing the depth. The top-level section is reserved for the document title, so the first section in the document will have two leading characters.

== First Section

This example uses the asymmetric Atx-style. It requires the least number of characters and it’s intuitive since the number of leading characters represents the heading level.

When using the HTML backend, this section title will be transformed into an h2 heading (likely wrapped in a section block for styling):

<h2>First Section</h2>

The symmetric Atx-style bookends the section title between a matching pair of delimiters.

== First Section ==

While this syntax may look nicer to some, it requires twice the number of delimiters without adding any additional semantics. Therefore, I consider it a waste of keystrokes and don’t recommend using it. However, either of the Atx-style section titles are better than the Setext style.

Setext

In the Setext-style, the section title is defined on two lines. The second line is a string of characters that form an underline below the section title. Here’s an example of a section title from above defined in the Setext-style.

First Section
-------------

Since the length of the line is determined by the number of characters in the section title, another dimension must be introduced to determine the level. That dimension is the character used for the underline. The characters used are (sorted from document title to level 5 section) =, -, ~, ^, and +.

I strongly recommend against using the Setext style for section titles. Keeping the length of the underline in sync with the title length is an unnecessary task and time waster. A more substantial reason is that the mapping between character and heading level is very difficult to remember and not obvious to new AsciiDoc adopters. All in all, using Setext section titles is a bad practice. Don’t use them!

The recommendation is to use the asymmetric Atx-style for section titles. It’s the most simple and intuitive method for writing, as well as for reading in the plain text (source) file.

Document title

Since the document title occurs only once in a document, I consider it acceptable (but strongly discouraged) to use the Setext style in this instance, so long as you also disable the compat-mode attribute (otherwise, it puts the processor into compat mode).

Document Title
==============
Author Name <author@example.org>
:compat-mode!:
You must explicitly disable compatibility mode (i.e., compat-mode!) in this case if you don’t want it enabled. Compatibility mode is automatically enabled when using the Setext style document title. See the migration guide for details.

Be sure to include your name immediately following the document title.

AsciiDoc only supports an e-mail following the author line. It does not support a personal URL. Asciidoctor supports a raw URL as well as an inline link macro.

It’s a good practice to also include a revision line following the author line.

Document Title
==============
Author Name <author@example.org>
v1.0, 2012-02-10
:compat-mode!:

The version number is optional. The revision line may consist of a date only.

Document Title
==============
Author Name <author@example.org>
2012-02-10
:compat-mode!:

Delimited Blocks

Delimited blocks contain special text such as code listings, quotes, sidebar text, tables and so on. As you may have guessed, they are bounded by a string of delimiters. The delimiters are defined on a line by themselves. The content goes in between the delimiter lines. Here’s an example of a listing:

----
$ asciidoctor -b html5 recommended-practices.adoc
----

Delimited blocks require four or more repeating characters on a line by themselves to mark the boundary of the block. The one exception is the open block, which only requires two - repeating characters.

You may be tempted to extend the line further, either to a predetermined length or to match the length of the content.

-------------------------------------------------
$ asciidoctor -b html5 recommended-practices.adoc
-------------------------------------------------

Don’t do this!

Maintaining long delimiter lines is a colossal waste of time, not to mention arbitrary and error prone. I strongly urge you to use the minimum number of characters necessary to form a delimited block and move on to drafting the content. The reader will never see these long strings of delimiters anyway since they are not carried over to the output (HTML, DocBook, etc).

AsciiDoc does not enforce that the length of the line that opens the delimited block match the length of the line that closes the delimited block, but I think it should. Asciidoctor enforces this requirement, so make sure they match!

Document Attributes (i.e., Variables)

You can save yourself a lot of typing by leveraging document attributes. Document attributes promote frequently occurring references and phrases to the top of the document (or section). You can declare information once by assigning it to an attribute, then refer to that attribute throughout the document. In that sense, you can think of attributes like variables for AsciiDoc.

Storing information in attributes that you declare at the top of the document makes finding the information easy and saves you from having to update the information in multiple places (i.e., keeps the document DRY). When you update the value of the attribute, the value will change everywhere in the document that attribute is referenced.

DRY URLs

A common use for attributes is to store URLs. URLs frequently change, can be rather long, and often have special characters that need escaping. All of these challenges are solved by declaring the URLs in attributes.

You simply assign the URL to a short, easy to remember attribute:

:url-issues: https://github.com/asciidoctor/asciidoctor/issues

Then you reference this attribute whenever you want to use the URL in your document, such as to make a link:

Submit bug fixes and feature requests to the {url-issues}[issue tracker].

Since you may define attributes for many different things, it’s good to employ a system to keep them organized. Let’s study one such system.

Attribute Groups

If you define a lot of document attributes, the document header can get messy. To keep it tidy, it’s a good idea to:

  • Name related attributes using a common prefix (i.e., give them a namespace)

  • Group related attributes together underneath a banner

For example, we recommend using either the prefix url- or uri- when declaring attributes that hold URL values. This is done for several reasons:

  1. It communicates the type of value the attribute holds (e.g., url-issues is a URL for the issue tracker)

  2. It avoids collisions with attributes used for other purposes (e.g., using org for the organization URL could collide with org used for the organization name)

  3. It causes a code assist tool in an editor (such as text auto-completion) to naturally group related attributes (e.g., bring up a list of all URL attributes by typing url-)

Grouping related attributes makes it easy to see where new attributes should be added, thus helping to keep the document header tidy. It also opens the door for reorganizing the groups into separate files in the future. Within a group, you might consider alphabetizing the attributes. One exception to this rule is if an attribute references another attribute, in which case you have to define the attribute being referenced first.

Here are some of the banners we recommend for grouping related attributes in the document header:

  • Metadata - Attributes that define information that goes into the header of the output document for indexing

  • Settings - Attributes that control built-in conversion and formatting behavior

  • Refs - Attributes that are referenced in the content, such as URLs; prefix each attribute by its type to make auto-complete work nicer, such as url- for a URL.

You can use whatever additional groups feel natural to you.

Here’s how that looks when put these recommendations together:

= Document Title
Author Name
// Metadata:
:description: The description of this page.
:keywords: writing, documentation, publishing
// Settings:
:icons: font
:idprefix:
:idseparator: -
// Refs:
:url-project: https://asciidoctor.org
:url-docs: {url-project}/docs
:url-issues:  https://github.com/asciidoctor/asciidoctor
:img-ci: https://github.com/asciidoctor/asciidoctor/workflows/CI/badge.svg

Counters

Counters are backed by document attributes. Since they share the same global namespace as all other document attributes, it’s a good idea to apply a prefix to counter names to avoid collisions with other attributes. For example:

{counter:cnt-step}

Document settings

Most document settings are controlled using attributes. Document settings are configured using attribute entries immediately following the document title (without any blank lines in between). There are several options of interest.

Section numbering

You can enable numbering of sections using the sectnums attribute (off by default).

:sectnums:
Document description

You can set the description of the document using the description attribute. The description is included in the header of the document.

:description: This document catalogs a set of recommended practices for writing in AsciiDoc.

You can break any attribute value across several lines by ending the lines in a \ preceded by a space.

:description: This document catalogs a set of recommended practices \
              for composing documents in AsciiDoc.

You can use this text anywhere in the document by referencing it as an attribute.

{description}
Section title IDs and ID prefixes

IDs are generated for each section title by default. The ID is generated from the section title, prefixed with an underscore (i.e., _) by default. You can change the prefix using the idprefix attribute.

:idprefix: id_

If you want to remove the prefix, assign it to an empty value:

:idprefix:

To disable the auto-generation of section IDs, unset the sectids attribute:

:sectids!:
Table of contents

Set the toc attribute to activate an auto-generated table of contents at the top of the document:

:toc:

Images and Other Media

TODO

Paths

don’t include the images directory in each image reference

Block vs inline

…​

Conditional Inclusion

TODO

how to use, reason for using

Lists

Unordered list markers

AsciiDoc supports both * (one or more) and - (only one) as markers for a top-level list item.

* first
* second
* third

or

- first
- second
- third

However, the dash marker cannot be repeated when defining a list item. This can lead to confusion since AsciiDoc increases the nesting level each time it encounters a different marker. For instance, in the following case, the item that has an asterisk marker is nested inside the first item.

- first
* nested item
- second
- third

This nesting rule is true even when the number of asterisks seems to indicate the level:

*** first
* nested item
*** second
*** third

Yep, that’s right, the second list item is nested inside the first list item.

If you stick to convention, the number of asterisks can represent the nesting level:

* first
** nested item
* second
* third

Now that’s intuitive.

I strongly recommend using the asterisk marker if you are going to be using nested lists.

If you only have top-level list items, then using either marker is reasonable. I may even recommend using the dash marker for lists that are not intended to have nested items and the asterisk marker for lists that do have nested items. That way it’s easy to identify them as different types.

Description lists

They exist!

Separating lists

Adjacent lists sometimes like to fuse. To force the start of a new list, offset the two lists by an empty line comment:

* apples
* oranges
* bananas

//-

* carrots
* tomatoes
* celery

Literal Text

TODO

backticks vs plus and passthough stuffs

recommendation for inline code quote char

Tables

Stacked cells

Leverage them, makes it easy to read