unified

Project: micromark/micromark-extension-gfm-footnote

Package: micromark-extension-gfm-footnote@1.0.4

  1. Dependents: 0
  2. micromark extension to support GFM footnotes
  1. unified 169
  2. markdown 136
  3. micromark 20
  4. gfm 19
  5. micromark-extension 17
  6. definition 15
  7. footnote 7
  8. note 5

micromark-extension-gfm-footnote

Build Coverage Downloads Size Sponsors Backers Chat

micromark extension to support GFM footnotes.

Contents

What is this?

This package contains extensions that add support for footnotes enabled by GFM to micromark.

GitHub announced footnotes on September 30, 2021 but did not specify them in their GFM spec. As they are implemented in their parser and supported in all places where other GFM features work, they can be considered part of GFM. GitHub employs several other features (such as mentions or frontmatter) that are either not in their parser, or not in all places where GFM features work, which should not be considered GFM.

The implementation of footnotes on github.com is currently a bit buggy. The bugs have been reported on cmark-gfm. This micromark extension matches github.com except for its bugs.

When to use this

These tools are all low-level. In many cases, you want to use remark-gfm with remark instead.

Even when you want to use micromark, you likely want to use micromark-extension-gfm to support all GFM features. That extension includes this extension.

When working with mdast-util-from-markdown, you must combine this package with mdast-util-gfm-footnote.

Install

This package is ESM only. In Node.js (version 12.20+, 14.14+, 16.0+, or 18.0+), install with npm:

npm install micromark-extension-gfm-footnote

In Deno with esm.sh:

import {gfmFootnote, gfmFootnoteHtml} from 'https://esm.sh/micromark-extension-gfm-footnote@1'

In browsers with esm.sh:

<script type="module">
  import {gfmFootnote, gfmFootnoteHtml} from 'https://esm.sh/micromark-extension-gfm-footnote@1?bundle'
</script>

Use

Say our document example.md contains:

Using footnotes is fun![^1] They let you reference relevant information without disrupting the flow of what you’re trying to say.[^bignote]

[^1]: This is the first footnote.
[^bignote]: Here’s one with multiple paragraphs and code.

    Indent paragraphs to include them in the footnote.

    ```
    my code
    ```

    Add as many paragraphs as you like.

