diff --git a/lib/markdown.ts b/lib/markdown.ts index 4db8e2b..890ff1b 100644 --- a/lib/markdown.ts +++ b/lib/markdown.ts @@ -1,4 +1,7 @@ import MarkdownIt from "markdown-it"; +import StateCore from "markdown-it/lib/rules_core/state_core"; +import MarkdownItAnchor from "markdown-it-anchor"; +import slugify from "@sindresorhus/slugify"; import * as util from "./util"; const md = new MarkdownIt({ @@ -7,6 +10,42 @@ const md = new MarkdownIt({ typographer: true }); +// Inserts heading anchors +// Based on https://github.com/valeriangalliat/markdown-it-anchor +md.core.ruler.push("anchor", (state) => { + const tokens = state.tokens; + + tokens + .filter((token) => token.type === "heading_open") + .forEach((token) => { + const index = tokens.indexOf(token); + const title = tokens[index + 1] + .children + .filter((token) => token.type === "text" || token.type == "code_inline") + .reduce((acc, t) => acc + t.content, ""); + + let slug = token.attrGet("id") || slugify(title); + if (token.attrGet("id") == null) { + token.attrPush(["id", slug]); + } + + const linkTokens = [ + Object.assign(new state.Token("link_open", "a", 1), { + attrs: [ + ["class", "header-anchor"], + ["href", "#" + slug], + ["aria-hidden", "true"] + ] + }), + Object.assign(new state.Token("html_block", "", 0), { content: token.markup }), + new state.Token("link_close", "a", -1), + Object.assign(new state.Token("text", "", 0), { content: " " }) + ]; + + state.tokens[index + 1].children.unshift(...linkTokens); + }); +}); + export function render(text: string): string { return md.render(text); } \ No newline at end of file diff --git a/site/css/main.scss b/site/css/main.scss index 1b74c2f..7fa1eb2 100644 --- a/site/css/main.scss +++ b/site/css/main.scss @@ -43,14 +43,6 @@ article { margin-bottom: 75px; color: var(--content-text-color); - h1::before { content: "#"; } - h2::before { content: "##"; } - h3::before { content: "###"; } - h1::before, h2::before, h3::before { - font-family: $monospace; - color: var(--accent-color); - margin-right: 10px; - } @media (min-width: 768px) { a::before { content: "["; } a::after { content: "](" attr(href) ")"; word-wrap: break-word; } @@ -102,6 +94,12 @@ article { margin-bottom: 0; font-size: 1.8rem; + &::before { + content: "#"; + font-family: $monospace; + color: var(--accent-color); + } + > a { color: var(--content-text-color); text-decoration: none; @@ -132,6 +130,13 @@ article { h1, h2, h3, h4, h5, h6 { font-family: $sansSerif; + + .header-anchor { + font-family: $monospace; + color: var(--accent-color); + + &::before, &::after { content: ""; } + } } h1 { font-size: 1.8rem; } diff --git a/site/index.html.ejs b/site/index.html.ejs index 92b58a5..a14daec 100644 --- a/site/index.html.ejs +++ b/site/index.html.ejs @@ -6,7 +6,11 @@ metadata.layout = "default.html.ejs"