record ADR-0036: shortcodes render through theme templates
Two things already recorded settle the shape. The theme contract says the engine decides nothing about how content looks, "including how media is embedded", so a <figure> assembled in Go would be the engine dressing content. And invariant 2 wants the trusted/untrusted split as real code, not goldmark's default. Both are satisfied by the same design: parse the shortcode into an AST node, then render it by executing a theme template of that name. Raw HTML stays disabled, so every byte of HTML on a page came from a template the site owns, and an author's text survives only as arguments that html/template escapes. Recorded before the code because the syntax authors type is a disk contract, and because a feature that cannot render itself needs a partial-rendering function passed in at wiring time — a consequence worth agreeing to in advance.
This commit is contained in:
@@ -440,3 +440,24 @@ can be added or dropped without touching a URL. Expensive — a Bengali reader s
|
||||
changing that later needs this decision reversed plus an alias for every path already published.
|
||||
Revisit if: Bengali becomes the dominant language of the site — the trigger ADR-0009 already names. That
|
||||
is a default-language change, not per-variant slugs.
|
||||
|
||||
## ADR-0036 — Shortcodes parse to engine-built nodes and render through theme templates
|
||||
Date: 2026-07-30 · Status: accepted
|
||||
Decision: a shortcode is `{{< name key="value" >}}` alone on a line, parsed into an AST node by a goldmark
|
||||
block parser. It renders by executing a theme template of the same name with its arguments as data — the
|
||||
engine supplies no markup — and raw HTML in Markdown stays disabled, so the only HTML on a page comes from
|
||||
a template the site owns. A shortcode whose template is missing logs and renders nothing rather than
|
||||
failing the request.
|
||||
Why: two constraints meet here and both are already recorded. `theme-contract.md` says the engine decides
|
||||
nothing about how something looks, "including how media is embedded", so a `<figure>` built in Go would be
|
||||
the engine dressing content. And invariant 2 needs the trusted/untrusted split to be real code rather than
|
||||
goldmark's default: with `html.WithUnsafe()` still off, an author's raw HTML is dropped while a
|
||||
shortcode's output is trusted *because a template produced it*, and the author's bytes survive only as
|
||||
arguments, which `html/template` escapes.
|
||||
Consequence: cheap — markup lives where markup belongs, a theme restyles a shortcode by redefining one
|
||||
template, and the escaping is the standard library's rather than ours. Expensive — a feature under
|
||||
`internal/ext` cannot render itself, so it is handed a partial-rendering function at wiring time
|
||||
(`cmd/khosra/wire.go`), and the shortcode's argument names become part of the theme contract, additive
|
||||
only.
|
||||
Revisit if: a shortcode needs to emit something no template can express. That is an argument for a new
|
||||
contract field, not for the engine writing HTML.
|
||||
|
||||
Reference in New Issue
Block a user