This post is an ongoing experiment demonstrating the “front end” output produced by various expressions of AsciiDoc syntax, and it will show off some of the CSS and JavaScript work I’ve done in this area.
The styling and effects you see here are not “vanilla” AsciiDoc output. After rendering AsciiDoc to HTML, it still needs to be styled with CSS, and most interactive effects need to be defined with CSS or JavaScript.
Styling and effects are outside this post, but you can find the sources for all of this at https://github.com/DocOps/lab/tree/main/_sass and https://github.com/DocOps/lab/blob/main/assets/js/main.js.
The Examples
It’s time to examine the AsciiDoc awesome sauce.
Example Block Example
First, let’s address something you don’t see every day in tech blogging: an actual, semantically (and visually) distinct block for exhibiting examples.
I typically use example blocks to show results, but you can also use them for code listings as well. Let’s make this one a combo.
name: example
array:
- item1
- item2
- item3
.Example block containing a code block
====
I typically use example blocks to show _results_, but you can also use them for code listings as well.
Let's make this one a combo.
[source,yaml]
----
name: example
array:
- item1
- item2
- item3
----
====
We will be using example blocks throughout this post to show off rendered output, in contrast to the code blocks we’ll use to expose the source of those blocks.
Transclusion Examples
The AsciiDoc include:: directive is awesome.
It embeds the content of another file — in whole or in part — into a parent document.
Such includes are also used throughout the source for this post.
Nearly every example of block content in this post is transcluded from an adjacent file called _asciidoc-snippets.adoc, which is subvidivded using AsciiDoc include tags, demarcated like so:
This part of the document will not be included.
// tag::segment-name[]
Everything between tagged lines is included in the "calling" document.
// end::segment-name[]
Nothing outside the tags will be included in a tagged include directive
The desired line of AsciiDoc source in a nearby or even online file can be transcluded with a line like:
include::_asciidoc-snippets.adoc[tag="segment-name"]
Everything between tagged lines is included in the "calling" document.
But this works for files of any other format, as well. I use it a lot for YAML files.
properties:
# tag::arbitrary-tagname[]
- key: tagged-content
description: |
This exemplifies demarcated tagging in YAML, for inclusion into AsciiDoc files.
status: awesome!
# end::arbitrary-tagname[]
[source,yaml]
include::_metablog/_asciidoc-yaml-snippet.yml[tag=arbitrary-tagname]
- key: tagged-content
description: |
This exemplifies demarcated tagging in YAML, for inclusion into AsciiDoc files.
status: awesome!
Admonition Examples
What some systems call “alerts” or “callouts” or “notices”, AsciiDoc calls “admonitions”.
Let’s kick things off with an example of a simple note admonition.
|
This is a NOTE admonition with a Lucide info circle icon. It should display with a blue color scheme and an information icon. It can contain multiple paragraphs, like this second one. |
Let's kick things off with an example of a simple note admonition.
[NOTE]
====
This is a NOTE admonition with a Lucide info circle icon. It should display with a blue color scheme and an information icon.
It can contain multiple paragraphs, like this second one.
====
That wasn’t simple enough for you? Check this out…
Those `====` demarcation characters and the `[NOTE]` heading are only necessary when there is something special about your admonition content, such as multiple paragraphs or distinct styling.
TIP: Normally, this works just fine to create an admonition block.
And blogging in standardized lightweight markup doesn't get much simpler than that.
Those ==== demarcation characters and the [NOTE] heading are only necessary when there is something special about your admonition content, such as multiple paragraphs or distinct styling.
| Normally, this works just fine to create an admonition block. |
And blogging in standardized lightweight markup doesn’t get much simpler than that.
Callout Example
Speaking of “callouts” — when AsciiDoc uses that term, it means annotations inside a code block.
def hello_world message="Hello, world!", case="upper" (1)
message_cased = case == "upper" ? message.upcase : message.downcase (2)
puts message_cased (3)
end
| 1 | defines the method with default parameters |
| 2 | processes the message based on the case parameter |
| 3 | outputs the processed message |
If you highlight the contents and use your operating system’s copy-to-clipboard, or if you click the copy button, you’ll see that only the source code copies, not the callout numbers.
Click here to reveal a textarea box you can paste into
See? No callouts were picked up in the copy operation!
[source,ruby]
----
def hello_world message="Hello, world!", case="upper" # <1>
message_cased = case == "upper" ? message.upcase : message.downcase # <2>
puts message_cased # <3>
end
----
<1> defines the method with default parameters
<2> processes the message based on the case parameter
<3> outputs the processed message
Blockquote Examples
Block quotes are a staple of blogging. Here are two of my favorite ways to use them.
This is a blockquote with a citation. Not something you use in tech writing very often, but quite common in all kinds of blogging.
[quote, Dr Meta]
This is a blockquote with a citation.
Not something you use in tech writing very often, but quite common in all kinds of blogging.
I have always loved blockquotes, so I made my own style that can be designated with a simple role argument in AsciiDoc.
This is some text that I want to highlight as a pull quote. I’ve styled it to be distinct from a standard blockquote, and I use them to preview interesting content that appears later in a post.
[quote,role=pullquote]
This is some text that I want to highlight as a pull quote.
I've styled it to be distinct from a standard blockquote, and I use them to preview interesting content that appears later in a post.
Table Examples
There is no question that AsciiDoc’s lightweight table markup is excellent.
.This table has an automatically numbered caption
[cols="1,1,1",options="header"]
|===
| Header 1 | Header 2 | Header 3
| Cell 1
| Cell 2
| Cell 3
| Cell 4
| Cell 5
| Cell 6
| Cell 7
| Cell 8
| Cell 9
|===
| Header 1 | Header 2 | Header 3 |
|---|---|---|
Cell 1 |
Cell 2 |
Cell 3 |
Cell 4 |
Cell 5 |
Cell 6 |
Cell 7 |
Cell 8 |
Cell 9 |
AsciiDoc table syntax doesn’t rely on mimicking the shape of the table or its columns and cells, so you can break it down and organize the markup in a way that makes sense to you. AsciiDoc does not require you to make ASCII art like some table syntaxes.
But if you need to make it complex, you can do that too.
| Multiverse Documentation Planning Matrix — Because simple tables are for mortals | |||||
|---|---|---|---|---|---|
Persona |
Toolkit |
Sample Artifact |
KPI |
||
End-User Pain points:
|
AsciiDoc + Antora |
Quickstart Guide
|
⏱ Time-to-First-Success ↓ |
||
Admin User |
Paligo + Snagit |
Troubleshooting Article |
☎ Support Tickets/1k users ↓ |
||
——— Sprint 42: Docs Ops Experiments ——— |
|||||
Developer |
AsciiDoc + Make + PlantUML |
Code-Adjacent HOWTO
|
⚙ Build Failures Blamed on Docs ↓ |
||
Jekyll + Kramdown |
API Changelog
|
🔁 PRs Merged w/ Docs ↑ |
|||
Antora + Lunr |
“Docs as Feature” RFC
— A slightly dramatic PM
|
🧭 Feature Discoverability ↑ |
|||
——— Release Train: 2.3 “Quantum Marmot” ——— |
|||||
Composite Artifact: “One Doc to Rule Them All”
|
📈 Adoption Rate ↑ |
||||
Internal Auditor |
AsciiDoc + CSV + a dash of awk |
Compliance Matrix (SOC 2, ISO 27001) |
σ = 2.3± [1] |
||
Site Reliability Engineer |
Runbook Pack |
Incident Bundle
|
🔍 Incident Response Time ↓ |
||
In conclusion: Tables are for communicating structure, not suffering. Use colspans/rowspans judiciously, and never apologize for beautiful grids. |
|||||
Click to expose the source code for the crazy table
.DocOps Lab ✦ Table of Delightfully Over-Engineered Examples
[cols="^1,<2,3,>1",width=100%,options="header,footer",frame=all,grid=all,stripes=even]
|===
4+^h| Multiverse Documentation Planning Matrix — Because simple tables are for mortals
^s| Persona
^s| Toolkit
^s| Sample Artifact
^s| KPI
// ── A multirow persona cell with block content; spans 2 body rows.
<a| *End-User*
*Pain points*:
* “Where is the button?”
* “What’s a token?”
* “Why is my coffee YAML-flavored?”
| AsciiDoc + Antora
a| Quickstart Guide
TIP: Keep it friendly. If the docs feel like a boss fight, you already lost.
^| ⏱ *Time-to-First-Success* ↓
| *Admin User*
| Paligo + Snagit
| Troubleshooting Article
| ☎ *Support Tickets/1k users* ↓
// ── A “section header” row that spans all four columns.
4+^| ——— Sprint 42: Docs Ops Experiments ———
// ── A persona cell that spans three rows; note the dot-prefix count for rowspan.
.3+^| *Developer*
| AsciiDoc + Make + PlantUML
a| Code-Adjacent HOWTO
[source,ruby]
----
def deploy env='prod'
puts "Shipping to #{env}…"
end
----
^| ⚙ *Build Failures Blamed on Docs* ↓
| Jekyll + Kramdown
a| API Changelog
* Sections grouped by `part`
* Rendered to HTML/PDF
* Includes `{%- include -%}` sorcery
^| 🔁 *PRs Merged w/ Docs* ↑
| Antora + Lunr
a| “Docs as Feature” RFC
[quote, 'A slightly dramatic PM']
____
If it isn’t documented, did it even ship?
____
^| 🧭 *Feature Discoverability* ↑
// ── Another section header with full-row colspan.
4+^.^| ——— Release Train: 2.3 “Quantum Marmot” ———
// ── Here we demonstrate a single row that contains a 3-column cell + one regular cell.
3+<a| **Composite Artifact: “One Doc to Rule Them All”**
* Installation (for Humans)
* API (for Machines)
* Architecture (for Historians)
+
NOTE: This cell spans *three* columns. The rightmost cell is independent.
^| 📈 *Adoption Rate* ↑
// ── Fun with inline formatting, footnotes, and subscripts/superscripts.
| *Internal Auditor*
| AsciiDoc + CSV + a dash of awk
| Compliance Matrix (SOC 2, ISO 27001)
| σ = 2.3^±^ footnote:[Margin of error calculated by a friendly spreadsheet goblin.]
// ── A cell that spans two columns on the right; pair with two normal cells on the left.
| *Site Reliability Engineer*
| Runbook Pack
<a| *Incident Bundle*
* Playbooks
* Postmortem template
* “Pager-Yoga” breathing guide
^| 🔍 *Incident Response Time* ↓
// ── Closing “totals/notes” row spanning all columns as a footer.
4+^s| In conclusion: Tables are for communicating *structure*, not suffering. Use colspans/rowspans judiciously, and never apologize for beautiful grids.
|===
Yes, that really was all done with AsciiDoc markup.
However, I am pleased to admit that ChatGPT actually wrote all of that content based on a prompt to create “really complex AsciiDoc table” and to “make the content interesting, maybe clever/cute/funny”. I was so shocked, I decided to use it as-is.
Description List Example
One feature of AsciiDoc that Markdown does not even attempt is the description or definition list: <DL> in HTML terms.
- AsciiDoc
-
A lightweight markup language that is particularly well-suited for technical documentation and blogging, offering a rich set of features for structuring content.
- Markdown
-
A widely-used lightweight markup language known for its simplicity and ease of use, but with fewer features compared to AsciiDoc.
- lightweight markup
-
A category of markup languages designed to be easy to read and write in plain text, while still allowing for formatting and structuring of content.
AsciiDoc::
A lightweight markup language that is particularly well-suited for technical documentation and blogging, offering a rich set of features for structuring content.
Markdown::
A widely-used lightweight markup language known for its simplicity and ease of use, but with fewer features compared to AsciiDoc.
lightweight markup::
A category of markup languages designed to be easy to read and write in plain text, while still allowing for formatting and structuring of content.
DLs are a semantically powerful format for “parameterized data” — I use it all the time in my docs, and whenever an LLM tries to do this with bulleting and bold, I cringe.
- **AsciiDoc:**
A lightweight markup language that is particularly well-suited for technical documentation and blogging, offering a rich set of features for structuring content.
Importantly, this will not generate a DL list, which means screen readers and interpreters may not even associate the bolded term with its description.
Other List Types
AsciiDoc also supports ordered lists (numbered), unordered lists (bulleted), and checklists (task lists), along with nesting of each type, even across types.
Ordered Lists
Ordered lists can be pretty powerful, in that you can start an re-start them at any number.
An ordered list broken up by an admonition:
-
Install the NPM packages.
npm install
-
Build the app.
npm run build
| Your app is now built and ready to deploy! |
-
Deploy the app.
npm run deploy
An ordered list broken up by an admonition:
. Install the NPM packages.
+
[.prompt]
npm install
. Build the app.
+
[.prompt]
npm run build
[NOTE]
Your app is now built and ready to deploy!
[start=3]
. Deploy the app.
+
[.prompt]
npm run deploy
Unordered Lists
For unordered lists, AsciiDoc supports using either - or * characters for bullet markup.
Nesting is done by adding bullets — no need to keep track of indentation.
-
First item
-
Sub-item A
-
Sub-sub-item i
-
Sub-sub-item ii
-
-
Sub-item B
-
-
Second item
-
Another first-level item
-
Nested with dashes
-
Back to dashes at first level
-
-
Final item
.An unordered list with deep nesting square bullets
[square]
* First item
[square]
** Sub-item A
*** Sub-sub-item i
*** Sub-sub-item ii
** Sub-item B
* Second item
.Hyphen bullets with nested ordered list
- Another first-level item
. Nested with dashes
. Back to dashes at first level
- Final item
Sidebar Example
Now we get to what might be my favorite AsciiDoc feature, which is a lovely holdover from DocBook: sidebars.
This would be mainline text that pertains very directly and prominently to the subject. The reader is reading or scanning along, but at this point the author wants to at least introduce some auxiliary content that is relevant but not strictly necessary to the main point, or not a priority at this stage.
Below that sidebar element, the mainline text would resume, and the reader can choose to read the sidebar, stash it for later, or skip it altogether.
You can also just stick all your sidebars at the end of the post like little appendices, optionally linking to them from the point in the text where they are relevant.
Click to see how the sidebar was coded
This would be mainline text that pertains very directly and prominently to the subject.
The reader is reading or scanning along, but at this point the author wants to at least introduce some auxiliary content that is relevant but not strictly necessary to the main point, or not a priority at this stage.
.A sidebar about sidebar authoring
****
Sidebars are for textbooks and magazines, at least in the sense of being placed alongside the main content to provide additional context, definitions, or related information.
In HTML, sidebars tend to be for navigation and others static content like masthead info.
But the _concept_ of a content-adjacent sidebars is useful in all kinds of technical writing, including blogging.
Sidebars are for content that is pinned or otherwise relevant to the main topic, but which might be a distraction if dumped inline.
It doesn't quite belong as a section or even a subsection.
The content is not totally required knowledge, but it relates to the content and is not yet a whole other lesson.
You want readers to at least be able to make note of it, even at risk of interrupting their reading flow.
In AsciiDoc, sidebars can contain just about any other block or inline content.
When it comes to layout, sidebars should either be collapsed or they should actually be pinned to a bar that runs alongside the content.
And in either case, they should be dismissable and stashable so a reader can skip them now but come back to them later.
****
Below that sidebar element, the mainline text would resume, and the reader can choose to read the sidebar, stash it for later, or skip it altogether.
You can also just stick all your sidebars at the end of the post like little appendices, optionally linking to them from the point in the text where they are relevant.
Examples Wrapup
Those were several of my favorite semantic block elements supported by AsciiDoc. Not even mentioned were the enumerable inline semantic features like inline roles and what can be done with them on the front end, nor AsciiDoc’s terrific built-in support for footnotes[2].
Both the DocOps Blog and this MetaBlog will continue to explore AsciiDoc syntax and output from time to time, but those were some of the highlights.
However, this post is not done yet, and neither are the AsciiDoc examples. We still have to answer the big question…
AsciiDoc is Better than What, Though?
Well, it’s definitely better than rich-text editors (“WYSIWYG”) like those typically used in WordPress and other blogging platforms — at least if your subject matter is code. I guess you might be a tech blogger who writes about gadgets, in which case I have no idea, you do you.
But since the vast majority of blogging about coding is done in Markdown, let’s compare AsciiDoc to the leading brand.
Markdown vs AsciiDoc
Admittedly, I have never blogged in Markdown, but since AsciiDoc is basically a superset of Markdown, you’d be hard-pressed to find advantages of Markdown itself.
There surely are probably syntax situations where Markdown has the edge over AsciiDoc, though I am only aware of a couple. I’ll take you through them here, with some meanderings planned along the way.
Hyperlink Syntax
Let’s look at some Markdown.
You're reading along in Markdown and then you come to a link like [Asciidoctor](https://asciidoctor.org), and that seems natural
Whereas in AsciiDoc, you're reading along and then https://asciidoctor.org[Asciidoctor] BAM! you got hit with a URL that didn't really matter.
Mind you, both of these markups generate the same HTML output: Asciidoctor. But I have to admit, I have come to slightly prefer the label-first Markdown approach, mainly for the effect when I’m reading the text as code, even though it uses two extra characters.
Click here for a mega-meta mini tangent
What other ways is Markdown syntax thought to be stronger than AsciiDoc?
Code Blocks Syntax? (Nope)
You may be thinking you prefer Markdown’s simple ``` notation for demarcating code blocks, which are after all among the most frequently employed elements in blogging about code.
Not so fast. If you prefer that style, Asciidoctor supports it perfectly. Take a look:
Here is the effect of using just 3 backticks to sandwich literal content.
def hello_world():
print("Hello, world!")
This was written in a code block denoted with `+++```+++` literals.
```python
def hello_world():
print("Hello, world!")
```
```asciidoc
This was written in a code block denoted with `+++```+++` literals.
```
When it comes to literal blocks, which are typically used to represent plain text or terminal input/output in docs, AsciiDoc has two or three ways of expressing this element, whereas Markdown has only one that I am aware of.
Converters tend to render this as a <pre> block without a <code> inset.
This is a literal block, the syntax for which was a simple 1-space indent
....
This literal block is fenced by four dots (periods), which allows for
non-contiguous
multiline text.
....
[literal,role=prompt,title=Properly labeled command literal]
Use a heading line if you wish to assign a role or other options.
This is a literal block, the syntax for which was a simple 1-space indent
This literal block is fenced by four dots (periods), which allows for non-contiguous multiline text.
Use a heading line if you wish to assign a role or other options.
There really is no guaranteed Markdown equivalent to this, but you can try something like what follows:
This line was indented by 4 spaces to indicate a literal.
You can keep adding lines that are indented by 4 spaces.
Some converters will render this embedded in `<code>` tags anyway.
If you want your literal blocks to be handled distinctly from code blocks, or if you want to do anything cool with them or add caption/labels, you’re going to have to jerry rig it in Markdown, probably by adding explicit HTML.
HTML Embedding? (Nope)
Speaking of passthroughs, you may have been thinking all along that you prefer the way Markdown supports HTML tags throughout.
You can just write <div class="foo"> and it works, right?
Cool.
Same with AsciiDoc.
You just use those passthrough markers inline, or the 4-char version (++++) for a block.
You're looking at HTML right now, sourced inside an AsciiDoc file.
It can be used for anything, including to embed adorable SVG images you come up with after an all-night coding session, like this:
++++
<!-- HTML block with a gray background and white text and an embedded SVG file of a cute logo -->
<div style="background-color: #414141; color: #333; padding: 10px; border-radius: 5px;">
<p style="font-weight: 900">You're looking at HTML right now, sourced inside an AsciiDoc file.</p>
<p>It can be used for anything, including to embed adorable SVG images you come up with after an all-night coding session, like this:</p>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="#ffcc00"/>
<text x="50%" y="50%" font-size="20" text-anchor="middle" fill="#333" dy=".3em">Cute!</text>
</svg>
</div>
++++
So, anything you can do in an HTML page, you can do in AsciiDoc files.
Strikethrough Syntax (Gotcha, AsciiDoc!)
Okay, okay, I realize I only gave one example of where Markdown syntax is superior, and then I slipped right back into showing how great AsciiDoc is.
So I will give you truthfully the only other syntax advantage of Markdown over AsciiDoc that I am aware of, and that is bloggers’ favorite, ubiquitous strikethrough effect.
This is ~~strikethrough~~ text.
That same syntax in AsciiDoc produces: This is ~strikethrough~ text.
Oof. Not cool, AsciiDoc.
An inline role is required to do this in my beloved format:
This is the built-in role for [line-through]#strikethrough#.
This is an alternate method [.strike]#for slightly# with fewer characters.
This is the built-in role for strikethrough.
This is an alternate method for slightly with fewer characters.
Then again, if you’re into using inline highlighting in your text, AsciiDoc makes that trivial with #inline highlighting# syntax, while you have to use <mark>inline highlighting</mark> HTML tags in Markdown.
Sorry, I realize I have done it again, but this is why I’m so confident that if you like blogging in Markdown, you will love blogging in AsciiDoc.
Conclusion
AsciiDoc is nearly as advantageous for blogging about code as it is for officially documenting software. You can do so much more without missing literally anything you can do with Markdown.
I encourage anyone to provide other examples of superior Markdown syntax or capability.
The biggest thing going for Markdown is surely that every programming language and code-oriented content platform supports some flavor of it. That is a huge plus for Markdown, but AsciiDoc’s widespread advantages make it worth setting up if you blog in any of these SSGs.
I would say it’s even worth choosing or migrating to a platform from that list, with Jekyll, Hugo, and 11ty being my favorites for blogging.