Skip to content

Footnotes

Bodies as blocks, the body content column, collection inside containers and placement of the rendered list.

Generated from resources/examples/edge-cases.md and resources/examples/core.md - edit the cases there, not here. Each case links the conformance fixture it produces.

Footnote with multiple blocks

2 conformance fixtures

A footnote definition's body is parsed as full block content — multiple paragraphs (or lists, etc.) indented under the definition. The backlink is appended to the last block.

carve
See the note.[^n]

[^n]: First paragraph of the note.

    Second paragraph, indented under the definition.
html
<p>See the note.<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>First paragraph of the note.</p>
      <p>Second paragraph, indented under the definition.<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

The continuation marker + also works here: a lone + attaches the following flush-left block to the note, so a second block needs no indentation.

carve
See the note.[^n]

[^n]: First paragraph of the note.
+
A second paragraph, joined with +.
html
<p>See the note.<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>First paragraph of the note.</p>
      <p>A second paragraph, joined with +.<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

Cross-references resolve inside footnote bodies

1 conformance fixture

A footnote definition is full block content, so a </#id> cross-reference (and reference links) inside a footnote body resolve against document-level targets.

carve
# H

Body[^n]

[^n]: see </#h>
html
<section id="H">
  <h1>H</h1>
  <p>Body<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
</section>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>see <a href="#H">H</a><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

Footnote definition inside a container is collected

2 conformance fixtures

A footnote definition is document-level metadata: it is collected and resolved even when it sits inside a blockquote or a list item (PART 9 §16). The reference resolves to an endnote and the container that held the definition is left empty.

Definition inside a blockquote:

carve
See [^a].

> [^a]: note body
html
<p>See <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a>.</p>
<blockquote>

</blockquote>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note body<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

Definition inside a list item:

carve
See [^a].

- [^a]: note body
html
<p>See <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a>.</p>
<ul>
  <li></li>
</ul>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note body<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

Footnotes placement

1 conformance fixture

A ::: footnotes block flushes the endnotes section at that point instead of at the document end. All footnotes are included, even those referenced after the marker.

carve
Intro[^a] and[^b].

::: footnotes
:::

## After

More text.

[^a]: first note

[^b]: second note
html
<p>Intro<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> and<a id="fnref2" href="#fn2" role="doc-noteref"><sup>2</sup></a>.</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>first note<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
    <li id="fn2">
      <p>second note<a href="#fnref2" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
<section id="After">
  <h2>After</h2>
  <p>More text.</p>
</section>

Footnote definition requires an inline body

1 conformance fixture

A footnote definition carries its body on the marker line: [^label]: followed by a space and inline content (PART 9 §16). A bare [^label]: with nothing after the colon is not a definition — it stays an ordinary paragraph, and a following indented line folds into it as paragraph text. Continuation lines extend a definition only when the marker line itself opened one.

carve
Use [^a].

[^a]:
  First
html
<p>Use [^a].</p>
<p>[^a]:
First</p>

Footnote definition separator must be a space

1 conformance fixture

The separator after a footnote-definition marker must be a literal space (U+0020). A tab after [^label]: does not open a definition; the line stays an ordinary paragraph and the tab is preserved as text. This aligns with heading, list, and task markers, which already reject a tab, and with the grammar 's space production.

carve
Use [^a].

[^a]:	Tabbed
html
<p>Use [^a].</p>
<p>[^a]:	Tabbed</p>

Unresolved footnote reference with a trailing attribute stays literal

3 conformance fixtures

A [^a] footnote reference with no matching definition is not a footnote and does not become an attributed span: it stays literal text, and a following {...} attribute block does not attach to it.

carve
Text[^a]{.ref}.
html
<p>Text[^a].</p>

A resolved footnote reference is unaffected.

carve
Text[^a].

[^a]: note.
html
<p>Text<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a>.</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note.<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A genuine bracketed span with attributes still works.

carve
A [span]{.c} here.
html
<p>A <span class="c">span</span> here.</p>

