Skip to content

Feature list

The complete, numbered list of what the format supports — one row per feature, each with a stable F<n> that the rest of the project can cite. This card is the index; it is deliberately not the normative text. Where a construct has a specification, the row points at it, because a feature list that restates its own spec drifts from it by the second edit.

Two things live here and nowhere else: the numbering, and the status of every feature including the ones that do not exist yet. See hyper-markdown for what the format is for, and md-hmd-interop for its relation to plain markdown.

How to read the table

One flat table, six columns, every feature the format has or has considered — built or not, accepted or rejected.

Column Meaning
# Stable identifier. Never reused, never renumbered.
Feature The name to call it in prose.
Written What an author types.
Comes from or inspired by Where the idea is sourced from.
Implemented by What makes it work — the format itself, an extension, a binary — or if unbuilt.
Status Where it stands, from the vocabulary below.

Status vocabularyshipped works today and is gated by a test · planned specified or agreed, not built · nice to have wanted, unspecified, nobody has committed · deferred explicitly postponed to a later spec · rejected decided against, recorded so it is not relitigated.

# Feature Written Comes from or inspired by Implemented by Status
F1 Wikilink [[page]] MediaWiki / WikiWikiWeb the format, HMD-0001 §2 / §5 shipped
F2 Aliased wikilink [[page|display text]] MediaWiki the format, HMD-0001 §2 shipped
F3 Heading reference [[page#Section]] MediaWiki the format, HMD-0001 §2 / §3 shipped
F4 Block anchor text ^block-id Obsidian the format, HMD-0001 §2 shipped
F5 Block reference [[page#^block-id]] Obsidian the format, HMD-0001 §2 shipped
F6 Embed ![[…]] Obsidian (spelling), MediaWiki transclusion (concept) the format, HMD-0001 §6 shipped
F7 Tags tags: [area/auth] Jekyll frontmatter, GitHub the format, HMD-0001 §5.3 shipped
F8 Per-card toggles use: [no_autodiscovery] the format, HMD-0001 §5.3 shipped
F9 Explicit imports import: [from /shared import *] Python's import the format, HMD-0001 §5.3 shipped
F10 Nav position nav: 10 MkDocs nav: the format, HMD-0002 §2 shipped
F11 Callouts !!! note reStructuredText admonitions admonition shipped
F12 Collapsible callouts ??? tip reStructuredText admonitions pymdownx.details shipped
F13 Fenced code ```lang GFM pymdownx.superfences shipped
F14 Tables | a | b | GFM tables shipped
F15 Task lists - [x] item GFM pymdownx.tasklist shipped
F16 Footnotes text[^1] Pandoc footnotes shipped
F17 Display and inline math $x$, $$…$$ LaTeX pymdownx.arithmatex + MathJax shipped
F18 D2 diagrams ```d2 D2 diagram.py + the d2 binary shipped
F19 Heading permalinks (automatic) MkDocs / Sphinx toc shipped
F20 Strikethrough ~~text~~ GFM pymdownx.tilde shipped
F21 Inline properties key:: value Logseq — (HMD-0003 §2) deferred
F22 Highlights ==text== Obsidian, CriticMarkup nice to have
F23 Mermaid diagrams ```mermaid Mermaid rejected
F24 Templates Confluence, MediaWiki — (HMD-0003 §5) deferred
F25 HQL — query language Obsidian Dataview, Confluence CQL, SQL — (HMD-0003) nice to have
F26 Backlinks and category pages Wikipedia, Obsidian nice to have
F27 Escapes for reserved characters nice to have
F28 A literal [[ outside a code span nice to have

F6 is any of F1–F5 with a ! in front, so it multiplies the table rather than adding a new construct. Expansion is textual, does not shift heading levels, detects cycles (HMD007), and caps depth at 16 (HMD008).

F18 is the one row whose implementation is not purely bought: a d2 fence is a diagram, not a code block, it renders through the binary to a data: URI image, and it degrades to a labelled placeholder when the binary is absent. F19 is load-bearing rather than decorative — F3 resolves against the slugs toc assigns, so the link and its destination cannot disagree.

F23 is rejected by the source, not merely unbuilt, because D2 (F18) already covers diagrams and two diagram languages is one too many. F22 was rejected on the same footing and is no longer — it is now nice to have, wanted but unspecified and uncommitted.

The Comes from column distinguishes the wiki tradition from Obsidian deliberately, because the two are routinely conflated. F1–F3 are MediaWiki syntax, and older than that in spirit: [[…]] and its | alias have been the wiki link since MediaWiki carried them to Wikipedia in 2002, with WikiWikiWeb (1995) upstream of both, and # for a fragment is older still — it is URI syntax. Obsidian adopted all three in 2020 because they were already the convention every wiki reader knew; it did not invent them. What Obsidian did contribute is the block layer, F4 and F5: the ^block-id anchor and the [[page#^block-id]] reference have no MediaWiki ancestor, since MediaWiki addresses sections but never blocks. F6 splits — the ![[…]] spelling is Obsidian's, the idea of transcluding one page into another is MediaWiki's ({{Template}}, 2004), which is the same lineage F24 cites.

Note that F26 already credited Wikipedia while F1–F3 credited Obsidian for that same inheritance; the rows now agree.

Where the format does diverge from Obsidian is the part that matters most: F1–F6 explicitly do not copy Obsidian's link-text character restrictions or its shortest-unique-path resolution. The format takes the wiki tradition's syntax and rejects Obsidian's resolution semantics.

F27 and F28 are the two real gaps, and they are the same gap seen from either end. There is no escape mechanism anywhere: the reserved set inside a target is exactly [, ], |, #, ^, and newline, so a target needing one of them is simply malformed (HMD010) — and in the other direction, the only way to show a wikilink without resolving it is to put it in a code span or a fence, because those are the regions the scanner masks. Raw inline HTML is not masked, so <code> written by hand still gets scanned.

The F2 row above is where that bites: it renders a pipe inside a table cell, which forces raw <code> (issue 0006), which in turn forces &#91;&#91; for the brackets so the scanner does not read the cell as a real link. A card that documents the format is the one card most likely to need this, which is why the gap is recorded rather than worked around quietly.

Presentation layers

The feature list above is renderer-agnostic — it says what the format has. What reaches a reader depends on which surface renders it, and there are two. Both consume the same semantics; neither redefines them.

Layer 1 — MkDocs, the published book

Specified by HMD-0002, implemented as a single file, mkdocs_plugin.py, registered through [project.entry-points."mkdocs.plugins"]. It expands embeds and rewrites links at on_page_markdown, before Python-Markdown runs, so every bought extension in Blocks C and D sees ordinary markdown and needs to know nothing about the format.

This layer is erasure: the output is flat HTML in which the boundary between a card and the content it embedded is gone, along with the provenance of every link. That is correct for a shipping format and fatal for a preview — see md-hmd-interop, which is where the argument lives. Book mode only; backlinks and category pages are F26.

One structural property is worth stating, because it is what keeps the layer cheap: MkDocs touches exactly one file. parse, resolve, embed, urls, and lint do not import it, and HMD-0002 §1–§4 name no renderer. Swapping it is one file and mkdocs.yml, not a re-specification.

Layer 2 — preview mode, the editor surface

The sibling line: a TypeScript document model and a VS Code webview that renders a card as you edit it. It is specified on the feat/vsc-ext branch by HMD-0020 (@hyper-markdown/core, the document model), HMD-0021 (the extension and its preview surface), and HMD-0022 (D2 in the webview, under its content security policy). Those proposals are not on this branch yet — the branch merges soon, and the links belong here once it does.

The load-bearing difference from Layer 1 is that preview mode keeps the embed boundary. It renders F6 as visibly embedded content with its source attached, which is the one thing it does that a plain markdown previewer cannot, and the reason the editor line rejected flat markdown as its transport. Layer 1 throws that away deliberately; Layer 2 cannot.

Python stays canonical for semantics. A second parser that resolved a name differently from the command line would be worse than no second parser, so HMD-0001 and the conformance corpus are the contract between the two layers rather than a shared runtime.

Support matrix

Where the two layers differ. Everything not listed is shipped on Layer 1 and tracked against Layer 2 by its own proposals.

# Feature Layer 1 — MkDocs Layer 2 — preview
F6 Embed expanded, boundary erased expanded, boundary kept
F10 nav: orders the site nav no meaning
F18 D2 diagrams data: URI <img> data: URI under the webview CSP
F26 Backlinks out of scope the natural home for it

Changing this card

Add a feature by appending the next free number in its block; never renumber and never reuse. Move a row's Status in the same commit that changes the code, the way a proposal's tracker moves a work point — see tracking. This card tracks the format; a tracker tracks the implementation of one proposal, and the two answer different questions.