GitHub Flavored Markdown (GFM)
Configure the five GFM constructs (strikethrough, tables, autolink literals, task lists, footnotes) via markdown.gfm.
zfb's Markdown pipeline supports the five GitHub Flavored Markdown (GFM) constructs implemented by the markdown-rs parser: strikethrough, tables, autolink literals, task list items, and footnote definitions. The markdown.gfm config key controls which of the five are active.
Conservative default
When markdown.gfm is absent — including when the whole markdown block is omitted — zfb applies a conservative default: strikethrough and tables are on; autolink literals, task list items, and footnote definitions are off. This is the smallest behavioral delta from zfb's historical effective state (table-only), rather than turning on the full GFM surface unconditionally.
import { defineConfig } from "zfb/config";
export default defineConfig({
// markdown.gfm omitted — conservative default applies:
// strikethrough + table on, everything else off
});Value shapes
markdown.gfm accepts three shapes:
Absent (default)
Omit the field entirely (or omit markdown altogether) to get the conservative default shown above.
Boolean shorthand
export default defineConfig({
markdown: {
gfm: true, // every GFM construct on
},
});gfm: true turns every construct on, including autolink literals, task list items, and footnote definitions. gfm: false turns every construct off — including strikethrough and tables.
Per-construct object
export default defineConfig({
markdown: {
gfm: {
autolinkLiteral: true,
taskListItem: true,
// strikethrough, table, footnoteDefinition omitted here fall back
// to the conservative default, not to `false`
},
},
});Fields you omit from the object keep the conservative-default value for that construct (strikethrough: true, table: true, the other three false). Fields you set explicitly are honored verbatim.
Constructs
| Construct | Object key | Conservative default | Renders as |
|---|---|---|---|
Strikethrough (~~text~~) | strikethrough | On | <del>text</del> |
| Pipe tables | table | On | <table> |
Autolink literal (bare https:) | autolinkLiteral | Off | <a href="..."> |
Task list items (- [x] / - [ ]) | taskListItem | Off | Checkbox — see below |
Footnote definitions ([^ref]: ...) | footnoteDefinition | Off | Footnote section — see below |
Task list checkboxes
Enabling taskListItem renders each - [x] / - [ ] item's checked state as a checkbox: a disabled <input type="checkbox"> opens the item's own paragraph, followed by a single space, carrying a checked attribute only when the item is checked. A plain list item with no [x]/[ ] marker is unaffected.
The checkbox goes inside the item's paragraph rather than beside it so it renders on the same line as its label. zfb does not unwrap tight-list paragraphs, so a checkbox emitted as a sibling of the <p> would sit on its own line above the text.
- [x] Done
- [ ] Not done<ul>
<li><p><input type="checkbox" disabled checked /> Done</p></li>
<li><p><input type="checkbox" disabled /> Not done</p></li>
</ul>This is minimal, GitHub-compatible checkbox rendering, not GitHub's full reference markup — there is no contains-task-list/task-list-item class on the <ul>/<li>, and the checkbox is always disabled (this is static, server-rendered output with no client-side toggle handler). If you are diffing zfb's output against github.com's own rendering, expect the checkbox itself to match but the surrounding markup to be plainer.
Footnotes
Enabling footnoteDefinition renders [^ref] references and their [^ref]: ... definitions as a collected footnote section at the end of the document, matching GitHub's own footnote behavior:
Numbering is by first-reference order, not definition source order — a definition written first but referenced second gets the higher number.
IDs are
user-content-fn-{slug}for a definition anduser-content-fnref-{slug}for a reference, where{slug}is the footnote label slugified the same way heading anchors are (case-folded ASCII, punctuation stripped, non-ASCII such as CJK passed through unchanged). A label that slugifies to an empty string (e.g.[^!!!]) falls back to the footnote's 1-based number instead (user-content-fn-1).Repeated references to the same definition share one number but each get their own backreference id, so the definition can link back to the specific occurrence that was clicked.
Duplicate definitions for the same label collapse to one — the first definition wins, later ones are discarded silently (matching CommonMark's rule for duplicate link reference definitions).
Unreferenced definitions render nothing — numbering follows reference order, so a definition nobody points at has no place in the sequence.
The section carries
data-footnotes, a visually-hiddenrole="heading"landmark reading "Footnotes" (a<div>, not a real<h2>— a real heading tag would be picked up and rewritten by zfb's own heading-anchor/TOC plugins), and both reference and backreference links carry accessibility attributes (aria-describedby/aria-label) matching GitHub's markup.The landmark is hidden by an inline
style, not by thesr-onlyclass it also carries. zfb ships no stylesheet defining.sr-only, and because the class is emitted by zfb itself it never appears in any file Tailwind scans, so Tailwind's ownsr-onlyutility is never generated either — a class-only approach would leave the landmark visible in most projects. The class remains as a styling hook. To make the heading visible instead, override the inline style with!important:.footnotes [role="heading"] { position: static !important; width: auto !important; height: auto !important; margin: 0 !important; clip: auto !important; clip-path: none !important; }
Footnotes[^note] keep reading order.
[^note]: The footnote text.<p>Footnotes<sup><a href="#user-content-fn-note" id="user-content-fnref-note" data-footnote-ref aria-describedby="footnote-label">1</a></sup> keep reading order.</p>
<section data-footnotes class="footnotes">
<div role="heading" aria-level="2" class="sr-only" style="position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0" id="footnote-label">Footnotes</div>
<ol>
<li id="user-content-fn-note">
<p>The footnote text.</p>
<a href="#user-content-fnref-note" data-footnote-backref aria-label="Back to reference 1">↩</a>
</li>
</ol>
</section>Interaction with CJK-friendly handling
autolinkLiteral interacts with markdown.cjkFriendly (on by default): a bare URL flush against CJK text would otherwise swallow the trailing CJK run into the link href. See CJK-friendly emphasis for the boundary rule.
See also
Markdown Features — the full feature map.
CJK-friendly emphasis — the
autolinkLiteral+ CJK boundary interaction.