A caret is a reference label, not an empty footnote

2 conformance fixtures

footnote_label = {character - ']'}+ is one-or-more, so [^] has no footnote label and [^]: /u is not a footnote definition. `reference_label = (character

  • ']' - '@'), {character - ']'}excludes exactly two characters, and^is not one of them - so the line IS a link reference definition whose label is^, and a reference spelled [text][^]` resolves against it.

Pinned because all three engines and this repository's own oracle once disagreed: the definition vanished in one, became an empty-label footnote in another, and the oracle's label pattern excluded a character the production admits (carve-rs#488, carve-rs#511, carve#589).

carve
[^]: /u

see [text][^].
html
<p>see <a href="/u">text</a>.</p>

A bare [^] with nothing defining it stays literal, since Carve has no shortcut reference.

carve
see [^].
html
<p>see [^].</p>

A definition on a footnote body's continuation line is collected

1 conformance fixture

A footnote body is a container like any other, and §16 collects a definition out of a container. On the body's own continuation column the line defines, renders nothing, and the reference below the note resolves. carve-rs rendered it as note text until carve-rs#599; nothing in the corpus held the other two to it.

carve
[^a]: note
  [r]: /u

see[^a] and [t][r]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> and <a href="/u">t</a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A footnote body holds blocks, and they render where they were written

1 conformance fixture

A note body is a container: it holds blocks, not just inline content, and each renders at the body's own indentation. The body here ends in a PARAGRAPH, so the backlink lands in it directly; "A footnote body's last block, when it is not a paragraph, gets a synthesized paragraph for the backlink" (below) pins what happens when the last block is a code block, a block quote, a table, a div or a raw block instead.

carve
[^a]: intro

  | a |
  | - |
  | b |

  closing line.

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>intro</p>
      <table>
        <thead><tr><th scope="col">a</th></tr></thead>
        <tbody>
          <tr><td>b</td></tr>
        </tbody>
      </table>
      <p>closing line.<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A heading in a footnote body takes an id but no section wrapper

1 conformance fixture

A heading inside a note is a heading: it gets the generated id every other heading gets, so a fragment link and an implicit reference can reach it. What it does NOT get is the <section> wrapper, which only applies at document level - a note is already inside an <li>.

carve
[^a]: note

  # H

  after

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note</p>
      <h1 id="H">H</h1>
      <p>after<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

An attribute line inside a footnote body attaches inside it

1 conformance fixture

A block-attribute line attaches to the block that follows it, wherever it was written. Inside a note body that is the note's own next block, and a dangling one at the end of the body attaches to nothing at all - it does not reach the document below (§15 A4).

carve
[^a]: note

  {.cls}
  styled

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note</p>
      <p class="cls">styled<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A nested list in a footnote body stays nested

1 conformance fixture

Relative indentation inside a note body means what it means everywhere else: it says which item a marker belongs to. A body collected flush-left loses that, and the sublist becomes a sibling.

carve
[^a]: note

  - one
    - deep

  end.

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note</p>
      <ul>
        <li>one
          <ul>
            <li>deep</li>
          </ul>
        </li>
      </ul>
      <p>end.<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A flush-left line after a footnote definition belongs to the document

1 conformance fixture

A note body is the definition line plus lines indented by at least two spaces (§16). A flush-left line is not one of them: it ends the body and is the document's own next block. The + continuation marker (§17 L4) is the way to attach a flush-left block to a note, and it is deliberate rather than accidental.

carve
a
[^f]: note
b

see[^f]
html
<p>a</p>
<p>b</p>
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A footnote body's own column is two, and a third column is its text

1 conformance fixture

The body's column is fixed by §16's space, space, not read off the first continuation line. A reader consumes exactly two columns and hands the rest to the body's blocks, so a body written one column in has ONE residual column - and there a block opener is paragraph text, the same way it is above a list item's content column (§24 C3). The same rows at two spaces are a table ("A footnote body holds blocks" pins that); a third column makes them a paragraph. carve-js derived the column from the first continuation line and read a table, alone against the other two engines and this spec (carve-js#677).

carve
[^a]: intro

   | a |
   | - |
   | b |

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>intro</p>
      <p>| a |
| - |
| b |<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A definition below a footnote body's column is the document's own text

1 conformance fixture

One space is not a continuation - §16 wants two - so the line leaves the note and is the document's next block. It is not a definition either: the production starts at the opening bracket and allows no leading indent, so the reference below stays literal. The line is VISIBLE and INERT, and those two halves have to be pinned together: carve-js and carve-php rendered it AND defined from it, so a reader saw the definition as prose while a reference silently resolved through the same line (carve#701, carve-js#681, carve-php#825).

carve
[^a]: note
 [r]: /u

see[^a] and [t][r]
html
<p>[r]: /u</p>
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> and [t][r]</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A definition past a footnote body's column is the body's own text

1 conformance fixture

Three spaces IS a continuation, so the line belongs to the note - but the body's column is two and the third column is residual indent its blocks read, so the definition never reaches an opener position and stays paragraph text inside the note. Visible and inert again, and for the opposite reason to the case above: there the line was outside the body, here it is inside it. Beside "A footnote body's own column is two", this is what makes the column load-bearing in both directions.

carve
[^a]: note
   [r]: /u

see[^a] and [t][r]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> and [t][r]</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note
[r]: /u<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A tab reaches a footnote body's column just as two spaces do

3 conformance fixtures

A footnote body's own column is fixed at two ("A footnote body's own column is two, and a third column is its text"), but reaching that column is column arithmetic (§24 C1), not a count of space characters: a tab from column 0 lands at column 4, already past the floor, so it satisfies the same requirement two literal spaces satisfy. A single space (column 1) still falls short and leaves the note, as it already does today (carve#692).

carve
[^a]: note

	more

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note</p>
      <p>more<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A space then a tab reaches the same column (1, then to the next stop at 4), so it qualifies too:

carve
[^a]: note

 	more

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note</p>
      <p>more<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

The blank line above is not what admits the tab - the column requirement is per line, so a tab-indented line directly under the definition, with no blank line at all, is a continuation too (and, with no blank between them, it folds into the same paragraph as a soft break, exactly as two spaces would):

carve
[^a]: note
	more

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note
more<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
5 conformance fixtures

A footnote body whose last block is a paragraph gets its backlink appended directly into that paragraph (see above). When the last block is something else, the backlink is never folded into a paragraph found inside or before that block - a synthesized <p> after the last block holds it instead, as a sibling of that block (carve#688). Five shapes, previously unpinned:

A code block:

carve
[^a]: note
  ```
  code
  ```

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note</p>
      <pre><code>code
</code></pre>
      <p><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A block quote. The quote's own paragraph ("quoted") is not where the backlink goes - that would misattribute it to the quoted source. The synthesized paragraph is a sibling of the <blockquote>, not a child of it:

carve
[^a]: note
  > quoted

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note</p>
      <blockquote><p>quoted</p></blockquote>
      <p><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A table:

carve
[^a]: note

  | a |
  | - |
  | b |

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note</p>
      <table>
        <thead><tr><th scope="col">a</th></tr></thead>
        <tbody>
          <tr><td>b</td></tr>
        </tbody>
      </table>
      <p><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A div:

carve
[^a]: note

  ::: note
  d
  :::

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note</p>
      <aside class="admonition note">
        <p>d</p>
      </aside>
      <p><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A raw block. Its content is passed through verbatim by definition; appending the backlink inside it would put navigation markup into a region the author asked to be left untouched, so the synthesized paragraph follows it instead:

carve
[^a]: note

  ```=html
  <b>x</b>
  ```

see[^a]
html
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>note</p>
      <b>x</b>
      <p><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A line at a footnote definition's own column, followed by non-blank text, forms its own tight block

1 conformance fixture

A footnote definition on a list item's own content column renders no trace (above), and none of the three reference engines loosens the item on account of it - agreed already. What was not settled is what a plain line right after the definition, with no blank line anywhere, does to the item. §10 I5 already answers half of it: an invisible construct interrupts an open paragraph exactly like a visible one, so the line after the definition starts a NEW block rather than folding back into the paragraph the definition ended. §17 L1/L2 answer the rest: nothing here is a blank line, so the item never loosens, and a tight item's paragraphs are ALL bare - the new block included, not only the first one. The item ends up holding two paragraph blocks with no blank between them, both unwrapped (carve#668; §17 L6).

carve
- a
  [^f]: x
  more

see[^f]
html
<ul>
  <li>a
    more
  </li>
</ul>
<p>see<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>x<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

An empty footnote body is written with the {empty} sentinel

1 conformance fixture

"Footnote definition requires an inline body" above is a PARSE rule, and it leaves the canonical writer a problem: a footnote definition whose body holds no blocks cannot be written back as a bare [^label]:, because that line is not a definition - so the definition and every reference to it would come back as literal text. PART 11 §7b answers it. The writer emits the sentinel attribute block {empty}, which the definition line consumes as attributes and discards, leaving the body empty and the reference resolved.

{ } and {} do not work in this position, and they are the first two spellings a reader reaches for: a block-attribute line requires at least one attribute, so neither run is an attribute block here and both stay literal text inside the note.

carve
See[^f]

[^f]: {empty}
html
<p>See<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

An empty inline note is literal

3 conformance fixtures

PART 9 §16 says it in as many words: "Empty or whitespace-only (^[], ^[ ]) is literal; an unclosed ^[… is literal." So ^[ does not open a note there.

What is left is ordinary text plus an ordinary bracketed run, and that matters for the third case below: a bare [] is literal, but []{.c} carries an attribute tail and is a span (PART 9 §14), so the ^ is the only part of ^[]{.c} that stays as written.

This needed pinning because the executable spec refused all three, and no corpus document held an empty note - so the refusal was unreachable, npm run core:check reported every input conformant either way, and the gap was guarded by the absence of a fixture rather than by a decision (markup-carve/carve#1188, the markup-carve/carve#755 class).

carve
x ^[]
html
<p>x ^[]</p>

Whitespace-only is the same case: a space between the brackets is not content.

carve
x ^[ ]
html
<p>x ^[ ]</p>

With an attribute block the brackets are a span, and only the ^ is literal.

carve
x ^[]{.c}
html
<p>x ^<span class="c"></span></p>

A note's content recognizes no note

4 conformance fixtures

PART 9 §16 on the inline form: "Content is INLINE-only, parsed recursively with footnote recognition DISABLED inside it (no ^[…] or [^ref] nested in a note, either direction)."

Disabled recognition makes the inner spelling ORDINARY TEXT rather than an unrenderable document. Inside a note ^[ opens nothing, so the ^ is text and [b] is a bracketed run; and [^1] is not a reference, so it is a bracketed run over the content ^1 - which is why the second case renders [^1] even though the document defines that label. The definition is then referenced by nothing and renders nothing of its own.

Recognition stays off for the WHOLE content, not one level of it: the third case holds a note spelling two deep and both stay literal.

The executable spec refused all three, and no corpus document nested a note in a note (markup-carve/carve#1188).

carve
x ^[a ^[b] c]
html
<p>x <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>a ^[b] c<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
carve
x ^[a [^1] c]

[^1]: n
html
<p>x <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>a [^1] c<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
carve
x ^[a ^[b ^[c] d] e]
html
<p>x <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>a ^[b ^[c] d] e<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

An attribute block on the inner spelling attaches to the bracketed run it turns out to be, on either side of the pair.

carve
x ^[a [^1]{.k} c]

[^1]: n
html
<p>x <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>a <span class="k">^1</span> c<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
2 conformance fixtures

PART 9 §16 records this as a LIMITATION: a footnote "inside link text ([t[^1]](u)) or inside a heading later cloned by a </#id> crossref nests an <a> in an <a>; avoid footnotes in those positions."

That is advice about what an author should expect, and it states the outcome. It does not put the document outside the language: the noteref lands where it was written, inside the link text, and the note takes its number from the one document-order sequence like any other.

Both note forms reach it the same way, which is what makes the pairing worth pinning - the executable spec rendered the inline form and refused the reference one (markup-carve/carve#1188).

carve
a [t[^1]](/u) b

[^1]: n
html
<p>a <a href="/u">t<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></a> b</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>n<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
carve
a [t^[n]](/u) b
html
<p>a <a href="/u">t<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></a> b</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>n<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
3 conformance fixtures

The PART 9 §16 limitation is about LINK TEXT, not about one spelling of a link: "a footnote (reference [^1] or inline ^[…]) inside link text ([t[^1]](u)) or inside a heading later cloned by a </#id> crossref nests an <a> in an <a>". A reference tail reaches the same place by a different route, and reaches the same answer - the noteref lands where it was written and the note draws its number from the one document-order sequence.

The neighboring section pins the inline-tail half of that cross. This one pins the reference-tail half, which the executable spec used to refuse: its own resolution frame carried the link text through a JSON payload, and JSON.stringify escaped the frame's field separator, so the footnote pass never saw the noteref sitting in it (markup-carve/carve#1195).

carve
a [t[^1]][r] b

[r]: /u

[^1]: n
html
<p>a <a href="/u">t<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></a> b</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>n<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
carve
a [t^[n]][r] b

[r]: /u
html
<p>a <a href="/u">t<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></a> b</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>n<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
carve
a [t[^1]][r] c [^1] b

[r]: /u

[^1]: n
html
<p>a <a href="/u">t<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></a> c <a id="fnref1-2" href="#fn1" role="doc-noteref"><sup>1</sup></a> b</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>n<a href="#fnref1" role="doc-backlink"><sup>1</sup></a> <a href="#fnref1-2" role="doc-backlink"><sup>2</sup></a></p>
    </li>
  </ol>
</section>

A note body's own references resolve

3 conformance fixtures

A footnote body is rendered when the endnotes list is built, which is after the document text has been walked. Whatever the body introduces - a reference link, another footnote, a reference link whose text holds a footnote - is therefore introduced late, and still has to be resolved.

The later note takes the next number in the same sequence, and its own body is resolved on the same terms, so the list can grow while it is being built.

carve
a [^1] b

[^1]: see [x][r]

[r]: /u
html
<p>a <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> b</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>see <a href="/u">x</a><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
carve
a [^1] b

[^1]: see [^2]

[^2]: two
html
<p>a <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> b</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>see <a id="fnref2" href="#fn2" role="doc-noteref"><sup>2</sup></a><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
    <li id="fn2">
      <p>two<a href="#fnref2" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
carve
a [^1] b

[^1]: see [t[^2]][r]

[r]: /u

[^2]: two
html
<p>a <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> b</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>see <a href="/u">t<a id="fnref2" href="#fn2" role="doc-noteref"><sup>2</sup></a></a><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
    <li id="fn2">
      <p>two<a href="#fnref2" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
4 conformance fixtures

Resolving a reference is deferred: the tail is recorded when the inline pass reaches it and matched against the definitions afterwards. Everything the link text holds is carried across that gap, and comes out the other side unchanged - including the constructs whose own resolution is deferred the same way.

Two of those are worth stating outright. A reference link inside link text is still a link, so PART 3's rule that links never nest applies to it exactly as it applies to an inline one: the inner link flattens to its text. An image reference is not a link, so it stays, and an image inside an anchor is what the document asked for.

carve
a [t</#}>][r] b

[r]: /u
html
<p>a <a href="/u">t&lt;/#}&gt;</a> b</p>
carve
a [t[x][r2]][r] b

[r]: /u

[r2]: /v
html
<p>a <a href="/u">tx</a> b</p>
carve
a [t[x][r2]](/u) b

[r2]: /v
html
<p>a <a href="/u">tx</a> b</p>
carve
a [t![z][r2]][r] b

[r]: /u

[r2]: /i.png
html
<p>a <a href="/u">t<img src="/i.png" alt="z"></a> b</p>

A footnote in an unresolved reference is not a reference

4 conformance fixtures

An unresolved reference degrades to its literal source (PART 9R R1), so the link text it rendered is discarded rather than written into the document. A footnote reference or an inline note sitting in that text therefore references nothing: it draws no number, its definition stays unreferenced and is dropped, and no endnotes section is written on its account.

Counting it anyway is what a pipeline does when it numbers footnotes before it knows whether the reference resolved. The numbering says so out loud - the note a reader can see is then numbered as a repeat of a reference the document does not contain, and a lone one leaves an endnote whose backlink names an id no element carries.

carve
a [t[^1]][nope] b

[^1]: n
html
<p>a [t[^1]][nope] b</p>
carve
a [t[^1]][nope] b [^1] c

[^1]: n
html
<p>a [t[^1]][nope] b <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> c</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>n<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
carve
a [t^[n]][nope] b
html
<p>a [t^[n]][nope] b</p>

A bracketed run that never had a tail is not a reference at all: PART 9 §14 renders its content, so a note inside it is written and counts.

carve
a [t[^1]] b

[^1]: n
html
<p>a [t<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a>] b</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>n<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

An inline note's content resolves after the note

7 conformance fixtures

A note's content is rendered where it is written and placed where the endnotes go, so a construct inside it whose own resolution is deferred - a crossref, a reference link, a reference image - has to survive that move. It does: the note carries the unresolved construct across, and the construct resolves against the whole document once the note has been placed.

Whether it resolves is a separate question from whether it survives. An unresolved crossref inside a note renders as its literal source, exactly as it would outside one.

carve
a ^[see </#h>] b

# h
html
<p>a <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> b</p>
<section id="h">
  <h1>h</h1>
</section>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>see <a href="#h">h</a><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
carve
a ^[see </#nope>] b
html
<p>a <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> b</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>see &lt;/#nope&gt;<a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
carve
a ^[see [t][r]] b

[r]: /u
html
<p>a <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> b</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>see <a href="/u">t</a><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
carve
a ^[see ![z][r]] b

[r]: /i.png
html
<p>a <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> b</p>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>see <img src="/i.png" alt="z"><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A collapsed reference reaches the heading index from inside a note too, and the note's own attributes are unaffected by what its content holds.

carve
a ^[see [h][]] b

# h
html
<p>a <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> b</p>
<section id="h">
  <h1>h</h1>
</section>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>see <a href="#h">h</a><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>
carve
a ^[</#h>]{.c} b

# h
html
<p>a <a id="fnref1" href="#fn1" role="doc-noteref" class="c"><sup>1</sup></a> b</p>
<section id="h">
  <h1>h</h1>
</section>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p><a href="#h">h</a><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

A note reached from a footnote body is placed after the body that introduced it, and its own content resolves the same way.

carve
a [^1] b

[^1]: see ^[</#h>]

# h
html
<p>a <a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> b</p>
<section id="h">
  <h1>h</h1>
</section>
<section role="doc-endnotes">
  <hr>
  <ol>
    <li id="fn1">
      <p>see <a id="fnref2" href="#fn2" role="doc-noteref"><sup>2</sup></a><a href="#fnref1" role="doc-backlink"></a></p>
    </li>
    <li id="fn2">
      <p><a href="#h">h</a><a href="#fnref2" role="doc-backlink"></a></p>
    </li>
  </ol>
</section>

Footnotes

2 conformance fixtures

A footnote definition that is never referenced produces no endnotes section.

carve
text
[^f]: note
html
<p>text</p>
carve
Wrapped[^two
words].

[^two words]: This definition is not referenced.

[^broken
label]: This is not a definition.
html
<p>Wrapped[^two
words].</p>
<p>[^broken
label]: This is not a definition.</p>

Released under the MIT License.