Anchor Links for Headings
This is guides how to add anchor links for headings in Starlight project.
Steps by steps
Section titled Steps by steps-
Installing dependencies & devDependencies using your favorite package manager:
Terminal window npm add rehype-autolink-headings rehype-slug hast-util-to-string hastscript html-escaperTerminal window pnpm add rehype-autolink-headings rehype-slug hast-util-to-string hastscript html-escaperTerminal window yarn add rehype-autolink-headings rehype-slug hast-util-to-string hastscript html-escaperTerminal window bun add rehype-autolink-headings rehype-slug hast-util-to-string hastscript html-escaperYou may be need add
@types/html-escaper
:Terminal window npm add --dev @types/html-escaperTerminal window pnpm add --dev @types/html-escaperTerminal window yarn add --dev @types/html-escaperTerminal window bun add --dev @types/html-escaper -
Create
rehype-autolink
plugins:plugins/rehype-autolink.ts // This code is licensed under the MIT license.// For more details, see: https://github.com/withastro/docs/blob/main/plugins/rehype-autolink.tsimport type { RehypePlugins } from 'astro'import type { Nodes } from 'hast'import { toString as hastToString } from 'hast-util-to-string'import { h } from 'hastscript'import { escape as htmlEscape } from 'html-escaper'import rehypeAutolinkHeadings from 'rehype-autolink-headings'const anchorLinkIcon = h('span',{ ariaHidden: 'true', class: 'anchor-icon' },h('svg',{ width: 16, height: 16, viewBox: '0 0 24 24' },h('path', {fill: 'currentcolor',d: 'm12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z',}),),)const anchorLinkSRLabel = (text: string) =>h('span',{ 'is:raw': true, class: 'sr-only' },`Section titled ${htmlEscape(text)}`,)const autolinkConfig = {properties: { class: 'anchor-link' },behavior: 'after',group: ({ tagName }: { tagName: string }) =>h('div', {tabIndex: -1,class: `heading-wrapper level-${tagName}`,}),content: (heading: Nodes) => [anchorLinkIcon,anchorLinkSRLabel(hastToString(heading)),],}export const rehypeAutolink = (): RehypePlugins => [[rehypeAutolinkHeadings, autolinkConfig],] -
Import
rehypeAutolink
andrehypeSlug
inastro.config.mjs
:astro.config.mjs // ...import { rehypeAutolink } from './plugins/rehype-autolink'import rehypeSlug from 'rehype-slug'export default defineConfig({// ...markdown: {rehypePlugins: [rehypeSlug,...rehypeAutolink(),],},integrations: [starlight({// ...customCss: ['./src/styles/headings.css',],})],}) -
Custom CSS for headings:
src/styles/headings.css .sl-markdown-content .heading-wrapper {--icon-size: 0.75em;--icon-spacing: 0.25em;line-height: var(--sl-line-height-headings);}.sl-markdown-content {.level-h2 {font-size: var(--sl-text-h2);}.level-h3 {font-size: var(--sl-text-h3);}.level-h4 {font-size: var(--sl-text-h4);}.level-h5 {font-size: var(--sl-text-h5);}}.sl-markdown-content .heading-wrapper> :first-child {margin-inline-end: calc(var(--icon-size) + var(--icon-spacing));display: inline;}.sl-markdown-content .heading-wrapper svg {display: inline;width: var(--icon-size);}.sl-markdown-content .anchor-link {margin-inline-start: calc(-1 * (var(--icon-size)));color: var(--sl-color-gray-3);&:hover,&:focus {color: var(--sl-color-text-accent);}}@media (hover: hover) {.sl-markdown-content .anchor-link {opacity: 0;}}.sl-markdown-content .heading-wrapper:hover>.anchor-link,.sl-markdown-content .anchor-link:focus {opacity: 1;}@media (min-width: 95em) {.sl-markdown-content .heading-wrapper {display: flex;flex-direction: row-reverse;justify-content: flex-end;gap: var(--icon-spacing);margin-inline-start: calc(-1 * (var(--icon-size) + var(--icon-spacing)));}.sl-markdown-content .heading-wrapper> :first-child,.sl-markdown-content .anchor-link {margin: 0;}}