This Eleventy (11ty) plugin generates a table of contents (TOC) from page headings using an Eleventy filter.
It adds a navigation landmark with a heading and ARIA role to make it accessible[1] [2]. And creates a nested list of headings by level.
Markdown
# Fruits
## Apples
### Empire
### Fuji
### Pink Lady
## Pears
### Bartlett
### Bosc
### Starkrimson
HTML
<nav class="nav-toc" role="navigation" aria-labelledby="nav-toc">
<h2 class="nav-toc-heading" id="nav-toc">Table of contents</h2>
<ol class="nav-toc-list">
<li class="nav-toc-list-item">
<a class="nav-toc-list-item-anchor" href="#apples">Apples</a>
<ol class="nav-toc-list">
<li class="nav-toc-list-item"><a class="nav-toc-list-item-anchor" href="#empire">Empire</a></li>
<li class="nav-toc-list-item"><a class="nav-toc-list-item-anchor" href="#fuji">Fuji</a></li>
<li class="nav-toc-list-item"><a class="nav-toc-list-item-anchor" href="#pink-lady">Pink Lady</a></li>
</ol>
</li>
<li class="nav-toc-list-item">
<a class="nav-toc-list-item-anchor" href="#oranges">Pears</a>
<ol class="nav-toc-list">
<li class="nav-toc-list-item"><a class="nav-toc-list-item-anchor" href="#bartlett">Bartlett</a></li>
<li class="nav-toc-list-item"><a class="nav-toc-list-item-anchor" href="#bosc">Bosc</a></li>
<li class="nav-toc-list-item"><a class="nav-toc-list-item-anchor" href="#starkrimson">Starkrimson</a></li>
</ol>
</li>
</ol>
</nav>
npm install @thedigitalman/eleventy-plugin-toc-a11y --save-dev
All headings must have an id
attribute. The plugin uses the id
attribute of the headings it finds to build the navigation.
You can do this manually, but using markdown-it
and markdown-it-anchor
make it easy.
Open your Eleventy config file (probably .eleventy.js
), use addPlugin
, and add some Markdown settings.
const eleventyPluginTOC = require( '@thedigitalman/eleventy-plugin-toc-a11y' );
const markdownIt = require( 'markdown-it' );
const markdownItAnchor = require( 'markdown-it-anchor' );
module.exports = function ( eleventyConfig ) {
// Plugins
eleventyConfig.addPlugin( eleventyPluginTOC );
// Markdown settings
eleventyConfig.setLibrary( 'md',
markdownIt().use( markdownItAnchor )
);
}
All headings must be in proper order without skipping levels [3].
Open a layout template file and add the filter before your content. This gives you a good outline, and lets people review the TOC before reading the content.
Liquid
{{ content | toc }}
<main>
{{ content }}
</main>
Nunjucks
Always include the safe filter when using Nunjucks.
{{ content | toc | safe }}
<main>
{{ content | safe }}
</main>
{
tags: ['h2', 'h3', 'h4', 'h5', 'h6'],
wrapper: 'nav',
wrapperClass: 'nav-toc',
heading: true,
headingClass: 'nav-toc-heading',
headingLevel: 'h2',
headingText: 'Table of contents',
listType: 'ol',
listClass: 'nav-toc-list',
listItemClass: 'nav-toc-list-item',
listItemAnchorClass: 'nav-toc-list-item-anchor'
}
Option | Data Type | Description | Notes |
---|---|---|---|
tags |
Object | An array of heading levels to include in the TOC. | Page titles (i.e. h1 ) should be excluded. |
wrapper |
String | The navigation landmark element of the TOC. | In most cases use nav . If you replace it, be sure it’s valid HTML and accessible. |
wrapperClass |
String | The CSS class name for the TOC parent element. | Using an empty string removes the class attribute. |
heading |
Boolean | Whether the TOC uses a heading element. | Using heading text for sections helps everyone. |
headingClass |
String | The CSS class name for the TOC heading element. | Using an empty string removes the class attribute. |
headingLevel |
String | The level of the TOC heading element. | In most cases use h2 , but you can use h2 – h6 . If you replace it, be sure it’s valid HTML and accessible. |
headingText |
String | The TOC heading element text. | Keep it concise and relevant. |
listType |
String | The type of list for navigation items. | Use ol or ul . Other elements won’t work. |
listClass |
String | The CSS class name for the list. | Using an empty string removes the class attribute. |
listItemClass |
String | The CSS class name for each list item. | Using an empty string removes the class attribute. |
listItemAnchorClass |
String | The CSS class name for each anchor in a list item. | Using an empty string removes the class attribute. |
You can override the default options in the Eleventy config file or inline.
Eleventy config file
eleventyConfig.addPlugin( pluginToC, {
wrapperClass: 'toc',
headingClass: 'toc-heading',
headingText: 'Topics',
listType: 'ul'
});
Inline (Liquid)
{{ content | toc(wrapperClass='toc',headingClass='toc-heading',headingText='Topics',listType='ul') }}
Inline (Nunjucks)
{{ content | toc(wrapperClass='toc',headingClass='toc-heading',headingText='Topics',listType='ul') | safe }}
- 4.3.6 Navigation | WAI-ARIA Authoring Practices 1.2 W3C ↑
- Navigation Landmark: ARIA Landmark Example W3C ARIA Authoring Practices Task Force ↑
- H42: Using h1-h6 to identify headings Techniques for WCAG 2.1 ↑