Skip to content

Commit d13851d

Browse files
authored
Merge pull request #194 from czepluch/fix/links-in-section-headers
Add anchor links to blog post section headers
2 parents c4f52a6 + 4a6a9f2 commit d13851d

8 files changed

Lines changed: 131 additions & 29 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"react-icons": "^4.9.0",
3939
"react-leaflet": "^4.2.1",
4040
"react-syntax-highlighter": "^15.5.0",
41+
"rehype-slug": "^6.0.0",
4142
"remove-markdown": "^0.5.0",
4243
"typescript": "4.9.4"
4344
},

src/components/BlogPost.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Box, type BoxProps } from '@chakra-ui/react'
22
import ReactMarkdown from 'react-markdown'
3+
import rehypeSlug from 'rehype-slug'
34
import gfm from 'remark-gfm'
45
import { MDStyles } from '@/styles'
56

@@ -9,7 +10,11 @@ interface BlogPostProps extends BoxProps {
910
export const BlogPost: React.FC<BlogPostProps> = ({ content, ...boxProps }) => (
1011
<>
1112
<Box as="article" {...boxProps}>
12-
<ReactMarkdown components={MDStyles} remarkPlugins={[gfm]}>
13+
<ReactMarkdown
14+
components={MDStyles}
15+
remarkPlugins={[gfm]}
16+
rehypePlugins={[rehypeSlug]}
17+
>
1318
{content}
1419
</ReactMarkdown>
1520
</Box>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
Heading,
3+
type HeadingProps,
4+
Link as ChakraLink,
5+
} from '@chakra-ui/react'
6+
import { LinkIcon } from '@chakra-ui/icons'
7+
8+
interface HeadingWithAnchorProps extends HeadingProps {
9+
id?: string
10+
}
11+
12+
export const HeadingWithAnchor: React.FC<HeadingWithAnchorProps> = ({
13+
children,
14+
id,
15+
...props
16+
}) => (
17+
<Heading id={id} role="group" display="flex" alignItems="center" {...props}>
18+
{children}
19+
{id && (
20+
<ChakraLink
21+
href={`#${id}`}
22+
aria-label="Link to this section"
23+
ml={2}
24+
opacity={0}
25+
_groupHover={{ opacity: 1 }}
26+
transition="opacity 0.1s"
27+
color="secondary"
28+
display="inline"
29+
>
30+
<LinkIcon boxSize="0.5em" />
31+
</ChakraLink>
32+
)}
33+
</Heading>
34+
)

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export * from './EventPreview'
1717
export * from './Fonts'
1818
export * from './Footer'
1919
export * from './Header'
20+
export * from './HeadingWithAnchor'
2021
export * from './Hero'
2122
export * from './UseCaseCard'
2223
export * from './Link'

src/pages/[event].tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import fs from 'fs'
22
import matter from 'gray-matter'
3+
import rehypeSlug from 'rehype-slug'
34
import gfm from 'remark-gfm'
45
import { ParsedUrlQuery } from 'querystring'
56
import ReactMarkdown from 'react-markdown'
@@ -145,6 +146,7 @@ const EventPage: React.FC<EventPost> = ({ frontmatter, content }) => {
145146
<ReactMarkdown
146147
components={EventMDStyles}
147148
remarkPlugins={[gfm]}
149+
rehypePlugins={[rehypeSlug]}
148150
skipHtml
149151
>
150152
{content}

src/styles/EventMDStyles.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { Heading } from '@chakra-ui/react'
1+
import { HeadingWithAnchor } from '@/components'
22
import { MDStyles } from '@/styles'
33

44
export const EventMDStyles = {
55
...MDStyles,
66
h2: ({ children, id }: any) => (
7-
<Heading
7+
<HeadingWithAnchor
88
as="h2"
99
id={id}
1010
textStyle="h2"
@@ -14,17 +14,17 @@ export const EventMDStyles = {
1414
textAlign="center"
1515
>
1616
{children}
17-
</Heading>
17+
</HeadingWithAnchor>
1818
),
1919
h3: ({ children, id }: any) => (
20-
<Heading
20+
<HeadingWithAnchor
2121
as="h3"
2222
id={id}
2323
textStyle="h3"
2424
color="text"
2525
my={{ base: 4, md: 6 }}
2626
>
2727
{children}
28-
</Heading>
28+
</HeadingWithAnchor>
2929
),
3030
}

src/styles/MDStyles.tsx

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
1-
import {
2-
Box,
3-
Divider,
4-
Flex,
5-
Heading,
6-
Image,
7-
Stack,
8-
Table,
9-
Text,
10-
} from '@chakra-ui/react'
11-
import { Code, Link } from '@/components'
1+
import { Box, Divider, Flex, Image, Stack, Table, Text } from '@chakra-ui/react'
2+
import { Code, HeadingWithAnchor, Link } from '@/components'
123
// TODO: Debug tables
134

145
export const MDStyles = {
@@ -23,7 +14,7 @@ export const MDStyles = {
2314
</Link>
2415
),
2516
h2: ({ children, id }: any) => (
26-
<Heading
17+
<HeadingWithAnchor
2718
as="h2"
2819
id={id}
2920
textStyle="h2"
@@ -33,10 +24,10 @@ export const MDStyles = {
3324
mb={{ base: 4, md: 6 }}
3425
>
3526
{children}
36-
</Heading>
27+
</HeadingWithAnchor>
3728
),
3829
h3: ({ children, id }: any) => (
39-
<Heading
30+
<HeadingWithAnchor
4031
as="h3"
4132
id={id}
4233
textStyle="h3"
@@ -46,10 +37,10 @@ export const MDStyles = {
4637
mb={{ base: 4, md: 6 }}
4738
>
4839
{children}
49-
</Heading>
40+
</HeadingWithAnchor>
5041
),
5142
h4: ({ children, id }: any) => (
52-
<Heading
43+
<HeadingWithAnchor
5344
as="h4"
5445
id={id}
5546
textStyle="h4"
@@ -59,31 +50,33 @@ export const MDStyles = {
5950
mb={{ base: 4, md: 6 }}
6051
>
6152
{children}
62-
</Heading>
53+
</HeadingWithAnchor>
6354
),
64-
h5: ({ children }: any) => (
65-
<Heading
55+
h5: ({ children, id }: any) => (
56+
<HeadingWithAnchor
6657
as="h5"
58+
id={id}
6759
textStyle="h5-mono"
6860
fontSize="xl"
6961
color="text"
7062
mt={{ base: 6, md: 8 }}
7163
mb={{ base: 2, md: 4 }}
7264
>
7365
{children}
74-
</Heading>
66+
</HeadingWithAnchor>
7567
),
76-
h6: ({ children }: any) => (
77-
<Heading
68+
h6: ({ children, id }: any) => (
69+
<HeadingWithAnchor
7870
as="h6"
71+
id={id}
7972
textStyle="h6-mono"
8073
fontSize="lg"
8174
color="text"
8275
mt={{ base: 4, md: 6 }}
8376
mb={{ base: 2, md: 4 }}
8477
>
8578
{children}
86-
</Heading>
79+
</HeadingWithAnchor>
8780
),
8881
pre: ({ children }: any) => (
8982
<Stack mb={5}>

yarn.lock

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,13 @@
649649
dependencies:
650650
"@types/unist" "^2"
651651

652+
"@types/hast@^3.0.0":
653+
version "3.0.4"
654+
resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa"
655+
integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==
656+
dependencies:
657+
"@types/unist" "*"
658+
652659
"@types/js-yaml@^4.0.5":
653660
version "4.0.9"
654661
resolved "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz"
@@ -760,6 +767,11 @@
760767
resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.26.0.tgz"
761768
integrity sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA==
762769

770+
"@types/unist@*", "@types/unist@^3.0.0":
771+
version "3.0.3"
772+
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c"
773+
integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==
774+
763775
"@types/unist@^2", "@types/unist@^2.0.0":
764776
version "2.0.11"
765777
resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz"
@@ -2123,6 +2135,11 @@ get-tsconfig@^4.10.0:
21232135
dependencies:
21242136
resolve-pkg-maps "^1.0.0"
21252137

2138+
github-slugger@^2.0.0:
2139+
version "2.0.0"
2140+
resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-2.0.0.tgz#52cf2f9279a21eb6c59dd385b410f0c0adda8f1a"
2141+
integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==
2142+
21262143
glob-parent@^5.1.2:
21272144
version "5.1.2"
21282145
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
@@ -2269,6 +2286,13 @@ hast-util-from-parse5@^7.0.0:
22692286
vfile-location "^4.0.0"
22702287
web-namespaces "^2.0.0"
22712288

2289+
hast-util-heading-rank@^3.0.0:
2290+
version "3.0.0"
2291+
resolved "https://registry.yarnpkg.com/hast-util-heading-rank/-/hast-util-heading-rank-3.0.0.tgz#2d5c6f2807a7af5c45f74e623498dd6054d2aba8"
2292+
integrity sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==
2293+
dependencies:
2294+
"@types/hast" "^3.0.0"
2295+
22722296
hast-util-parse-selector@^2.0.0:
22732297
version "2.2.5"
22742298
resolved "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz"
@@ -2334,6 +2358,13 @@ hast-util-to-string@^2.0.0:
23342358
dependencies:
23352359
"@types/hast" "^2.0.0"
23362360

2361+
hast-util-to-string@^3.0.0:
2362+
version "3.0.1"
2363+
resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz#a4f15e682849326dd211c97129c94b0c3e76527c"
2364+
integrity sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==
2365+
dependencies:
2366+
"@types/hast" "^3.0.0"
2367+
23372368
hast-util-whitespace@^2.0.0:
23382369
version "2.0.1"
23392370
resolved "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz"
@@ -3907,6 +3938,17 @@ rehype-prism-plus@1.6.3:
39073938
unist-util-filter "^4.0.0"
39083939
unist-util-visit "^4.0.0"
39093940

3941+
rehype-slug@^6.0.0:
3942+
version "6.0.0"
3943+
resolved "https://registry.yarnpkg.com/rehype-slug/-/rehype-slug-6.0.0.tgz#1d21cf7fc8a83ef874d873c15e6adaee6344eaf1"
3944+
integrity sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==
3945+
dependencies:
3946+
"@types/hast" "^3.0.0"
3947+
github-slugger "^2.0.0"
3948+
hast-util-heading-rank "^3.0.0"
3949+
hast-util-to-string "^3.0.0"
3950+
unist-util-visit "^5.0.0"
3951+
39103952
rehype-stringify@^9.0.0:
39113953
version "9.0.4"
39123954
resolved "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-9.0.4.tgz"
@@ -4520,6 +4562,13 @@ unist-util-is@^5.0.0:
45204562
dependencies:
45214563
"@types/unist" "^2.0.0"
45224564

4565+
unist-util-is@^6.0.0:
4566+
version "6.0.1"
4567+
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.1.tgz#d0a3f86f2dd0db7acd7d8c2478080b5c67f9c6a9"
4568+
integrity sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==
4569+
dependencies:
4570+
"@types/unist" "^3.0.0"
4571+
45234572
unist-util-position@^4.0.0:
45244573
version "4.0.4"
45254574
resolved "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz"
@@ -4542,6 +4591,14 @@ unist-util-visit-parents@^5.0.0, unist-util-visit-parents@^5.1.1:
45424591
"@types/unist" "^2.0.0"
45434592
unist-util-is "^5.0.0"
45444593

4594+
unist-util-visit-parents@^6.0.0:
4595+
version "6.0.2"
4596+
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz#777df7fb98652ce16b4b7cd999d0a1a40efa3a02"
4597+
integrity sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==
4598+
dependencies:
4599+
"@types/unist" "^3.0.0"
4600+
unist-util-is "^6.0.0"
4601+
45454602
unist-util-visit@^4.0.0:
45464603
version "4.1.2"
45474604
resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz"
@@ -4551,6 +4608,15 @@ unist-util-visit@^4.0.0:
45514608
unist-util-is "^5.0.0"
45524609
unist-util-visit-parents "^5.1.1"
45534610

4611+
unist-util-visit@^5.0.0:
4612+
version "5.1.0"
4613+
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.1.0.tgz#9a2a28b0aa76a15e0da70a08a5863a2f060e2468"
4614+
integrity sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==
4615+
dependencies:
4616+
"@types/unist" "^3.0.0"
4617+
unist-util-is "^6.0.0"
4618+
unist-util-visit-parents "^6.0.0"
4619+
45544620
unrs-resolver@^1.6.2:
45554621
version "1.11.1"
45564622
resolved "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz"

0 commit comments

Comments
 (0)