Text here and here and here.
[Learn more about markdown and footnotes in markdown](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#footnotes)

…and our module example.js looks as follows:

import fs from 'node:fs/promises'
import {micromark} from 'micromark'
import {gfmFootnote, gfmFootnoteHtml} from 'micromark-extension-gfm-footnote'

const output = micromark(await fs.readFile('example.md'), {
  extensions: [gfmFootnote()],
  htmlExtensions: [gfmFootnoteHtml()]
})

console.log(output)

…now running node example.js yields:

<p>Using footnotes is fun!<sup><a href="#user-content-fn-1" id="user-content-fnref-1" data-footnote-ref="" aria-describedby="footnote-label">1</a></sup> They let you reference relevant information without disrupting the flow of what you’re trying to say.<sup><a href="#user-content-fn-bignote" id="user-content-fnref-bignote" data-footnote-ref="" aria-describedby="footnote-label">2</a></sup></p>
<p>Text here and here and here.
<a href="https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#footnotes">Learn more about markdown and footnotes in markdown</a></p>
<section data-footnotes="" class="footnotes"><h2 id="footnote-label" class="sr-only">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>This is the first footnote. <a href="#user-content-fnref-1" data-footnote-backref="" class="data-footnote-backref" aria-label="Back to content"></a></p>
</li>
<li id="user-content-fn-bignote">
<p>Here’s one with multiple paragraphs and code.</p>
<p>Indent paragraphs to include them in the footnote.</p>
<pre><code>my code
</code></pre>
<p>Add as many paragraphs as you like. <a href="#user-content-fnref-bignote" data-footnote-backref="" class="data-footnote-backref" aria-label="Back to content"></a></p>
</li>
</ol>
</section>

API

This package exports the identifiers gfmFootnote and gfmFootnoteHtml. There is no default export.

The export map supports the endorsed development condition. Run node --conditions development module.js to get instrumented dev code. Without this condition, production code is loaded.

gfmFootnote()

Function that can be called (no options yet) to get a syntax extension for micromark (passed in extensions).

gfmFootnoteHtml(htmlOptions)

Function that can be called to get an HTML extension for micromark (passed in htmlExtensions).

htmlOptions

Configuration (optional).

htmlOptions.clobberPrefix

Prefix to use before the id attribute to prevent it from clobbering (string, default: 'user-content-'). DOM clobbering is this:

<p id=x></p>
<script>console.log(x)</script>
<!-- The element is printed to the console. -->

Elements by their ID are made available in browsers on the window object. Using a prefix prevents this from being a problem

htmlOptions.label

Label to use for the footnotes section (string, default: 'Footnotes'). Affects screen reader users. Change it if you’re authoring in a different language.

htmlOptions.backLabel

Label to use from backreferences back to their footnote call (string, default: 'Back to content'). Affects screen reader users. Change it if you’re authoring in a different language.

Authoring

When authoring markdown with footnotes, it’s recommended to use words instead of numbers (or letters or anything with an order) as references. That makes it easier to reuse and reorder footnotes.

It’s recommended to place footnotes definitions at the bottom of the document.

HTML

GFM footnotes relate to several HTML elements and ARIA properties. The structure for generated references looks as follows:

<sup><a href="#user-content-fn-xxx" id="user-content-fnref-xxx" data-footnote-ref="" aria-describedby="footnote-label">111</a></sup></p>

Where xxx is the identifier used in the markdown source, and 111 the number of corresponding, listed, definition.

See § 4.5.19 The sub and sup elements, § 4.5.1 The a element, and § 3.2.6.6 Embedding custom non-visible data with the data-* attributes in the HTML spec, and § 6.7 aria-describedby property in WAI-ARIA, for more info.

The structure for the section at the end of the document that contains generated definitions looks as follows:

<section data-footnotes="" class="footnotes"><h2 id="footnote-label" class="sr-only">Footnotes</h2>
<ol>
<!--…-->
</ol>
</section>

See § 4.3.3 The section element, § 4.3.6 The h1, h2, h3, h4, h5, and h6 elements, and § 4.4.5 The ol element in the HTML spec for more info.

The structure for each generated definition looks as follows:

<li id="user-content-fn-xxx">
yyy
<a href="#user-content-fnref-xxx" data-footnote-backref="" class="data-footnote-backref" aria-label="Back to content"></a>
</li>

Where xxx is the identifier used in the markdown source and yyy the content used in the definition.

See § 4.4.8 The li element, § 4.5.1 The a element, and § 3.2.6.6 Embedding custom non-visible data with the data-* attributes in the HTML spec, and § 6.7 aria-label property in WAI-ARIA, for more info.

CSS

The following CSS is needed to make footnotes look a bit like GitHub (and fixes a bug). For the complete actual CSS see sindresorhus/github-markdown-css.

/* Style the footnotes section. */
.footnotes {
  font-size: smaller;
  color: #8b949e;
  border-top: 1px solid #30363d;
}

/* Hide the section label for visual users. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  word-wrap: normal;
  border: 0;
}

/* Place `[` and `]` around footnote calls. */
[data-footnote-ref]::before {
  content: '[';
}

[data-footnote-ref]::after {
  content: ']';
}

Syntax

Footnotes form with, roughly, the following BNF:

footnote_reference ::= label_footnote
; Restriction: any indent is eaten after `:`, indented code is not possible
footnote_definition ::= label_footnote ':' 0.*space_or_tab 0.*code *( eol *( blank_line eol ) indented_filled_line )

; Restriction: maximum `999` codes allowed inside.
; Restriction: no blank lines.
; Restriction: at least 1 non-space and non-eol code must exist.
label_footnote ::= '[' '^' *( code - '[' - '\\' - ']' | '\\' [ '[' | '\\' | ']' ] ) ']'
; Restriction: at least one `code` must not be whitespace.
indented_filled_line ::= 4space_or_tab *code
blank_line ::= *space_or_tab
eol ::= '\r' | '\r\n' | '\n'
space_or_tab ::= ' ' | '\t'

Types

This package is fully typed with TypeScript. It exports the additional type HtmlOptions.

Compatibility

This package is at least compatible with all maintained versions of Node.js. As of now, that is Node.js 12.20+, 14.14+, 16.0+, and 18.0+. It also works in Deno and modern browsers.

Security

This package is safe. Setting htmlOptions.clobberPrefix = '' is dangerous.

Contribute

See contributing.md in micromark/.github for ways to get started. See support.md for ways to get help.

This project has a code of conduct. By interacting with this repository, organization, or community you agree to abide by its terms.

License

MIT © Titus Wormer