add container directives, and admonitions as their first user

`:::name{…}`, a body of Markdown, then `:::`. This spends the form reserved by
ADR-0059 rather than leaving it a promise — and building it back then would have
been a mechanism with no user, which is what the reserve was avoiding.

The body renders first and reaches the theme fragment as .Body, already HTML, so
emphasis, links, subscripts and icons all work inside an admonition. That is one
addition to the theme contract, additive as the stability rule requires, and two
lines of core — which is what the remaining budget allowed.

Rendered by a transformer rather than the node renderer, for the same reason an
include is: rendering a subtree needs the document, and a node renderer never
gets one.

When the theme has no template for a kind, the engine writes the body out
unwrapped. Same principle as an unknown icon keeping its text, and it matters
more here: a theme not knowing one name must never cost an author paragraphs,
and an unstyled aside is a far smaller failure than a missing one.

The leaf parser was parameterised by prefix rather than copied — a second copy of
parsing logic is a stop condition, and the two forms differ by one colon.

Containers do not nest: a `:::` inside closes the one it is in, the same limit an
include carries. Stated in the ADR and the contract rather than left to be found.

core 2796/2800, ext 1743/2000, 34 gates green, 0 warnings.
This commit is contained in:
Claude Opus 5
2026-08-01 22:00:33 +06:00
committed by bdeshi
parent df9335df33
commit 78b8c51dff
13 changed files with 302 additions and 73 deletions
+20
View File
@@ -1028,3 +1028,23 @@ this could damage prose are tests. Expensive — a theme cannot tell the engine
unknown name costs a fragment execution before falling back; and the reference theme's Unicode set is a
`{{if}}` chain, which is fine at six names and would not be at sixty.
Revisit if: a theme wants to declare its set to the engine — which is the settings cascade, not this.
## ADR-0064 — Container directives, and a fragment may receive a body
Date: 2026-08-01 · Status: accepted (spends the `:::` form reserved by ADR-0059; extends the theme contract
additively with `Fragment.Body`)
Decision: `:::name{…}`, a body of Markdown, then `:::` on its own line. The body is rendered first and handed
to the theme fragment of that name as `.Body`, already HTML. One level: a `:::` inside a container closes it.
When the theme has no template for the kind, the engine writes the body out unwrapped. The reference theme
defines `note`, `warn` and `tip`, each an `<aside>` with an optional title.
Why: admonitions are the first call that wraps content rather than replacing a line, which is what the
reserved form was for — building it in ADR-0059 would have been a mechanism with no user. The body is
rendered by a transformer rather than the node renderer for the same reason an include is: rendering a
subtree needs the document, and a node renderer has no way to get one. Falling back to the bare body is the
same principle as an unknown icon keeping its text — a theme not knowing one name must never cost an author
paragraphs, and an unstyled aside is a far smaller failure than a missing one.
Consequence: cheap — `epigraph`, `spec` or anything else that wraps content is now a template, not a code
change; `.Body` is two lines of contract. Expensive — containers do not nest, which is a real limit for a
warning inside a note; every kind needs its own template, so a theme with twelve admonition styles writes
twelve; and the body is rendered before the fragment sees it, so a fragment cannot choose *not* to render it.
Revisit if: nesting is genuinely wanted — which needs a fence length rule like CommonMark's code fences, not
a deeper parser.