Skip to content

Commit ac630f8

Browse files
authored
fix(theme): Fix CSS scroll-margin-top when clicking footnote items, factorize code (#11466)
1 parent 598af3b commit ac630f8

File tree

9 files changed

+209
-73
lines changed

9 files changed

+209
-73
lines changed

packages/docusaurus-theme-classic/src/theme/Heading/index.tsx

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,16 @@
88
import React, {type ReactNode} from 'react';
99
import clsx from 'clsx';
1010
import {translate} from '@docusaurus/Translate';
11-
import {useThemeConfig} from '@docusaurus/theme-common';
11+
import {useAnchorTargetClassName} from '@docusaurus/theme-common';
1212
import Link from '@docusaurus/Link';
1313
import useBrokenLinks from '@docusaurus/useBrokenLinks';
1414
import type {Props} from '@theme/Heading';
15-
16-
import styles from './styles.module.css';
15+
import './styles.module.css';
1716

1817
export default function Heading({as: As, id, ...props}: Props): ReactNode {
1918
const brokenLinks = useBrokenLinks();
20-
const {
21-
navbar: {hideOnScroll},
22-
} = useThemeConfig();
19+
const anchorTargetClassName = useAnchorTargetClassName(id);
20+
2321
// H1 headings do not need an id because they don't appear in the TOC.
2422
if (As === 'h1' || !id) {
2523
return <As {...props} id={undefined} />;
@@ -41,13 +39,7 @@ export default function Heading({as: As, id, ...props}: Props): ReactNode {
4139
return (
4240
<As
4341
{...props}
44-
className={clsx(
45-
'anchor',
46-
hideOnScroll
47-
? styles.anchorWithHideOnScrollNavbar
48-
: styles.anchorWithStickyNavbar,
49-
props.className,
50-
)}
42+
className={clsx('anchor', anchorTargetClassName, props.className)}
5143
id={id}>
5244
{props.children}
5345
<Link

packages/docusaurus-theme-classic/src/theme/Heading/styles.module.css

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,6 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
/*
9-
When the navbar is sticky, ensure that on anchor click,
10-
the browser does not scroll that anchor behind the navbar
11-
See https://x.com/JoshWComeau/status/1332015868725891076
12-
*/
13-
.anchorWithStickyNavbar {
14-
scroll-margin-top: calc(var(--ifm-navbar-height) + 0.5rem);
15-
}
16-
17-
.anchorWithHideOnScrollNavbar {
18-
scroll-margin-top: 0.5rem;
19-
}
20-
218
:global(.hash-link) {
229
opacity: 0;
2310
padding-left: 0.5rem;

packages/docusaurus-theme-classic/src/theme/MDXComponents/A/index.tsx

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,14 @@
88
import React, {type ReactNode} from 'react';
99
import clsx from 'clsx';
1010
import Link from '@docusaurus/Link';
11-
import {useThemeConfig} from '@docusaurus/theme-common';
11+
import {useAnchorTargetClassName} from '@docusaurus/theme-common';
1212
import type {Props} from '@theme/MDXComponents/A';
13-
import styles from './styles.module.css';
1413

15-
function isFootnoteRef(props: Props) {
16-
return props['data-footnote-ref'] === true;
17-
}
14+
export default function MDXA(props: Props): ReactNode {
15+
// MDX Footnotes have ids such as <a id="user-content-fn-1-953011" ...>
16+
const anchorTargetClassName = useAnchorTargetClassName(props.id);
1817

19-
function FootnoteRefLink(props: Props) {
20-
const {
21-
navbar: {hideOnScroll},
22-
} = useThemeConfig();
2318
return (
24-
<Link
25-
{...props}
26-
className={clsx(
27-
hideOnScroll
28-
? styles.footnoteRefHideOnScrollNavbar
29-
: styles.footnoteRefStickyNavbar,
30-
31-
props.className,
32-
)}
33-
/>
19+
<Link {...props} className={clsx(anchorTargetClassName, props.className)} />
3420
);
3521
}
36-
37-
export default function MDXA(props: Props): ReactNode {
38-
if (isFootnoteRef(props)) {
39-
return <FootnoteRefLink {...props} />;
40-
}
41-
return <Link {...props} />;
42-
}

packages/docusaurus-theme-classic/src/theme/MDXComponents/A/styles.module.css

Lines changed: 0 additions & 20 deletions
This file was deleted.

packages/docusaurus-theme-classic/src/theme/MDXComponents/Li.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@
66
*/
77

88
import React, {type ReactNode} from 'react';
9+
import clsx from 'clsx';
910
import useBrokenLinks from '@docusaurus/useBrokenLinks';
11+
import {useAnchorTargetClassName} from '@docusaurus/theme-common';
1012
import type {Props} from '@theme/MDXComponents/Li';
1113

1214
export default function MDXLi(props: Props): ReactNode | undefined {
1315
// MDX Footnotes have ids such as <li id="user-content-fn-1-953011">
1416
useBrokenLinks().collectAnchor(props.id);
17+
const anchorTargetClassName = useAnchorTargetClassName(props.id);
1518

16-
return <li {...props} />;
19+
return (
20+
<li className={clsx(anchorTargetClassName, props.className)} {...props} />
21+
);
1722
}

packages/docusaurus-theme-common/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ export {
109109
useSearchLinkCreator,
110110
} from './hooks/useSearchPage';
111111

112+
export {useAnchorTargetClassName} from './utils/anchorUtils';
113+
112114
export {isMultiColumnFooterLinks} from './utils/footerUtils';
113115

114116
export {isRegexpStringMatch} from './utils/regexpUtils';
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
.anchorTargetStickyNavbar {
9+
scroll-margin-top: calc(var(--ifm-navbar-height) + 0.5rem);
10+
}
11+
12+
.anchorTargetHideOnScrollNavbar {
13+
scroll-margin-top: 0.5rem;
14+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import {useThemeConfig} from './useThemeConfig';
9+
import styles from './anchorUtils.module.css';
10+
11+
/**
12+
* When the navbar is sticky, this ensures that when clicking a hash link,
13+
* we do not navigate to an anchor that will appear below the navbar.
14+
* This happens in particular for MDX headings and footnotes.
15+
*
16+
* See https://github.com/facebook/docusaurus/issues/11232
17+
* See also headings case https://x.com/JoshWComeau/status/1332015868725891076
18+
*/
19+
export function useAnchorTargetClassName(
20+
id: string | undefined,
21+
): string | undefined {
22+
const {
23+
navbar: {hideOnScroll},
24+
} = useThemeConfig();
25+
if (typeof id === 'undefined') {
26+
return undefined;
27+
}
28+
return hideOnScroll
29+
? styles.anchorTargetHideOnScrollNavbar
30+
: styles.anchorTargetStickyNavbar;
31+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# Footnotes
2+
3+
Lorem ipsum dolor sit amet [^1] [^2]
4+
5+
Lorem ipsum dolor sit amet [^3]
6+
7+
Lorem ipsum dolor sit amet [^4]
8+
9+
Lorem ipsum dolor sit amet [^5]
10+
11+
Lorem ipsum dolor sit amet [^6]
12+
13+
Lorem ipsum dolor sit amet [^7]
14+
15+
Lorem ipsum dolor sit amet [^8]
16+
17+
Lorem ipsum dolor sit amet [^9]
18+
19+
Lorem ipsum dolor sit amet [^10]
20+
21+
Lorem ipsum dolor sit amet [^11]
22+
23+
Lorem ipsum dolor sit amet [^12]
24+
25+
Lorem ipsum dolor sit amet [^13]
26+
27+
Lorem ipsum dolor sit amet [^14]
28+
29+
Lorem ipsum dolor sit amet [^15]
30+
31+
Lorem ipsum dolor sit amet [^16]
32+
33+
Lorem ipsum dolor sit amet [^17]
34+
35+
Lorem ipsum dolor sit amet [^18]
36+
37+
Lorem ipsum dolor sit amet [^19]
38+
39+
Lorem ipsum dolor sit amet [^20]
40+
41+
Lorem ipsum dolor sit amet [^21] [^22]
42+
43+
Lorem ipsum dolor sit amet [^23]
44+
45+
Lorem ipsum dolor sit amet [^24]
46+
47+
Lorem ipsum dolor sit amet [^25]
48+
49+
Lorem ipsum dolor sit amet [^26]
50+
51+
Lorem ipsum dolor sit amet [^27]
52+
53+
Lorem ipsum dolor sit amet [^28]
54+
55+
Lorem ipsum dolor sit amet [^29]
56+
57+
Lorem ipsum dolor sit amet [^30]
58+
59+
Lorem ipsum dolor sit amet [^31]
60+
61+
Lorem ipsum dolor sit amet [^32]
62+
63+
Lorem ipsum dolor sit amet [^33]
64+
65+
Lorem ipsum dolor sit amet [^34]
66+
67+
Lorem ipsum dolor sit amet [^35]
68+
69+
Lorem ipsum dolor sit amet [^36]
70+
71+
Lorem ipsum dolor sit amet [^37]
72+
73+
Lorem ipsum dolor sit amet [^38]
74+
75+
Lorem ipsum dolor sit amet [^39]
76+
77+
Lorem ipsum dolor sit amet [^40]
78+
79+
Lorem ipsum dolor sit amet [^41]
80+
81+
Lorem ipsum dolor sit amet [^42]
82+
83+
Lorem ipsum dolor sit amet [^43]
84+
85+
Lorem ipsum dolor sit amet [^44]
86+
87+
Lorem ipsum dolor sit amet [^45]
88+
89+
Lorem ipsum dolor sit amet [^46]
90+
91+
Lorem ipsum dolor sit amet [^47]
92+
93+
Lorem ipsum dolor sit amet [^48]
94+
95+
Lorem ipsum dolor sit amet [^49] [^50]
96+
97+
[^1]: footnote 1
98+
[^2]: footnote 2
99+
[^3]: footnote 3
100+
[^4]: footnote 4
101+
[^5]: footnote 5
102+
[^6]: footnote 6
103+
[^7]: footnote 7
104+
[^8]: footnote 8
105+
[^9]: footnote 9
106+
[^10]: footnote 10
107+
[^11]: footnote 11
108+
[^12]: footnote 12
109+
[^13]: footnote 13
110+
[^14]: footnote 14
111+
[^15]: footnote 15
112+
[^16]: footnote 16
113+
[^17]: footnote 17
114+
[^18]: footnote 18
115+
[^19]: footnote 19
116+
[^20]: footnote 20
117+
[^21]: footnote 21
118+
[^22]: footnote 22
119+
[^23]: footnote 23
120+
[^24]: footnote 24
121+
[^25]: footnote 25
122+
[^26]: footnote 26
123+
[^27]: footnote 27
124+
[^28]: footnote 28
125+
[^29]: footnote 29
126+
[^30]: footnote 30
127+
[^31]: footnote 31
128+
[^32]: footnote 32
129+
[^33]: footnote 33
130+
[^34]: footnote 34
131+
[^35]: footnote 35
132+
[^36]: footnote 36
133+
[^37]: footnote 37
134+
[^38]: footnote 38
135+
[^39]: footnote 39
136+
[^40]: footnote 40
137+
[^41]: footnote 41
138+
[^42]: footnote 42
139+
[^43]: footnote 43
140+
[^44]: footnote 44
141+
[^45]: footnote 45
142+
[^46]: footnote 46
143+
[^47]: footnote 47
144+
[^48]: footnote 48
145+
[^49]: footnote 49
146+
[^50]: footnote 50

0 commit comments

Comments
 (0)