Extensions examples
Features provided by extensions on top of the core language: admonitions, abbreviations, mentions and tags, inline extensions, symbols, and cross-reference numbering.
Admonitions
::: note
Heads up — this is important.
:::<aside class="admonition note">
<p>Heads up — this is important.</p>
</aside>Carve renders ::: blocks by a two-tier rule (PART 9 §12). The eight canonical types — note, tip, warning, danger, info, success, example, quote — render as <aside class="admonition {type}">. Any other identifier (hint, tabs, mermaid, details, …) renders as a generic <div class="{type}">, the fenced-div primitive the block-extension mechanism builds on. A quoted title after the type becomes a <p class="admonition-title"> in either tier; the quotes are stripped and never folded into the class.
Recognized ::: type words
A ::: name opener's behavior keys off the type word (not a class). Only these words are recognized by core; every other word is an ordinary generic <div class="{word}"> that an extension may give meaning to.
| Type word | Renders as | Special behavior |
|---|---|---|
note tip warning danger info success example quote | <aside class="admonition {type}"> | Admonition (PART 9 §12); optional quoted title → <p class="admonition-title"> |
| (pipe) | <div class="line-block"> | Line block - preserves the author's per-line layout / soft breaks (PART 9 §23). The token is the pipe, not a word; an inline ::: {.line-block} is not a fence at all (strict djot) but an ordinary paragraph. |
| (any other word) | <div class="{word}"> | None in core, a generic fenced div; meaning supplied by a Tier-3 extension (e.g. tabs, code-group, mermaid). |
Because the behavior keys to the bare type word, give a purely presentational container a class on an attribute line before the opener ({.mybox} then :::) so you never collide with a recognized type word. The ::: fence takes no inline attributes (strict djot), so an inline ::: {.mybox} is a paragraph, not a div.
A quoted title on a canonical type renders inside the <aside>:
::: tip "Pro Tip"
Save early, save often.
:::<aside class="admonition tip">
<p class="admonition-title">Pro Tip</p>
<p>Save early, save often.</p>
</aside>A custom type renders as a generic <div> with the literal type as its class.
::: hint "Heads up"
Custom call-out.
:::<div class="hint">
<p class="admonition-title">Heads up</p>
<p>Custom call-out.</p>
</div>A [label] after the type (and after any quoted header) is a grouping identifier — the same [label] token a code fence takes. Core ignores it on a standalone block; a group extension (e.g. tabs) uses it as the tab name. It is the canonical replacement for the older tabs {label="…"} / inner-heading convention (both stay supported, deprecated). The selected default-tab marker is not a label — it stays a boolean attribute on the preceding {…} line. Title and label never trade places: under a group extension the quoted header stays inside the panel as its admonition-title line while the [label] moves out to the tab button (and the standalone div-label caption disappears); a header is never used as the tab name.
::: tip "Pro Tip" [Build]
Save early, save often.
:::<aside class="admonition tip">
<p class="admonition-title">Pro Tip</p>
<p class="div-label">Build</p>
<p>Save early, save often.</p>
</aside>The quoted opener header is the only source of the visible admonition-title paragraph. A title="…" key on the preceding attribute line is an ordinary HTML title attribute on the wrapper (a hover tooltip) — a separate channel, not a fallback spelling, and it never collides with the opener header:
{title="attr title"}
::: note "opener title"
Body.
:::<aside class="admonition note" title="attr title">
<p class="admonition-title">opener title</p>
<p>Body.</p>
</aside>The title is ordinary inline content - emphasis, code, and the other inline forms work inside it (unlike a code-fence header, which targets an HTML attribute and stays literal):
::: note "Install *now* via `npm`"
Body.
:::<aside class="admonition note">
<p class="admonition-title">Install <strong>now</strong> via <code>npm</code></p>
<p>Body.</p>
</aside>A typeless generic div may carry a label too (a tab member with no semantic type); core still renders a plain <div>.
::: [First]
First panel.
:::<div>
<p class="div-label">First</p>
<p>First panel.</p>
</div>As the first token after the fence, the bare [label] may sit directly against it (:::[First]), the same allowance a code fence makes for ```[NPM].
:::[First]
First panel.
:::<div>
<p class="div-label">First</p>
<p>First panel.</p>
</div>::: warning
Mind the gap.
:::<aside class="admonition warning">
<p>Mind the gap.</p>
</aside>An admonition may contain multiple block-level children, including lists and code blocks.
::: tip
Quick steps:
- read the docs
- run the demo
:::<aside class="admonition tip">
<p>Quick steps:</p>
<ul>
<li>read the docs</li>
<li>run the demo</li>
</ul>
</aside>Abbreviations
The HTML spec is essential reading.
*[HTML]: HyperText Markup Language<p>The <abbr title="HyperText Markup Language">HTML</abbr> spec is essential reading.</p>Mentions and tags
Hey @alice, see #release-1.0.<p>Hey <span class="mention"><strong>@alice</strong></span>, see <span class="tag"><strong>#release-1.0</strong></span>.</p>Inline extensions
Press :kbd[Ctrl+C] to copy.<p>Press <kbd>Ctrl+C</kbd> to copy.</p>An unrecognized extension name falls back to a generic <span class="ext-NAME">.
:foo[bar]<p><span class="ext-foo">bar</span></p>The extension name is an identifier, which permits a leading _, so _ is a valid extension name (grammar extension_name).
:_[x]<p><span class="ext-_">x</span></p>An identifier must start with a letter or _, so a digit-first name is not a valid extension. :1[x] stays literal text; :a1[x] (a digit after the first letter) is a valid extension and renders as a generic span.
:1[x]
:a1[x]<p>:1[x]
<span class="ext-a1">x</span></p>The extension_content runs up to the first ] (extension_content = {character - ']'}); a nested ] therefore closes the extension early and the remainder is literal text.
:foo[a [b] c]<p><span class="ext-foo">a [b</span> c]</p>An authored {.class} on a generic inline extension merges into the single class attribute — the structural ext-NAME class comes first, then the authored classes. There is never a second class attribute.
:foo[a]{.cls}<p><span class="ext-foo cls">a</span></p>Symbols
:name: is a symbol: a generic named inline placeholder with no built-in semantics. The parser records only the name; resolution is processor configuration, in precedence order: a registered inline-renderer extension handler for symbol nodes, else the renderer symbols map (name → replacement, emitted raw in the target format — processor configuration is trusted, the same class as the renderers map), else the literal text :name:. Emoji substitution is the common use, not a language feature. :type[…] is still an extension and is tried first.
The name starts with a letter, a digit, + or - and continues with word characters, + or -, so the reaction shortcodes :+1: and :-1: parse. It may not start with _: :_x_: would otherwise steal from underline, so :_x: stays literal text. Like mentions and tags, a symbol only opens at the start of content or after a non-word character: a:b:c and 10:30: stay literal text, while (:tada:) is a symbol. (Djot's symbols open intraword; Carve's boundary rule deliberately does not — see the djot divergence notes.)
Great :rocket: and :kbd[Ctrl] is an extension.<p>Great :rocket: and <kbd>Ctrl</kbd> is an extension.</p>Boundary and name-shape cases — all of these stay plain text. The colon is glued to a word character in the first two, and _ cannot open a name in the third:
a:b:c and 10:30: meeting, :_x: too.<p>a:b:c and 10:30: meeting, :_x: too.</p>A symbol is recognized before smart typography, so a name made of typographic punctuation is a symbol rather than a substitution: :+-: is the symbol +-, not a ± between colons. The typographic forms still apply wherever no symbol opens — a +- b is a ± b, and word:+-: has no boundary, so its +- is substituted:
Vote :+1: or :-1:. Tolerance :+-: is a symbol, but a +- b and word:+-: are not.<p>Vote :+1: or :-1:. Tolerance :+-: is a symbol, but a ± b and word:±: are not.</p>A trailing attribute block attaches to the symbol; in HTML output attributes force a <span> wrapper around the resolved (or literal) output so they have an element to land on. Without attributes no wrapper is emitted.
Launch :rocket:{.big} now.<p>Launch <span class="big">:rocket:</span> now.</p>Numbered cross-references
A # in a caption is a number placeholder: the label is the text before it, the number is injected in its place, and </#id> to the element resolves to "label + number".
{#fig-sun}

^ Figure #: A sunset<figure id="fig-sun">
<img src="sun.jpg" alt="A sunset">
<figcaption>Figure 1: A sunset</figcaption>
</figure>Numbers run per label, in document order.

^ Figure #: one

^ Figure #: two<figure>
<img src="a.jpg" alt="one">
<figcaption>Figure 1: one</figcaption>
</figure>
<figure>
<img src="b.jpg" alt="two">
<figcaption>Figure 2: two</figcaption>
</figure>A </#id> to a numbered caption fills its text with the label and number.
{#fig-sun}

^ Figure #: A sunset
See </#fig-sun> for the colors.<figure id="fig-sun">
<img src="sun.jpg" alt="A sunset">
<figcaption>Figure 1: A sunset</figcaption>
</figure>
<p>See <a href="#fig-sun">Figure 1</a> for the colors.</p>Tables use the same placeholder; the number lands in the <caption>.
{#tbl-r}
|= Item |= Qty |
| Apple | 3 |
^ Table #: Stock
See </#tbl-r>.<table id="tbl-r">
<caption>Table 1: Stock</caption>
<thead><tr><th>Item</th><th>Qty</th></tr></thead>
<tbody>
<tr><td>Apple</td><td>3</td></tr>
</tbody>
</table>
<p>See <a href="#tbl-r">Table 1</a>.</p>Labels bucket independently, so other languages number on their own.

^ Abbildung #: erstes

^ Figure #: first<figure>
<img src="a.jpg" alt="a">
<figcaption>Abbildung 1: erstes</figcaption>
</figure>
<figure>
<img src="b.jpg" alt="b">
<figcaption>Figure 1: first</figcaption>
</figure>A #word stays a tag, never a number placeholder.

^ See #data for details<figure>
<img src="c.jpg" alt="chart">
<figcaption>See <span class="tag"><strong>#data</strong></span> for details</figcaption>
</figure>An escaped \# is a literal number sign, never a placeholder.

^ Costs \# units<figure>
<img src="p.jpg" alt="price">
<figcaption>Costs # units</figcaption>
</figure>A caption after a fenced code block makes it a numbered listing: the block is wrapped in a <figure>, and </#id> resolves to "Listing N" on the same per-label counter as figures and tables.
{#lst-greet}
```python
def greet():
return 1
```
^ Listing #: a greeting
See </#lst-greet>.<figure id="lst-greet">
<pre><code class="language-python">def greet():
return 1
</code></pre>
<figcaption>Listing 1: a greeting</figcaption>
</figure>
<p>See <a href="#lst-greet">Listing 1</a>.</p>A caption after a standalone display-math block makes it a numbered equation: the math is wrapped in a <figure>, and </#id> resolves to "Equation N" on its own per-label counter. Only a block whose sole content is the display-math span qualifies; inline math, or display math with trailing prose, is untouched.
{#eq-emc}
$$`E = mc^2`
^ Equation #: mass-energy
See </#eq-emc>.<figure id="eq-emc">
<p><span class="math display">\[E = mc^2\]</span></p>
<figcaption>Equation 1: mass-energy</figcaption>
</figure>
<p>See <a href="#eq-emc">Equation 1</a>.</p>Diagrams and charts
FencedRender claims a fenced code block by its language word and emits a single hydration element for a client library to draw. Eight presets ship: mermaid, d2, graphviz, wavedrom, abc and plantuml (text mode; plantuml also claims puml), plus vega-lite and chart (json mode).
Mermaid covers most of UML - classDiagram, sequenceDiagram, stateDiagram-v2 and erDiagram - and plantuml / puml is the dedicated PlantUML preset for the full UML set:
``` mermaid
classDiagram
class Parser {
+parse(source) Document
-blockPass()
}
Parser --> Document : produces
Document <|-- Section
```Text mode escapes the body inside a <pre>, escaping & and < but preserving > so arrow syntax survives:
<pre class="mermaid">graph LR; A --> B</pre>A chart fence carries a Chart.js config, emitted verbatim in json mode:
``` chart
{"type":"bar","data":{"labels":["V60","Aeropress"],"datasets":[{"data":[12,7]}]}}
```<div class="chart"><script type="application/json">{"type":"bar","data":{"labels":["V60","Aeropress"],"datasets":[{"data":[12,7]}]}}</script></div>These pairs are shown as plain fences rather than ::: compare blocks: the presets are Tier-3, and Tier-3 output is deliberately never pinned in the conformance corpus. See Diagrams & Charts for the full preset table, static rendering, and degradation behavior.