A reader recently asked us how to color individual table rows in AsciiDoc and how to put lists inside cells. Both feel impossible at first, but they are not. Here are three CSS patterns for row highlighting and the one AsciiDoc modifier that unlocks block content in cells, with a clear recommendation at the end.

Why You Can’t Color AsciiDoc Table Rows Out of the Box

AsciiDoc tables look great on the input side. A few pipes, a header row, done. The trouble starts on the output side. asciidoctor converts your |=== block to plain <table><tr><td> HTML and ships it. There is no built-in row-color syntax in AsciiDoc, and default cells only accept inline content — so lists and admonitions just print as text.

The fix lives in two layers. AsciiDoc roles like [.highlight] become CSS classes on the rendered output. That is your hook. From there CSS does the actual styling. For block content inside cells, the a| cell modifier switches a single cell, or an entire column, to full AsciiDoc mode.

Three small pieces, namely roles, CSS, and a|, cover both problems. The same pattern works whether you target HTML with asciidoctor or a PDF via asciidoctor-pdf. For the underlying table syntax itself, the Tables section in our manual is the canonical reference.

How to Put Lists Inside AsciiDoc Table Cells

This is the easier of the two questions. AsciiDoc has a dedicated cell mode for block content. You opt in per cell with a|, or per column with cols="...a...". For a refresher on the underlying syntax, our Learning Path covers lists and admonitions in their own modules.

Single Cell With a|

AsciiDoc table with one cell switched to AsciiDoc mode via the a| modifier, showing a bulleted list inside that cell while the other cells render as plain text

Use a| instead of | for one specific cell. The rest of the table stays in normal mode, which keeps inline asterisks, plus signs, and other punctuation from being misread as block syntax.

[cols="1,2"]
|===
| Normal cell | Standard text without block features

| Cell with a list
a|
* Works
* Even without `cols="1a"`
** Nested item

| Normal again | Plain text
|===

Use this when block content appears occasionally and the rest of the table is plain prose.

Whole Column With cols="...a..."

AsciiDoc table with the whole second column in AsciiDoc mode, rendering a nested bullet list and a NOTE admonition inside cells

When a column reliably holds block content, set the a modifier directly in the column spec. Every cell of that column then renders in AsciiDoc mode without per-cell repetition.

[cols="1a,2a", options="header"]
|===
| Feature | Description

| Nested list
|
* Main point A
** Sub-point A.1
** Sub-point A.2
* Main point B

| Admonition
|
NOTE: Admonitions work inside table cells too.
|===

One side effect to know: in column-wide AsciiDoc mode, a leading asterisk in plain text becomes a bullet. If you have content where literal * should stay literal, prefer per-cell a|.

How to Color Rows in AsciiDoc Tables: Three Approaches

There is no [row-color=yellow] attribute in AsciiDoc. There are three patterns that get you the same result with very different trade-offs. The first one is the one we recommend in almost every case.

Variant A: Utility Class per Cell (Recommended)

AsciiDoc table styled with Variant A, the recommended approach: the 2FA row is fully highlighted in yellow and one cell of the Password reset row is highlighted individually

The idea is simple. Mark cells with an inline role like [.highlight]#text#. AsciiDoc renders that as a <span class="highlight"> inside the <td>. Then a single CSS rule using :has() colors the surrounding cell.

[cols="1,2,1", options="header"]
|===
| Feature | Description | Status

| Login                | Standard implementation              | OK
| [.highlight]#2FA#    | [.highlight]#Whole row highlighted#  | [.highlight]#Review#
| Logout               | Works                                | OK
| Password reset       | [.highlight]#Just this one cell#     | OK
|===

For a single cell, mark only that cell. For a full row, repeat the role on every cell of that row. Mixing colors within a row works the same way: use a different class per cell.

The matching CSS uses :has() to color the <td> whenever it contains a marker span. New colors are one extra rule each:

