slug
Filter Extension
#The default slug
filter uses slugify under the hood, but sometimes the default behavior isn't quite enough if you are using special characters, including emoji.
We can override the filter to enable strict
mode, enforce lowercasing, and optionally add any other characters you encounter being problematic. In this case, I'm enforcing removing "
because I have experienced issues without explicitly defining it.
Usage: {{ title | slug }}
// Import prior to `module.exports` within `.eleventy.js`
const slugify = require("slugify");
eleventyConfig.addFilter("slug", (str) => {
if (!str) {
return;
}
return slugify(str, {
lower: true,
strict: true,
remove: /["]/g,
});
});
Enable anchor links on content headings
#Eleventy uses markdown-it for Markdown parsing, and shows a few options for configuring it in the 11ty docs.
There are several plugins you can add to extend markdown-it, but in this example we are adding anchor links to our content headings. We're also extending the idea from our slug
update to update which characters are removed and replaced to create anchors.
Kudos to Nicolas Hoizey for the concise method of modifying the
markdown-it-anchor
behavior to add a wrapping div to assist in styling placement of the anchor symbol. And for raising the issue to improve the base behavior of this plugin.
The markdown-it-anchor
plugin added three permalink output options to assist with accessibility. This snippet uses the linkInsideHeader
option with a custom symbol
and permalink
render function, which produces output like the following that also adds a wrapping .heading-wrapper
. I've also selected the aria-labelledby
option to prevent the extra text showing up in RSS feeds and search engine results.
// npm install --save-dev markdown-it-anchor slugify
// Import prior to `module.exports` within `.eleventy.js`
const markdownIt = require("markdown-it");
const markdownItAnchor = require("markdown-it-anchor");
// If not already added from previous tip
const slugify = require("slugify");
const linkAfterHeader = markdownItAnchor.permalink.linkAfterHeader({
class: "anchor",
symbol: "<span hidden>#</span>",
style: "aria-labelledby",
});
const markdownItAnchorOptions = {
level: [1, 2, 3],
slugify: (str) =>
slugify(str, {
lower: true,
strict: true,
remove: /["]/g,
}),
tabIndex: false,
permalink(slug, opts, state, idx) {
state.tokens.splice(
idx,
0,
Object.assign(new state.Token("div_open", "div", 1), {
// Add class "header-wrapper [h1 or h2 or h3]"
attrs: [["class", `heading-wrapper ${state.tokens[idx].tag}`]],
block: true,
})
);
state.tokens.splice(
idx + 4,
0,
Object.assign(new state.Token("div_close", "div", -1), {
block: true,
})
);
linkAfterHeader(slug, opts, state, idx + 1);
},
};
/* Markdown Overrides */
let markdownLibrary = markdownIt({
html: true,
}).use(markdownItAnchor, markdownItAnchorOptions);
// This is the part that tells 11ty to swap to our custom config
eleventyConfig.setLibrary("md", markdownLibrary);
Example output:
<div class="heading-wrapper h2">
<h2 id="enable-anchor-links-on-content-headings">Enable anchor links on content headings</h2>
<a
class="tdbc-anchor"
href="#enable-anchor-links-on-content-headings"
aria-labelledby="enable-anchor-links-on-content-headings"
>
<span hidden>#</span>
</a>
</div>
Why use hidden
? Because RSS, search engine results, and "reader mode" will (usually) respect it and prevent those environments from showing the anchor symbol.
You'll want the following CSS or similar to handle showing the anchor link symbol on your website:
/* Make the '#' anchor visible on your site */
.anchor hidden {
display: block;
}