---
title: "Markdown anchor link without heading: pandoc span tests"
slug: markdown-anchor-without-heading
excerpt: "Pandoc's HTML for an id set on empty brackets, plus the link pointing at it, is quoted under Span and link under markdown."
quickAnswer: "Sample input and the four reader settings applied to it are shown in Sample input with a blank span. Under Span and link under markdown, HTML and native output for that file are quoted. Text that one run left in place appears in Brackets left as text in commonmark, beside grep counts. A run with one extension added appears under Adding bracketed_spans to commonmark. Bytes from a second extension run sit under Adding attributes to commonmark. An extra file holding words between the brackets is covered in Putting words inside the span. Spec lines for links with a hash destination are listed in Fragment links in the spec. Numbered steps with pandoc and grep fill Add an id to a spot in your file."
keyTakeaways:
  - "Both elements pandoc wrote for one sample file are quoted under Span and link under markdown."
  - "Grep counts for a run where the brackets survived sit in Brackets left as text in commonmark."
  - "Look in Adding attributes to commonmark for pandoc's output with that extension, quoted byte for byte."
  - "Output for a span wrapped around a sentence is set out in Putting words inside the span."
  - "Commands that write the HTML and count ids make up Add an id to a spot in your file."
publishedDate: 2026-10-21
dateModified: 2026-10-21
author: Bobby Huang
cluster: B
role: post
status: published
faq:
  - q: Is a markdown anchor link without heading text possible?
    a: "Steps that set an id and link to it are numbered in Add an id to a spot in your file."
  - q: Why did my empty brackets show up as text?
    a: "A run where they came through as characters is quoted in Brackets left as text in commonmark."
  - q: Which pandoc setting wrote the span?
    a: "Look under Adding bracketed_spans to commonmark for one setting quoted with its output."
  - q: Can the span hold words?
    a: "HTML written for a sentence placed inside the brackets is covered in Putting words inside the span."
related:
  - markdown-headings-anchor-links
  - markdown-text-color
  - raw-html-in-markdown
---

A markdown anchor link without heading text came down to two pieces in this test. One was an empty span carrying an id, set at the close of a sentence. The other was a link whose destination read `#signed`.

Each run happened once on one macOS machine, and the pandoc there was 3.8.3. Spec quotes and line numbers cite CommonMark at release 0.31.2. Everything below sticks to the HTML and native output pandoc printed, and it claims nothing past those bytes.

## Sample input with a blank span

Running `cat anc.md` printed:

```text
Jane Doe signed here. []{#signed}

Go back to [the signature](#signed).
```

Line 1 ends with a pair of square brackets holding nothing, then `{#signed}` in curly braces. Since no words sit between the brackets, any span built from them would be empty. Line 3 is an inline link. It reads `the signature`, and its destination is `#signed`.

Pandoc turned this file into HTML under four `-f` settings: `markdown`, `commonmark`, `commonmark+bracketed_spans` and `commonmark+attributes`. A second input, `anc-text.md`, was added by a script written for this post. It puts a whole sentence inside the brackets.

Ids that pandoc gives to headings are their own topic, and the [guide to headings and anchor links](markdown-headings-anchor-links.md) handles them.

## Span and link under markdown

The command:

```sh
pandoc -f markdown -t html anc.md
```

Output:

```html
<p>Jane Doe signed here. <span id="signed"></span></p>
<p>Go back to <a href="#signed">the signature</a>.</p>
```

On line 1, the brackets and braces are gone. In their place pandoc wrote an empty `span` element with an `id` attribute set to `signed`. On line 2, the link became an `a` element carrying `href="#signed"`. Strip the `#` from that href and what remains equals the span's id, letter for letter.

To see what pandoc built before it wrote any HTML, the extra test ran the same file with `-t native`. The first paragraph closed with this line:

```text
    , Span ( "signed" , [] , [] ) []
```

So the bracket pair became a `Span` element, and `"signed"` is the first value inside its parentheses. Further down, the second paragraph held a `Link` element ending in `( "#signed" , "" )`.

## Brackets left as text in commonmark

The command:

```sh
pandoc -f commonmark -t html anc.md
```

Output:

```html
<p>Jane Doe signed here. []{#signed}</p>
<p>Go back to <a href="#signed">the signature</a>.</p>
```

Here, the empty brackets and `{#signed}` came through as ordinary characters. No `span` element was written, and no `id` attribute either. The link line matches the `markdown` run byte for byte.

The extra test counted both results. Run against the `commonmark` output, the `id="signed"` count came back `0`, while the literal `[]{#signed}` count came back `1`. Against the `markdown` output, those two counts flipped, to `1` and `0`.

So this run's output held `href="#signed"` with no element anywhere carrying that id. A fixed-string count of `]{#` in the spec text found 0 lines. The phrase `block quote`, counted as a control, matched 54 lines in that same file.

