Steps to Reproduce
{% figure caption="My file" %}
{% snippet path="a.ts" /%}
{% /figure %}
Expected
Either the fence appears inside the figure, or the rune says it cannot hold one.
Actual
{ "name": "figure",
"attributes": { "data-rune": "figure", "typeof": "ImageObject" },
"children": [
{ "name": "figcaption", "attributes": { "data-name": "caption" },
"children": ["My file"] } ] }
The <pre> is gone. No error, no warning, no build diagnostic. The page renders a caption floating under nothing.
Notes
- Reproduced with a
{% snippet %} because that is the case that surfaced it, but the cause has nothing to do with snippet — any non-media child is dropped the same way. A fenced code block written directly in the body behaves identically. typeof="ImageObject" is emitted unconditionally, so a figure holding anything other than an image also publishes a wrong schema.org type. That part is SPEC-130's channel, but the trigger is the same assumption.- This blocks the composition SPEC-062 recommends. Its CSS comment tells authors wanting labelled chrome to reach for
{% codegroup title="…" %} precisely because figure cannot serve — but nothing says so at the point of use, and the content model implies it can. - Severity is
major rather than minor because the failure mode is silent content loss. A rune that refused the child would be a smaller problem. - Two defensible fixes, and they are not equivalent:
- Emit the non-media children in body order, keeping
imgs as the media slot. Makes {% figure %} a general captioned-container and unblocks figure-wrapped snippets, tables and diagrams. - Warn and drop, keeping figure image-only. Preserves the current contract and the
ImageObject type, and makes the limit visible. The choice is a product decision about what figure is, so it belongs in a spec rather than being settled in the fix.
Decision
Fix 1: figure is a general captioned container. Decided when scheduling v0.39.0. Non-media children are emitted in body order, and imgs stays the media slot. Together with WORK-615, which removes snippet's own figure wrapper, this makes {% figure %} the documented way to give a snippet, table or diagram a caption.
What the fix still has to settle, and record in its resolution:
- The schema.org type.
typeof="ImageObject" cannot stay unconditional once a figure may hold a code fence. One option: keep ImageObject when the media slot is the figure's only content, and emit no type otherwise. That choice belongs in the schema table (SPEC-130), and the baseline diff is reviewed, not regenerated silently. - Contracts and CSS. The structure contract moves. Lumina's figure styles need to hold up for non-media bodies.
- Docs. The figure rune page states what it accepts, and SPEC-062's "reach for codegroup instead" advice is revisited.
References
- SPEC-106 — image
src scheme sugar; where isMediaNode's admitted set comes from - SPEC-062 — the snippet rune, whose standalone chrome this interacts with
- SPEC-141 — removes snippet's own figure wrapper, which makes this the recommended path