Links, images and references
Destinations, titles, reference and collapsed forms, autolinks and where a definition may live.
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.
Nested brackets in link text
1 conformance fixture
Link, image, and span text may contain balanced nested brackets; the closing ] is found by balance, not at the first inner ].
[a [b] c](/u)<p><a href="/u">a [b] c</a></p>An image's alt text closes where a link's text closes
8 conformance fixtures
316-an-image-s-alt-text-closes-where-a-link-s-text-closes316-an-image-s-alt-text-closes-where-a-link-s-text-closes-2316-an-image-s-alt-text-closes-where-a-link-s-text-closes-3316-an-image-s-alt-text-closes-where-a-link-s-text-closes-4316-an-image-s-alt-text-closes-where-a-link-s-text-closes-5316-an-image-s-alt-text-closes-where-a-link-s-text-closes-6316-an-image-s-alt-text-closes-where-a-link-s-text-closes-7316-an-image-s-alt-text-closes-where-a-link-s-text-closes-8
An image has the same three forms as a link, and PART 3 says only the leading ! and the <img src> output differ. The bracketed run is not one of the things that differ: the alt text ends at the MATCHING ], by the same balanced, escape- and literal-span-aware scan that closes link text. So an alt text may hold a bracket, at any depth, in every form and in every host that re-parses the run.
What the run does NOT share with link text is its content model. alt is an HTML attribute, so nothing inside is inline-parsed: an escape stays as authored and a backtick run stays a backtick run.
a ![t[z]][r] b
[r]: /i.png<p>a <img src="/i.png" alt="t[z]"> b</p>a ![t[z]](/i.png) b<p>a <img src="/i.png" alt="t[z]"> b</p>Nesting is unbounded, and a trailing attribute block still attaches to the resolved image.
a ![t[z[q]]][r]{.c} b
[r]: /i.png<p>a <img src="/i.png" alt="t[z[q]]" class="c"> b</p>An escape and a code span both keep their ] out of the close, and both reach the attribute as the bytes the author wrote.
a ![t\]z](/i.png) b<p>a <img src="/i.png" alt="t\]z"> b</p>a ![t`]`z](/i.png) b<p>a <img src="/i.png" alt="t`]`z"> b</p>An UNBALANCED ] still closes the run where it stands, which leaves a reference tail with nothing in front of it and the whole line literal.
a ![t]z][r] b
[r]: /i.png<p>a ![t]z][r] b</p>The hosts that re-read the run agree with the inline pass. A paragraph whose whole content is one reference image is captionable however its alt text is spelled, and an image inside link text is still an image.
![t[z]][r]
^ cap
[r]: /i.png<figure>
<img src="/i.png" alt="t[z]">
<figcaption>cap</figcaption>
</figure>a [x ![t[z]][r] y](/u) b
[r]: /i.png<p>a <a href="/u">x <img src="/i.png" alt="t[z]"> y</a> b</p>An editorial comment's bracket is content, not the close
4 conformance fixtures
The close scan skips the interior of every span whose content is LITERAL: inline code, the !-prefixed inline literal, and an editorial comment. The test is the property, not the list - a ] inside any of them cannot be escaped, so ending the run there would leave no spelling that keeps both the construct and the author's text.
A comment with no bracket in it never had a say in where the run ends.
a [t{# n #}z](/u) b<p>a <a href="/u">t<span class="critic-comment"> n </span>z</a> b</p>One with a bracket in it does not either, in a link, in a span, in an inline note, or in an alt text.
a [t{# ] #}z]{.c} b<p>a <span class="c">t<span class="critic-comment"> ] </span>z</span> b</p>a ^[t{# ] #}z] b<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>t<span class="critic-comment"> ] </span>z<a href="#fnref1" role="doc-backlink">↩</a></p>
</li>
</ol>
</section>a ![t{# ] #}z](/i.png) b<p>a <img src="/i.png" alt="t{# ] #}z"> b</p>Reference labels are case-sensitive
1 conformance fixture
Reference labels are matched case-sensitively (no case normalization). A label whose case does not match its definition stays unresolved and renders literally, like any other unresolved reference.
[Text][REF]
[ref]: /u<p>[Text][REF]</p>Link destination parentheses balance
4 conformance fixtures
A ( inside a (...) destination is matched against a later ), so the destination ends at the first ) that has no opener left to pair with. URLs carrying parentheses -- Wikipedia and MDN produce them constantly -- are therefore written plainly, with no escape and no second spelling. Djot and CommonMark both balance destination parentheses the same way.
[x](http://a/b(c))<p><a href="http://a/b(c)">x</a></p>Nesting is tracked to any depth, and a ) with nothing to close ends the destination -- the rest stays literal text.
[x](a(b(c))d) and [y](e)f)<p><a href="a(b(c))d">x</a> and <a href="e">y</a>f)</p>An unbalanced parenthesis that belongs inside the URL is backslash-escaped. Only \(, \) and \\ are escapes here, so a backslash in front of anything else is an ordinary character and URLs full of backslashes are unaffected.
[x](http://a/b\)c) and [y](a\\b) and [z](a\qb)<p><a href="http://a/b)c">x</a> and <a href="a\b">y</a> and <a href="a\qb">z</a></p>A newline counts as whitespace, so it ends the destination too: an unclosed ( whose run reaches the end of the line is not a link. The ( and the following text stay literal across the line break (grammar link_destination).
[t](url
more)<p>[t](url
more)</p>Empty link and image titles are preserved
1 conformance fixture
An explicit empty title ("") is kept as title="" rather than dropped -- the grammar permits an empty link_title, and all three implementations emit it identically.
[x](u "")<p><a href="u" title="">x</a></p>A backslash in a link destination is a literal character
1 conformance fixture
A link destination has no backslash escapes: url_char includes the backslash as an ordinary URL character, kept verbatim. [t](a\b) links to a\b.
[t](a\b)<p><a href="a\b">t</a></p>Autolink display keeps the raw content
1 conformance fixture
An autolink's display text is the raw content between < and >: a URI autolink keeps its scheme (<mailto:a@b> shows mailto:a@b), while an email autolink (no explicit scheme) shows the address with a mailto: href.
<mailto:a@b><p><a href="mailto:a@b">mailto:a@b</a></p>Link reference definition separator must be a space
1 conformance fixture
The same rule applies to link reference definitions: [label]: must be followed by a literal space. A tab leaves the line as a paragraph, so the later [a][] has no target to resolve.
[a]: /url
[a][]<p>[a]: /url</p>
<p>[a][]</p>Indented image and caption stay literal
3 conformance fixtures
A lone image on its own line at column 0 becomes a <figure> when a ^ caption line follows. Indented by a space, neither the image line nor the caption is a top-level opener, so the pair folds into a literal paragraph with the raw ^ still in the text.

^ Figure 1: moon<p><img src="a.jpg" alt="Apollo">
^ Figure 1: moon</p>An indented attribute brace above the indented image and caption is likewise literal; all three lines join as one paragraph.
{.gallery}

^ Figure 1: moon<p>{.gallery}
<img src="a.jpg" alt="Apollo">
^ Figure 1: moon</p>Control - flush left at column 0 the same image and caption form the <figure>.

^ Figure 1: moon<figure>
<img src="a.jpg" alt="Apollo">
<figcaption>Figure 1: moon</figcaption>
</figure>Indented reference and footnote definitions stay literal
2 conformance fixtures
A reference-link definition and a footnote definition are top-level block constructs that register at column 0. Indented by a space the definition line is an ordinary paragraph: it registers nothing, so the reference or footnote that used it never resolves and both render as literal text.
An indented reference definition does not register; the link stays unresolved.
Read [intro][x].
[x]: /intro "T"<p>Read [intro][x].</p>
<p>[x]: /intro “T”</p>An indented footnote definition does not register; the footnote reference stays literal.
Note[^fn].
[^fn]: body.<p>Note[^fn].</p>
<p>[^fn]: body.</p>A repeated definition: which one wins
3 conformance fixtures
The three definition kinds do not answer this the same way, so each is pinned separately.
A repeated link reference definition is overridden by the later one.
see [t][r].
[r]: /a
[r]: /b<p>see <a href="/b">t</a>.</p>A repeated abbreviation definition behaves the same way - the later expansion wins.
*[A]: a
*[A]: b
A here.<p><abbr title="b">A</abbr> here.</p>A repeated footnote definition does not: the FIRST one wins and the later one is dropped. carve lint reports it as duplicate-footnote-definition.
see [^f].
[^f]: one
[^f]: two<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>one<a href="#fnref1" role="doc-backlink">↩</a></p>
</li>
</ol>
</section>A collapsed reference is matched by the label the author wrote
2 conformance fixtures
[label][] resolves against the definition whose label is that BRACKET TEXT, whitespace-collapsed - the same spelling the definition line registers. The rendered text is a different string as soon as the label carries markup, and keying on it inverts the rule in both directions: the definition that names the label stops resolving, and a plain definition the author never referenced starts.
This engine-visible pair is what nothing pinned. carve-php stripped _ * ~ ^ + = { } [ ] ` from every collapsed label and got both halves backwards (carve-php#768); the executable spec keyed on the rendered text and got the same two answers wrong (carve#648). Three implementations agreed all along and no case could tell.
[*bold*]: /x
see [*bold*][]<p>see <a href="/x"><strong>bold</strong></a></p>The inverse: a decorated label does not reach a plain definition, because [bold] is not the label that was written. Unresolved, the construct renders as the SOURCE the author typed - [*bold*][], markers and all - not as the bracket content re-rendered. The executable spec emitted the rendered form here ([<strong>bold</strong>][]), which drops the markers that identify the construct while keeping the brackets that make it look like one.
[bold]: /x
see [*bold*][]<p>see [*bold*][]</p>A definition inside a container is collected at that container's content column
3 conformance fixtures
A definition is invisible and active wherever its container puts it. At column 0 that is settled; one container deeper it is the same rule, measured from INSIDE the container: > - a puts the item's content column at 2 of the quoted content, so a definition written there belongs to the item.
Every engine lost this in a different way and had to be fixed for it - carve-js#646, carve-php#786, carve-rs#587 - which is what a case pins against.
> - a
> [r]: /u
see [t][r]<blockquote>
<ul>
<li>a</li>
</ul>
</blockquote>
<p>see <a href="/u">t</a></p>The mirror arrangement - a quote INSIDE an item rather than an item inside a quote - reads the same way: the quote sits at the item's content column, the definition is its content, and the quote renders empty.
- a
> [r]: /u
see [t][r]<ul>
<li>a
<blockquote>
</blockquote>
</li>
</ul>
<p>see <a href="/u">t</a></p>An indented > that reaches no content column is not a container at all: the line renders as the text it looks like and defines nothing. Without this case the two above can be satisfied by stripping whitespace indiscriminately, which is exactly what three separate fixes did before it was measured.
[x][r] here.
> [r]: /u<p>[x][r] here.</p>
<p>> [r]: /u</p>Trailing attributes on a link reference definition
3 conformance fixtures
A {...} block at the end of a definition line attaches to the DEFINITION and reaches every link that resolves the label (PART 9R R1). That is what makes a definition worth writing once: without it, attributing a destination used ten times means repeating the attribute ten times.
[Example][ex] and [again][ex]
[ex]: https://example.com {.external}<p><a href="https://example.com" class="external">Example</a> and <a href="https://example.com" class="external">again</a></p>The merge is the one stacked attribute lists already use (PART 9 §15 A3): the definition's list first, the link's second, so a repeated key takes the LAST value and classes ACCUMULATE.
[Example][ex]{.internal #b}
[ex]: /u {.external #a}<p><a href="/u" class="external internal" id="b">Example</a></p>A {...} on its own line ABOVE a definition is a different construct: it floats PAST the definition to the next visible block (§15 A2a). Both can appear at once, and they do different things.
{.a}
[ex]: /u {.b}
[E][ex] and text<p class="a"><a href="/u" class="b">E</a> and text</p>An image takes a reference the way a link does
1 conformance fixture
An image resolves against the same definition table a link does (PART 3, reference_image; carve#641). Every reference FORM was pinned for links and none for images, so the three engines agreed here with nothing holding them to it - and the AST rule that a resolved reference keeps ref and rawRef beside its destination (PART 12 §3a) had no image case either.
![moon][m]
[m]: /moon.png<img src="/moon.png" alt="moon">A collapsed image reference uses its alt text as the label
1 conformance fixture
![alt][] takes the alt text as the label, and a definition title becomes the image's title.
![moon][]
[moon]: /moon.png "Title"<img src="/moon.png" alt="moon" title="Title">One definition serves a link and an image
1 conformance fixture
The definition table is shared, so the same label resolves for both - once as a destination, once as a source.
See [text][m] and ![moon][m].
[m]: /moon.png<p>See <a href="/moon.png">text</a> and <img src="/moon.png" alt="moon">.</p>An unresolved image reference stays literal
1 conformance fixture
With no matching definition the image renders as the text the author typed, exactly as an unresolved link reference does.
![moon][gone]<p>![moon][gone]</p>A reference image takes a caption
1 conformance fixture
A caption attaches to the captionable block above it, and an image written as a reference is an image. Every captioned-image case pinned the inline form, so the oracle accepted only that spelling and left the caption as literal text under a reference image while all three engines built the figure.
![a][ok]
^ cap
[ok]: /p.png<figure>
<img src="/p.png" alt="a">
<figcaption>cap</figcaption>
</figure>An unresolved reference image takes no caption
1 conformance fixture
The caption attaches to a captionable block, and a reference image that resolves to nothing is not one: the whole thing stays the text the author typed, both lines in one paragraph. The resolved form beside it becomes a figure, and the definition may sit anywhere - which is why this cannot be decided by looking at the image line alone.
![a][nope]
^ cap<p>![a][nope]
^ cap</p>An at sign is a reference label character everywhere but the first position
1 conformance fixture
reference_label = (character - ']' - '@'), {character - ']'}. The exclusion is positional: it keeps [@key] free for citations and costs nothing after the first character.
A label that STARTS with @ is a citation definition in the extension layer, so the core corpus cannot state its rendering here - the executable spec refuses that input (carve#798). The engines agree on it: [@x]: /u defines nothing and the label renders as a mention.
[a@b]: /v
see [u][a@b].<p>see <a href="/v">u</a>.</p>A label beginning with an at sign is not a reference label
2 conformance fixtures
reference_label = (character - ']' - '@'), {character - ']'} subtracts @ from the first position, and that exclusion is what keeps [@key] free for citations. So a bracket pair whose content starts with @ was never spelling a label, and the label slot of a reference link is no exception to it: the slot does not match, the production does not match, and the construct is not a reference link. The bracket run stays literal text, exactly as any other malformed reference does (carve#1302).
[t][@a]<p>[t][@a]</p>The image spelling reads the same reference_label at the same slot, so it gets the same answer rather than a parallel rule.
![t][@a]<p>![t][@a]</p>The control is what makes this a rule rather than a breakage: @ at the first position ALREADY means citation, so it cannot also mean label, and an implementation that made the slot accept @ would have to take that spelling away from citations to do it. A citation's rendering cannot be stated here - the construct is Tier-2 and the executable spec refuses it (carve#798) - so 43-citations-at-label-in-reference-position in tests/corpus-optional carries the other half: with the Citations extension on, both spellings above are still literal text while a [@a] beside them resolves to a citation.
The run declines as ONE construct and is restored as one, so the @a inside it is text and nothing else. A rescan would read [t][@a] as a [t][ run, a mention and a ], and with the extension on it would read the now tail-less [@a] as a citation - which is why the optional control renders both spellings literally on the far side of the switch.
At the HTML layer these rows cannot separate "not a reference label" from "a label nothing defines": a @-first label can never be defined, and a declining reference renders as its verbatim source either way. PART 12 §3a is where the readings would differ, all three engines publish an unresolved-reference node there, and the grammar clause records that without settling it.
A link definition written before a footnote stays before it
2 conformance fixtures
§7 orders collected definitions by source position, and PART 11 §6 binds the writer to the order the tree holds. The corpus pinned only the shape where the footnote comes first (a definition on a footnote body's continuation line), and a writer that emits footnotes in a fixed position - first or last - is correct on exactly that shape. This is its mirror: every engine's carve output for it must put the link definition first, because the author did.
see[^a] and [t][r]
[r]: /u
[^a]: note<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>Two definitions of the SAME kind pin the other half of the rule: a writer that sorts them by label rather than by position reverses these two.
see[^b] and[^a]
[^b]: bee
[^a]: ay<p>see<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>bee<a href="#fnref1" role="doc-backlink">↩</a></p>
</li>
<li id="fn2">
<p>ay<a href="#fnref2" role="doc-backlink">↩</a></p>
</li>
</ol>
</section>A zero-width character in a reference definition destination
2 conformance fixtures
link_destination ends at Unicode whitespace, and a ZERO WIDTH NO-BREAK SPACE is not whitespace: it is an ordinary destination character. The same rule governs a reference definition, because the definition is built from that same production - so the character is neither skipped as the separator run nor read as the end of the destination.
The inline form was already pinned. This is the definition form, where two engines kept the character and one dropped it or truncated at it, because the host language's own whitespace class holds U+FEFF and the Unicode property does not.
[r]: https://e.com/
see [x][r]<p>see <a href="https://e.com/">x</a></p>Position does not change the answer: a definition is not truncated at a zero-width character in the middle of its destination either.
[r]: https://e.com/
see [x][r]<p>see <a href="https://e.com/">x</a></p>A block image is separated from the block after it on every target
1 conformance fixture
A lone image is a BLOCK, so whatever separates two blocks on a target separates this one from what follows. The corpus held no document where a block image is followed by another block, so no gate compared the engines on it - and one of them ran the alt text straight into the next paragraph on the plain and ANSI targets (alt textfollowing paragraph), which the two repos claiming non-HTML parity could not catch because each reads its own committed snapshot rather than the other engines (carve-rs#692, carve-js#762).

following paragraph<img src="img.png" alt="alt text">
<p>following paragraph</p>A reference definition is anchored at end of line
16 conformance fixtures
266-a-reference-definition-is-anchored-at-end-of-line266-a-reference-definition-is-anchored-at-end-of-line-2266-a-reference-definition-is-anchored-at-end-of-line-3266-a-reference-definition-is-anchored-at-end-of-line-4266-a-reference-definition-is-anchored-at-end-of-line-5266-a-reference-definition-is-anchored-at-end-of-line-6266-a-reference-definition-is-anchored-at-end-of-line-7266-a-reference-definition-is-anchored-at-end-of-line-8266-a-reference-definition-is-anchored-at-end-of-line-9266-a-reference-definition-is-anchored-at-end-of-line-10266-a-reference-definition-is-anchored-at-end-of-line-11266-a-reference-definition-is-anchored-at-end-of-line-12266-a-reference-definition-is-anchored-at-end-of-line-13266-a-reference-definition-is-anchored-at-end-of-line-14266-a-reference-definition-is-anchored-at-end-of-line-15266-a-reference-definition-is-anchored-at-end-of-line-16
reference_definition ends in newline, and always has. All three engines and the executable spec nevertheless read [a]: /u zzz as a definition with trailing junk, and nothing in the grammar authorized that reading. carve#911 settled it the way the production already said: what follows the destination and the optional title makes the production FAIL, so the line is an ordinary paragraph.
The reason it matters is not tidiness. PART 7 promises that a slot which fails to match "falls back to prose rather than silently dropping metadata", and at this line there was no prose to fall back to -- the swallowing tail took whatever the slot rejected. So the clause's promised failure mode was unreachable here, and every narrowing at this line dropped metadata instead. With the line anchored the promise holds, and the tab and cardinality rules at both slots follow from the general rule with no special case.
[a]: /u zzz
[a][]<p>[a]: /u zzz</p>
<p>[a][]</p>A quoted run after a title is junk in the same way. The title is read, and then the line fails on what is left:
[a]: /u "T" zzz
[a][]<p>[a]: /u “T” zzz</p>
<p>[a][]</p>The tab, at both slots
The title slot and the trailing-attributes slot take space under PART 7, like every other padding slot. Both sit on this line, and both were left unpinned by carve#907 for the reason above: with the line unanchored, a tab there dropped the metadata rather than producing the visible failure the clause names. Now it produces the failure, so it can be pinned.
Each slot carries the tab-first form and BOTH mixed runs. A rule about a run written as "the first character must be a space" passes the tab-first fixture and admits <SP><TAB>; written as "the last character must be a space" it admits <TAB><SP> instead. Both spellings have been written for real in this org, in three languages, on one day.
The title slot:
[a]: /u "T"
[a][]<p>[a]: /u “T”</p>
<p>[a][]</p>[a]: /u "T"
[a][]<p>[a]: /u “T”</p>
<p>[a][]</p>[a]: /u "T"
[a][]<p>[a]: /u “T”</p>
<p>[a][]</p>The trailing-attributes slot:
[a]: /u {.c}
[a][]<p>[a]: /u {.c}</p>
<p>[a][]</p>[a]: /u {.c}
[a][]<p>[a]: /u {.c}</p>
<p>[a][]</p>[a]: /u {.c}
[a][]<p>[a]: /u {.c}</p>
<p>[a][]</p>What the anchor does NOT reject
The line ending is whitespace -- a space or a tab -- which is the same terminal blank_line = {whitespace} takes (PART 1, carve#890). So trailing spaces and a trailing tab are still a line ending rather than content, and the definition stands; a trailing NO-BREAK SPACE, EN QUAD or FORM FEED is content under that same ruling, so a line ending in one is not a definition. A trailing ZERO-WIDTH character is a third answer again: U+200B and U+FEFF are not whitespace at all, so they never reach the line ending -- link_destination reads them, the definition stands, and the character is in the href. The trailing attribute block is peeled by a scan that trims the line ending first, so that scan has to trim the SAME run the anchor accepts, or the same character answers one way with a block on the line and another without one. Those shapes are pinned in tests/separator-role-split.test.mjs rather than here, because a trailing whitespace run in a reviewable Markdown source file is one editor save from vanishing -- and because what a document does with trailing whitespace is its own question (carve#926).
And the glued form is untouched, because nothing is left over: link_destination simply reads the braces.
[a]: /u{.c}
[a][]<p><a href="/u{.c}">a</a></p>A definition still INTERRUPTS, attribute block and all
The anchor changes what the pattern matches, and the pattern is read in nine places rather than one: eight of them ask "is this line a definition" to decide paragraph interruption, lazy continuation, the def-list fold, the container scan, the item fold and the marker scan. While the pattern ended in a swallow-everything tail those eight could test the RAW line and be right by accident, because [a]: /u {.c} matched it raw. Anchored, they cannot: the trailing attribute block has to be split off first, or a definition carrying one stops interrupting anything and folds into the paragraph above it.
Nothing pinned that. Reverting all eight to the raw line left the entire suite green, and a differential sweep then found 42 of 72 generated shapes moving. These three are the sweep's representatives -- top level, inside a list item, and inside a definition-list description.
text
[a]: /u {.c}
[a][]<p>text</p>
<p><a href="/u" class="c">a</a></p>- text
[a]: /u {.c}
[a][]<ul>
<li>text</li>
</ul>
<p><a href="/u" class="c">a</a></p>:: term
: def
[a]: /u {.c}
[a][]<dl>
<dt>term</dt>
<dd>def</dd>
</dl>
<p><a href="/u" class="c">a</a></p>The fourth is the one the other three cannot reach. A block quote's open paragraph asks the same question in a SECOND place -- the lazy-continuation test, not the paragraph collector -- and reverting only that one site left even the differential sweep above unmoved. Here the definition has to interrupt, or the line below it lazily continues INSIDE the quote:
> text
[a]: /u {.c}
more
[a][]<blockquote><p>text</p></blockquote>
<p>more</p>
<p><a href="/u" class="c">a</a></p>Two more, found the same way: each of the eight sites was reverted on its own, and three of them survived every shape above. A definition INSIDE a quote is the third site -- it is what leaves the quote with no open paragraph, so a following lazy line starts its own block instead of folding in:
> text
> [a]: /u {.c}
lazy<blockquote><p>text</p></blockquote>
<p>lazy</p>And a definition after a blank line inside a list item is the fourth and fifth. An invisible construct is not a second paragraph, so the list stays TIGHT (PART 9 §17 L1/L2); with the raw predicate it loosens:
- text
[a]: /u {.c}
[a][]<ul>
<li>text</li>
</ul>
<p><a href="/u" class="c">a</a></p>The CONTROLS. Every legal shape of the line still is one:
[a]: /u "T" {.c}
[a][]<p><a href="/u" title="T" class="c">a</a></p>An autolink body admits non-ASCII and excludes format characters
10 conformance fixtures
272-an-autolink-body-admits-non-ascii-and-excludes-format-characters272-an-autolink-body-admits-non-ascii-and-excludes-format-characters-2272-an-autolink-body-admits-non-ascii-and-excludes-format-characters-3272-an-autolink-body-admits-non-ascii-and-excludes-format-characters-4272-an-autolink-body-admits-non-ascii-and-excludes-format-characters-5272-an-autolink-body-admits-non-ascii-and-excludes-format-characters-6272-an-autolink-body-admits-non-ascii-and-excludes-format-characters-7272-an-autolink-body-admits-non-ascii-and-excludes-format-characters-8272-an-autolink-body-admits-non-ascii-and-excludes-format-characters-9272-an-autolink-body-admits-non-ascii-and-excludes-format-characters-10
url_char was an enumerated ASCII set, so read as written an autolink admitted no non-ASCII at all - and two engines linked internationalized domains anyway (markup-carve/carve#860). The rule now reads: outside ASCII, url_char admits any character that is not whitespace and not a FORMAT character (General_Category Cf).
The deciding asymmetry is that the same destination written as an inline link already links everywhere, because link_destination admits unicode_url_char. One destination cannot answer two ways on the character set depending on which spelling the author reached for.
<https://例.jp/><p><a href="https://例.jp/">https://例.jp/</a></p>A non-ASCII PATH links on the same terms:
<https://example.com/café><p><a href="https://example.com/café">https://example.com/café</a></p>And so does a non-ASCII character that is not a LETTER. This is the row that separates the rule from "Unicode letters are letters": the executable spec's urlChar used ohm's built-in letter, which is Unicode-aware, so it linked café for free and left a currency sign, a CJK comma or an emoji literal.
<https://example.com/€10><p><a href="https://example.com/€10">https://example.com/€10</a></p>A format character is not a URL character
The exclusion is the half that is new rather than permissive. A format character is invisible by definition, so a host carrying one renders as the host WITHOUT it and links somewhere else - a spoofing surface, not an authoring convenience. The next document has a U+FEFF BYTE ORDER MARK between the e and the .com, and it is not an autolink:
<https://e.com/><p><https://e.com/></p>A LEADING one, before the scheme, is literal too - there for a different reason, since a scheme starts with a letter and this is not one:
<https://e.com/><p><https://e.com/></p>A U+200B ZERO WIDTH SPACE is a format character as well, despite the name - Unicode moved it out of the space categories in 4.0.1. Spelling the rule as a PROPERTY is what makes these two documents answer alike: a host language whose own whitespace class happens to hold U+FEFF gets the first one right for the wrong reason and this one wrong.
<https://e.com/><p><https://e.com/></p>A U+00A0 NO-BREAK SPACE is excluded by the OTHER half of the rule - it is whitespace - and was already the answer under both readings:
<https://e .com/><p><https://e .com/></p>What did not change
CONTROL. The ASCII exclusions are untouched. ", \, a backtick, {, }, |, ^, < and > are still not url_chars, and all four artifacts already agreed on them - which is why the rule is spelled unicode_url_char - format_char rather than "any non-whitespace, non-control character". The latter would re-admit these nine and move every implementation on a question nobody asked. (The straight quotes additionally pick up smart-quote typography, which is what makes the output curly.)
<https://example.com/"q"><p><https://example.com/“q”></p>CONTROL. link_destination is a different production and is unchanged, so a format character in an INLINE destination is still an ordinary destination character. The pair below and the interior-BOM autolink above are the same character in the same position, answering differently because the two spellings are two productions:
[t](https://e.com/)<p><a href="https://e.com/">t</a></p>CONTROL. Only the BODY admits non-ASCII. A scheme is letter, {letter | digit | '+' | '-' | '.'} and letter is the enumerated ASCII alphabet, so a scheme written in another script opens no autolink. The executable spec accepted one until this landed, for the same reason it linked café: ohm's letter is Unicode-aware.
<例://example.com/><p><例://example.com/></p>Unresolved reference link
1 conformance fixture
A reference with no matching definition renders as literal text.
A [missing][nope] ref stays literal.<p>A [missing][nope] ref stays literal.</p>Bare URLs stay literal
1 conformance fixture
A bare URL is not auto-linked (matching djot); wrap it in <…> to link.
see https://example.com now<p>see https://example.com now</p>Links
5 conformance fixtures
A backslash-escaped delimiter inside a title is a literal quote (CommonMark-style), so \" does not end a double-quoted title — it renders as a literal " (") in the title attribute (grammar link_title).
[t](/url "ti\"tle")<p><a href="/url" title="ti"tle">t</a></p>An empty link text is allowed and produces an empty anchor — useful as a target for a styled link.
[](https://example.com)<p><a href="https://example.com"></a></p>Links never nest. When a link's text contains another link, the inner link is replaced by its text and only the outer destination applies, so explicit nesting collapses to a single anchor.
[[x](y)](z)<p><a href="z">x</a></p>The same rule covers an autolink that lands inside a link's text: the autolink becomes plain text, never a nested anchor.
[pre <http://h> post](/u)<p><a href="/u">pre http://h post</a></p>It also covers a crossref, which only becomes a link once it resolves: inside a link's text it contributes its resolved text, not a second anchor.
# H
[see </#H>](/outer)<section id="H">
<h1>H</h1>
<p><a href="/outer">see H</a></p>
</section>The rule is about link content in the parsed tree. One renderer-level case is out of scope: a footnote reference inside a link label still renders its own doc-noteref anchor inside the outer anchor. Putting a footnote inside a link is unusual, and the footnote body and endnote are unaffected.
Reference link
5 conformance fixtures
"Anywhere in the document" includes inside a container: a reference definition written inside a blockquote or a list item is still an invisible block that is collected into the document-wide definition table, so a reference elsewhere resolves against it. The container keeps only its visible content (here, none).
> [ref]: /url
See [it][ref].<blockquote>
</blockquote>
<p>See <a href="/url">it</a>.</p>- [ref]: /url
See [it][ref].<ul>
<li></li>
</ul>
<p>See <a href="/url">it</a>.</p>A reference definition shares the link_destination rule, so its URL ends at the first whitespace. The definition is also ANCHORED AT END OF LINE, so what follows the destination is not ignored: it makes the production fail, and the line is an ordinary paragraph. [r]: a b c is therefore not a definition at all, and the reference below it does not resolve.
This is carve#911's ruling. The line used to be read as a definition with trailing junk, in all three engines and in the executable spec, and nothing in the grammar authorized that reading -- reference_definition has always ended in newline. It also made PART 7's promised fallback unreachable here: a title or attribute slot that failed had no prose to fall back to, so its metadata was silently dropped instead.
[r][r]
[r]: a b c<p>[r][r]</p>
<p>[r]: a b c</p>A backslash-escaped quote inside the title is a literal quote, exactly as in an inline link title (link_title). The escaped \" does not end the quoted run; it renders as a literal " (") in the title attribute.
[x][y]
[y]: /u "a\"b\"c"<p><a href="/u" title="a"b"c">x</a></p>A reference definition requires a non-empty destination (grammar reference_definition). A [r]: with nothing after the colon — or only trailing whitespace — is not a definition; the line stays literal text.
[r]:<p>[r]:</p>Autolinks
1 conformance fixture
url_char also excludes " \ ` { } | ^, so a double quote inside the brackets breaks the autolink. The whole run is literal text; the straight quotes additionally pick up smart-quote typography.
<http://a.com/"q"><p><http://a.com/“q”></p>