zudo-doc
GitHub repository

Type to search...

to open search from anywhere

Heading Links

Created May 28, 2026Updated Jun 7, 2026Takeshi Takatsudo

Adds slug-based id attributes and self-referencing anchor links to every heading.

Core feature — always active. The anchor-ID strategy is configurable (see below).

What it does

Every heading at h2 and below automatically receives:

  1. A slug-based id attribute derived from the heading text using zfb's own slugify (github-slugger-like, but it collapses punctuation such as . and / to - rather than stripping it).

  2. A self-referencing anchor element wrapping the heading text, so readers can copy a direct deep-link.

  3. Deduplication: when the same slug would appear more than once in a document, a counter suffix is appended (overview, overview-1, overview-2, …).

h1 is never assigned an id — the page title (from frontmatter) is the document's only h1.

ID strategy

The scheme used to build the id is selected by markdown.features.headingIds.strategy in zfb.config.ts. zudo-doc wires this to a single setting, headingIdStrategy in src/config/settings.ts, so the rendered IDs and the right-hand TOC anchors are driven by one source of truth.

// zfb.config.ts
markdown: {
  features: {
    headingIds: { strategy: settings.headingIdStrategy }, // "flat" | "hierarchical"
  },
}

flat

Slugs (from zfb's own slugify, not npm github-slugger) with one dedup counter shared across h2h6 (overview, overview-1, …). This is zfb's default scheme.

hierarchical (used by this site)

Each heading's id is prefixed with its ancestor chain, joined by -:

## Foo

### Moo

#### Mew

renders as id="foo", id="foo-moo", id="foo-moo-mew" (instead of the flat foo, moo, mew). The in-heading anchor href, the right-hand TOC, the Heading Marker TOC, and the TOC export all follow the same IDs.

Details:

  • A duplicated full path still gets the dedup counter (a-b, a-b-1).

  • A deduplicated parent contributes its final ID to children: a second ## Foo becomes foo-1, so its ### Bar becomes foo-1-bar.

  • Hierarchical anchors are reconstructible from the heading outline and collide far less often than flat slugs, at the cost of longer URLs.

Anchor-breaking

Switching strategies is anchor-breaking: existing deep links to nested headings (#moo) stop resolving once IDs become #foo-moo. zudo-doc adopted hierarchical deliberately (upstream zfb#871; original finding #1938) with no backward-compat for old anchors.

Deep-linking

Because each heading carries a stable id, you can link to any section of a page from another page. Under the hierarchical strategy, use the ancestor-prefixed form for nested headings:

[See the Moo section](./other-page.mdx#foo-moo)

Notes

  • zudo-doc's right-hand TOC builder (pages/lib/_extract-headings.ts) mirrors the same strategy, so the TOC href="#…" values always match the rendered heading ids.

  • The Heading Marker TOC opt-in feature depends on the stable heading identifiers produced by this plugin. When headingMarkerToc is enabled, it runs after heading links have been applied.

Revision History

Takeshi TakatsudoCreated: 2026-05-29T01:40:39+09:00Updated: 2026-06-07T17:11:47+09:00

AI Assistant

Ask a question about the documentation.