table.tableblock td:has(.highlight)       { background-color: #FFF59D; }
table.tableblock td:has(.highlight-red)   { background-color: #FFCDD2; }
table.tableblock td:has(.highlight-green) { background-color: #C8E6C9; }
table.tableblock td:has(.highlight-blue)  { background-color: #BBDEFB; }

.highlight,
.highlight-red,
.highlight-green,
.highlight-blue { background: transparent; }

The marker spans themselves are made transparent so only the surrounding cell shows color. Reorder your rows freely: the highlighting follows the content.

One caveat: :has() requires a modern rendering engine. All current browsers support it. For asciidoctor-pdf, confirm your stylesheet pipeline understands it before you commit to this pattern. The same role-to-class bridge powers everything from custom navigation to entire site templates, as shown in our walkthrough of integrating website elements via docinfo and documented end to end in the Styling with CSS chapter of our manual.

Variant B: Table Role Plus nth-child

AsciiDoc status legend table styled with Variant B, rows 2 to 4 colored yellow, red and green via nth-child selectors based on their position

When the table is genuinely a status legend with a fixed row order, you can move all knowledge into CSS. Put a role on the table and color rows by index.

[.status-table]
[cols="1,2,1", options="header"]
|===
| Status  | Description                  | Priority

| Normal  | Standard row                 | Low
| Warning | Row 2 — yellow per theme     | Medium
| Error   | Row 3 — red per theme        | High
| OK      | Row 4 — green per theme      | Low
|===
table.status-table tbody tr:nth-child(1) { background-color: transparent; }
table.status-table tbody tr:nth-child(2) { background-color: #FFF59D; }
table.status-table tbody tr:nth-child(3) { background-color: #FFCDD2; }
table.status-table tbody tr:nth-child(4) { background-color: #C8E6C9; }

Compact on the AsciiDoc side, but positional. Move a row up and the colors stay at their old index. Use this only when row order is part of the contract.

Variant C: Purely Positional

AsciiDoc table styled purely positional with Variant C, the second row yellow and the fourth row red, while all coloring lives in CSS and the source AsciiDoc carries only a hook role

Nothing on the AsciiDoc side beyond a hook role that targets the CSS at this specific table. Useful for legacy content you cannot edit, or recurring templates where every instance has the same shape.

[.positional-demo]
[cols="1,2,1", options="header"]
|===
| Status  | Description                | Priority

| Normal  | Standard row               | Low
| Warning | This row is yellow         | Medium
| Normal  | Plain row in between       | Low
| Error   | This row is red            | High
| OK      | Plain row                  | Low
|===
table.positional-demo tbody tr:nth-child(2) {
  background-color: #FFF59D;
}
table.positional-demo tbody tr:nth-child(4) {
  background-color: #FFCDD2;
}

The hook role prevents the selector from leaking into other tables on the page. Beyond that it is the same trade-off as variant B, with even less context on the AsciiDoc side.

Combining Variant A With Lists in a Cell

AsciiDoc table combining Variant A with the a| cell modifier, a highlighted cell containing a three-item bullet list while a second row stays unhighlighted

The two questions intersect when a highlighted cell also needs a list. For block content inside a cell, the inline role [.highlight]#...# will not wrap the whole block. Use [.highlight] as a block role on a delimited open block instead. Inside an a| cell this gives you the marker <div> that the td:has(.highlight) rule needs.

[cols="1,3a", options="header"]
|===
| Feature | Details

| [.highlight]#2FA#
|
[.highlight]
--
This cell is highlighted and still contains a list:

* TOTP via authenticator
* SMS fallback
* Recovery codes
--

| Logout
| Standard, no highlight.
|===

The -- lines define an open block. Block role on top, content in between, normal AsciiDoc inside. The :has() rule from variant A finds the .highlight div and colors the entire <td>.

Which Approach to Pick

For nearly every team migrating real content from Confluence, Word, or another legacy source, start with variant A. It scales to single cells, full rows, mixed colors per row, and arbitrary patterns. New colors are one CSS rule. Rows can move without breaking the visual logic. It combines cleanly with a| for cells that hold lists, code, or admonitions.

  • Variant A: the default. Markup carries semantics; CSS does the styling. Survives row reordering.
  • Variant B: only when the table is a fixed-order legend and the row index is genuinely part of the meaning.
  • Variant C: only when you cannot touch the AsciiDoc source and the schema is stable.

Conclusion

Roles, CSS, and a| are the full toolkit for styling AsciiDoc tables. Once the three are in place, the choice between row highlighting and rich cell content stops being a question of AsciiDoc syntax. It becomes a question of how much information you want in the markup versus the stylesheet.

For more on shaping the AsciiDoc output beyond tables, see our guide to the Tufte information-design style in adoc Studio and the broader modularization patterns for AsciiDoc. To go deeper into the AsciiDoc table syntax itself, work through the Tables module of our Learning Path and the AsciiDoc reference in the manual. For the bigger picture on separating content from presentation, the Docs-as-Code pillar guide is the starting point. If you are still in the middle of moving content out of Confluence, Word, or DITA, our no-nonsense migration playbook collects the practical steps.

FAQ: Common Questions About AsciiDoc Table Styling

Can I color individual rows in AsciiDoc tables?

Not with built-in syntax. AsciiDoc has no row-color attribute. Use CSS targeted via roles. The most flexible pattern is [.highlight]#text# on each cell of the row, combined with td:has(.highlight) in CSS.

How do I put a list inside an AsciiDoc table cell?

Prefix the cell with a| to switch a single cell to AsciiDoc mode. For an entire column, use cols="...a..." in the column spec. Inside an AsciiDoc-mode cell, normal block syntax for lists, code blocks, and admonitions works as expected.

Does this work with asciidoctor-pdf?

Variant A relies on the CSS :has() selector, which is supported by all current browsers and by recent versions of asciidoctor-pdf. Test your stylesheet pipeline once before relying on it. Variants B and C use only nth-child, which is universally supported.

Why doesn’t `[.highlight]#…#` work inside a cell with a list?

The inline form wraps only inline content. For block content like lists, use [.highlight] as a block role on a delimited open block (----). That gives you a <div class="highlight"> that the :has() rule can find.

What’s the difference between `a|` and `cols=”…a…”`?

a| switches one specific cell to AsciiDoc mode. cols="...a..." switches every cell of a column. Use the column-wide form when block content is the rule, the per-cell form when it is the exception, because column-wide mode treats leading asterisks in plain text as list bullets.

Are AsciiDoc roles the same as CSS classes?

Effectively, yes. A role like [.highlight] on inline content becomes <span class="highlight">, on a block it becomes a class on the block element. From there, CSS does the rest. This is the same mechanism used to bridge AsciiDoc and full HTML/CSS templates.