diff --git a/lib/markdown.ts b/lib/markdown.ts index 4db8e2b..2d55648 100644 --- a/lib/markdown.ts +++ b/lib/markdown.ts @@ -1,4 +1,5 @@ import MarkdownIt from "markdown-it"; +import slugify from "@sindresorhus/slugify"; import * as util from "./util"; const md = new MarkdownIt({ @@ -7,6 +8,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..8ee750a 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,13 @@ article { margin-bottom: 0; font-size: 1.8rem; + &::before { + content: "#"; + font-family: $monospace; + color: var(--accent-color); + user-select: none; + } + > a { color: var(--content-text-color); text-decoration: none; @@ -132,6 +131,14 @@ article { h1, h2, h3, h4, h5, h6 { font-family: $sansSerif; + + .header-anchor { + font-family: $monospace; + color: var(--accent-color); + user-select: none; + + &::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"
<% for (const post of posts) { %>
-

<%= post.metadata.title %>

+

+ + <%= post.metadata.title %> + +

<%- include("includes/article-meta.html.ejs", { metadata: post.metadata }) %>
<%- post.metadata.excerpt %> diff --git a/site/layouts/article.html.ejs b/site/layouts/article.html.ejs index 270c2bc..a9a502d 100644 --- a/site/layouts/article.html.ejs +++ b/site/layouts/article.html.ejs @@ -4,7 +4,9 @@ metadata.layout = "default.html.ejs"
-

<%= metadata.title %>

+

+ <%= metadata.title %> +

<%- include("../includes/article-meta.html.ejs", { metadata }) %>
<%- content %> diff --git a/site/tutorial-series.html.ejs b/site/tutorial-series.html.ejs index 62e4593..8bd5595 100644 --- a/site/tutorial-series.html.ejs +++ b/site/tutorial-series.html.ejs @@ -10,7 +10,11 @@ metadata.layout = "default.html.ejs"; <% for (const tutorial of tutorials) { %> <% } %> diff --git a/site/tutorials.html.ejs b/site/tutorials.html.ejs index 089f095..f618e9e 100644 --- a/site/tutorials.html.ejs +++ b/site/tutorials.html.ejs @@ -5,7 +5,11 @@ metadata.layout = "default.html.ejs"
<% for (const series of allSeries) { %>
-

<%= series.index.metadata.title %>

+

+ + <%= series.index.metadata.title %> + +