## Adding bracketed_spans to commonmark

`bracketed_spans` is a pandoc extension. The `+` in the reader name switches it on for the `commonmark` reader:

```sh
pandoc -f commonmark+bracketed_spans -t html anc.md
```

Output:

```html
<p>Jane Doe signed here. <span id="signed"></span></p>
<p>Go back to <a href="#signed">the signature</a>.</p>
```

Those two lines are identical to the `markdown` output quoted above. This command ran three separate times across the test logs behind this post, and each of those three runs gave the same bytes.

For the empty-span pattern, then, this setting and the `markdown` reader agreed in this test.

## Adding attributes to commonmark

A re-run swapped in the `attributes` extension:

```sh
pandoc -f commonmark+attributes -t html anc.md
```

Pandoc wrote this, line break included:

```html
<p>Jane Doe signed here. [<span id="signed"
data-wrapper="1">]</span></p>
<p>Go back to <a href="#signed">the signature</a>.</p>
```

Read the bytes in order. An opening square bracket stays as text, ahead of the `span` tag. The tag carries two attributes, `id="signed"` and `data-wrapper="1"`. A closing square bracket then sits inside the span, right before `</span>`.

Counting `id="signed"` in this output gave `1`. Both square brackets remain in the paragraph here, while the runs quoted under Span and link under markdown and Adding bracketed_spans to commonmark dropped them.

## Putting words inside the span

The extra file `anc-text.md` moves the sentence between the brackets:

```text
[Jane Doe signed here.]{#signed}

Go back to [the signature](#signed).
```

Under `markdown` and under `commonmark+bracketed_spans`, pandoc wrote:

```html
<p><span id="signed">Jane Doe signed here.</span></p>
<p>Go back to <a href="#signed">the signature</a>.</p>
```

This time the span wraps the words, and the id sits on that same element. Under plain `commonmark`, line 1 came out as `[Jane Doe signed here.]{#signed}` inside a `p` element, brackets and braces intact.

Spans used to color or mark text belong to other walkthroughs: see [coloring Markdown text](markdown-text-color.md) and [highlighting Markdown text](markdown-highlight-text.md). Tags you type yourself are handled in the [walkthrough on HTML inside Markdown](raw-html-in-markdown.md).

## Fragment links in the spec

The link half of this pattern is in the spec. Lines 6836-6837 say a link holds link text, a link destination, and a title that may be left out. Line 7060 reads: "A link can contain fragment identifiers and queries". Its first example, on line 7064, is `[link](#fragment)`, and line 7070 gives the HTML as `<p><a href="#fragment">link</a></p>`.

Running `grep -n -F '](#'` on the spec matched one line, 7064. The id half, by contrast, came out in these runs under `markdown` and under `commonmark` with an extension added, and the plain `commonmark` run wrote no id at all.

## Add an id to a spot in your file

These commands back the steps below, and each one ran in the extra test:

```sh
cat anc.md
pandoc -f markdown -t html anc.md -o anc-md.html
pandoc -f commonmark -t html anc.md -o anc-cm.html
pandoc -f commonmark+bracketed_spans -t html anc.md -o anc-bs.html
grep -c 'id="signed"' anc-bs.html
grep -c 'href="#signed"' anc-bs.html
grep -c -F '[]{#signed}' anc-cm.html
```

To give a spot in your text an id and link to it, work through these six steps.

1. Choose the id. This test used `signed`.
2. Type the span where the spot belongs, either as `[]{#signed}` or as `[Jane Doe signed here.]{#signed}`. Under `markdown` and `commonmark+bracketed_spans`, pandoc 3.8.3 wrote a span carrying the id for both inputs. Plain `commonmark`, the comparison run, kept both as text and wrote no id.
3. Write the link with a hash before the same id, as in `[the signature](#signed)`.
4. Convert the file with `-f markdown` or `-f commonmark+bracketed_spans`, the two settings that wrote the span with no leftover brackets in this test. The extra test wrote `anc-md.html` and `anc-bs.html` from those two, plus `anc-cm.html` from plain `commonmark` for comparison.
5. Count ids and hrefs in the output. For `anc-bs.html`, both `grep -c` lines printed `1`.
6. Check for leftover brackets. On `anc-cm.html`, the `-F` count printed `1`, matching the plain `commonmark` run quoted under Brackets left as text in commonmark.

## Where Sashmark fits

Suppose Sashmark's v1 ends up matching the plan: fixing a sentence could happen right inside the rendered page mid-read. Lines nobody edited would then reach disk holding identical bytes, which is what a clean Round-Trip on the plan sets out to do. A Changes View, slated for that version, would also flag any edit made since your previous read-through, with git left aside.

It is in development and has not been released. The app itself has no AI features. See the [planned features](/features), the [help pages](/help) and the [roadmap](/roadmap) for where each part stands.

More at [sashmark.com](https://www.sashmark.com/).
