Skip to content

Commit f9dbecf

Browse files
committed
web: add linkable anchors for doc section headings
1 parent a46cfb8 commit f9dbecf

File tree

1 file changed

+38
-1
lines changed

1 file changed

+38
-1
lines changed

web/components/MarkdownContent.tsx

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,42 @@ import { MDXRemote } from 'next-mdx-remote/rsc';
22
import remarkGfm from 'remark-gfm';
33
import rehypeHighlight from 'rehype-highlight';
44
import rehypeSlug from 'rehype-slug';
5+
import type { ComponentPropsWithoutRef, ReactNode } from 'react';
56

67
interface MarkdownContentProps {
78
content: string;
89
}
910

11+
type HeadingLevel = 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
12+
13+
function createLinkableHeading(level: HeadingLevel) {
14+
return function LinkableHeading({
15+
children,
16+
id,
17+
...props
18+
}: ComponentPropsWithoutRef<HeadingLevel> & { children?: ReactNode }) {
19+
return (
20+
<div className="group relative">
21+
{id && (
22+
<a
23+
href={`#${id}`}
24+
aria-label={`Copy link to this section: ${id}`}
25+
title="Copy section link"
26+
className="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 no-underline transition-opacity text-gray-500 hover:text-black"
27+
>
28+
#
29+
</a>
30+
)}
31+
{level === 'h2' && <h2 id={id} {...props}>{children}</h2>}
32+
{level === 'h3' && <h3 id={id} {...props}>{children}</h3>}
33+
{level === 'h4' && <h4 id={id} {...props}>{children}</h4>}
34+
{level === 'h5' && <h5 id={id} {...props}>{children}</h5>}
35+
{level === 'h6' && <h6 id={id} {...props}>{children}</h6>}
36+
</div>
37+
);
38+
};
39+
}
40+
1041
export default async function MarkdownContent({ content }: MarkdownContentProps) {
1142
return (
1243
<div className="prose prose-lg max-w-none
@@ -22,6 +53,13 @@ export default async function MarkdownContent({ content }: MarkdownContentProps)
2253
prose-strong:font-bold prose-strong:text-black">
2354
<MDXRemote
2455
source={content}
56+
components={{
57+
h2: createLinkableHeading('h2'),
58+
h3: createLinkableHeading('h3'),
59+
h4: createLinkableHeading('h4'),
60+
h5: createLinkableHeading('h5'),
61+
h6: createLinkableHeading('h6'),
62+
}}
2563
options={{
2664
mdxOptions: {
2765
remarkPlugins: [remarkGfm],
@@ -32,4 +70,3 @@ export default async function MarkdownContent({ content }: MarkdownContentProps)
3270
</div>
3371
);
3472
}
35-

0 commit comments

Comments
 (0)