@docusaurus/theme-classic 3.7.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/lib/getSwizzleConfig.js +1 -1
  2. package/lib/index.js +9 -0
  3. package/lib/inlineScripts.js +6 -27
  4. package/lib/inlineSvgSprites.d.ts +7 -0
  5. package/lib/inlineSvgSprites.js +14 -0
  6. package/lib/options.js +5 -0
  7. package/lib/theme/AnnouncementBar/index.js +6 -2
  8. package/lib/theme/CodeBlock/{WordWrapButton → Buttons/Button}/index.d.ts +2 -2
  9. package/lib/theme/CodeBlock/Buttons/Button/index.js +13 -0
  10. package/lib/theme/CodeBlock/Buttons/CopyButton/index.d.ts +9 -0
  11. package/lib/theme/CodeBlock/{CopyButton → Buttons/CopyButton}/index.js +36 -25
  12. package/lib/theme/CodeBlock/Buttons/WordWrapButton/index.d.ts +9 -0
  13. package/lib/theme/CodeBlock/{WordWrapButton → Buttons/WordWrapButton}/index.js +12 -7
  14. package/lib/theme/CodeBlock/{CopyButton → Buttons}/index.d.ts +2 -2
  15. package/lib/theme/CodeBlock/Buttons/index.js +27 -0
  16. package/lib/theme/CodeBlock/Buttons/styles.module.css +37 -0
  17. package/lib/theme/CodeBlock/Content/Element.js +4 -2
  18. package/lib/theme/CodeBlock/Content/String.d.ts +1 -1
  19. package/lib/theme/CodeBlock/Content/String.js +22 -92
  20. package/lib/theme/CodeBlock/Content/index.d.ts +9 -0
  21. package/lib/theme/CodeBlock/Content/index.js +74 -0
  22. package/lib/theme/CodeBlock/Content/styles.module.css +0 -52
  23. package/lib/theme/CodeBlock/Layout/index.d.ts +9 -0
  24. package/lib/theme/CodeBlock/Layout/index.js +30 -0
  25. package/lib/theme/CodeBlock/Layout/styles.module.css +27 -0
  26. package/lib/theme/CodeBlock/Line/Token/index.d.ts +9 -0
  27. package/lib/theme/CodeBlock/Line/Token/index.js +11 -0
  28. package/lib/theme/CodeBlock/Line/index.d.ts +1 -1
  29. package/lib/theme/CodeBlock/Line/index.js +21 -7
  30. package/lib/theme/CodeBlock/Title/index.d.ts +9 -0
  31. package/lib/theme/CodeBlock/Title/index.js +10 -0
  32. package/lib/theme/ColorModeToggle/index.d.ts +1 -1
  33. package/lib/theme/ColorModeToggle/index.js +85 -27
  34. package/lib/theme/ColorModeToggle/styles.module.css +7 -2
  35. package/lib/theme/DocBreadcrumbs/StructuredData/index.d.ts +9 -0
  36. package/lib/theme/DocBreadcrumbs/StructuredData/index.js +21 -0
  37. package/lib/theme/DocBreadcrumbs/index.js +36 -54
  38. package/lib/theme/DocCard/index.js +6 -4
  39. package/lib/theme/DocCardList/index.js +12 -6
  40. package/lib/theme/DocCardList/styles.module.css +14 -0
  41. package/lib/theme/DocCategoryGeneratedIndexPage/index.js +1 -1
  42. package/lib/theme/DocCategoryGeneratedIndexPage/styles.module.css +0 -8
  43. package/lib/theme/DocItem/Paginator/index.js +7 -1
  44. package/lib/theme/DocPaginator/index.js +3 -2
  45. package/lib/theme/DocSidebarItem/Category/index.js +8 -1
  46. package/lib/theme/Footer/Layout/index.js +2 -1
  47. package/lib/theme/Footer/Links/MultiColumn/index.js +7 -1
  48. package/lib/theme/Icon/ExternalLink/index.js +4 -5
  49. package/lib/theme/Icon/SystemColorMode/index.d.ts +9 -0
  50. package/lib/theme/Icon/SystemColorMode/index.js +16 -0
  51. package/lib/theme/Layout/index.js +1 -0
  52. package/lib/theme/Navbar/ColorModeToggle/index.js +5 -4
  53. package/lib/theme/Navbar/Content/index.js +20 -3
  54. package/lib/theme/Navbar/Content/styles.module.css +8 -0
  55. package/lib/theme/Navbar/Layout/index.js +2 -1
  56. package/lib/theme/Navbar/MobileSidebar/Layout/index.js +35 -4
  57. package/lib/theme/NavbarItem/DefaultNavbarItem/Desktop/index.d.ts +9 -0
  58. package/lib/theme/NavbarItem/DefaultNavbarItem/Desktop/index.js +29 -0
  59. package/lib/theme/NavbarItem/DefaultNavbarItem/Mobile/index.d.ts +9 -0
  60. package/lib/theme/NavbarItem/DefaultNavbarItem/Mobile/index.js +20 -0
  61. package/lib/theme/NavbarItem/DefaultNavbarItem/index.js +25 -0
  62. package/lib/theme/NavbarItem/DocsVersionDropdownNavbarItem.d.ts +1 -1
  63. package/lib/theme/NavbarItem/DocsVersionDropdownNavbarItem.js +50 -7
  64. package/lib/theme/NavbarItem/DropdownNavbarItem/Desktop/index.d.ts +9 -0
  65. package/lib/theme/NavbarItem/DropdownNavbarItem/Desktop/index.js +74 -0
  66. package/lib/theme/NavbarItem/DropdownNavbarItem/Mobile/index.d.ts +10 -0
  67. package/lib/theme/NavbarItem/DropdownNavbarItem/Mobile/index.js +142 -0
  68. package/lib/theme/NavbarItem/DropdownNavbarItem/index.js +3 -142
  69. package/lib/theme/Tag/index.js +1 -0
  70. package/lib/theme/ThemeProvider/TitleFormatter/index.d.ts +9 -0
  71. package/lib/theme/ThemeProvider/TitleFormatter/index.js +19 -0
  72. package/lib/theme/ThemeProvider/index.d.ts +9 -0
  73. package/lib/theme/ThemeProvider/index.js +11 -0
  74. package/package.json +15 -15
  75. package/src/getSwizzleConfig.ts +1 -1
  76. package/src/index.ts +9 -0
  77. package/src/inlineScripts.ts +6 -27
  78. package/src/inlineSvgSprites.ts +12 -0
  79. package/src/options.ts +15 -0
  80. package/src/theme/AnnouncementBar/index.tsx +6 -2
  81. package/src/theme/CodeBlock/Buttons/Button/index.tsx +19 -0
  82. package/src/theme/CodeBlock/{CopyButton → Buttons/CopyButton}/index.tsx +42 -26
  83. package/src/theme/CodeBlock/{WordWrapButton → Buttons/WordWrapButton}/index.tsx +15 -12
  84. package/src/theme/CodeBlock/Buttons/index.tsx +32 -0
  85. package/src/theme/CodeBlock/Buttons/styles.module.css +37 -0
  86. package/src/theme/CodeBlock/Content/Element.tsx +4 -2
  87. package/src/theme/CodeBlock/Content/String.tsx +24 -101
  88. package/src/theme/CodeBlock/Content/index.tsx +84 -0
  89. package/src/theme/CodeBlock/Content/styles.module.css +0 -52
  90. package/src/theme/CodeBlock/Layout/index.tsx +34 -0
  91. package/src/theme/CodeBlock/Layout/styles.module.css +27 -0
  92. package/src/theme/CodeBlock/Line/Token/index.tsx +18 -0
  93. package/src/theme/CodeBlock/Line/index.tsx +26 -7
  94. package/src/theme/CodeBlock/Title/index.tsx +15 -0
  95. package/src/theme/ColorModeToggle/index.tsx +102 -32
  96. package/src/theme/ColorModeToggle/styles.module.css +7 -2
  97. package/src/theme/DocBreadcrumbs/StructuredData/index.tsx +24 -0
  98. package/src/theme/DocBreadcrumbs/index.tsx +35 -57
  99. package/src/theme/DocCard/index.tsx +8 -2
  100. package/src/theme/DocCardList/index.tsx +18 -7
  101. package/src/theme/DocCardList/styles.module.css +14 -0
  102. package/src/theme/DocCategoryGeneratedIndexPage/index.tsx +1 -1
  103. package/src/theme/DocCategoryGeneratedIndexPage/styles.module.css +0 -8
  104. package/src/theme/DocItem/Paginator/index.tsx +7 -1
  105. package/src/theme/DocPaginator/index.tsx +3 -2
  106. package/src/theme/DocSidebarItem/Category/index.tsx +8 -1
  107. package/src/theme/Footer/Layout/index.tsx +2 -1
  108. package/src/theme/Footer/Links/MultiColumn/index.tsx +7 -1
  109. package/src/theme/Icon/ExternalLink/index.tsx +5 -5
  110. package/src/theme/Icon/SystemColorMode/index.tsx +20 -0
  111. package/src/theme/Layout/index.tsx +1 -0
  112. package/src/theme/Navbar/ColorModeToggle/index.tsx +5 -4
  113. package/src/theme/Navbar/Content/index.tsx +20 -3
  114. package/src/theme/Navbar/Content/styles.module.css +8 -0
  115. package/src/theme/Navbar/Layout/index.tsx +2 -1
  116. package/src/theme/Navbar/MobileSidebar/Layout/index.tsx +43 -4
  117. package/src/theme/NavbarItem/DefaultNavbarItem/Desktop/index.tsx +34 -0
  118. package/src/theme/NavbarItem/DefaultNavbarItem/Mobile/index.tsx +23 -0
  119. package/src/theme/NavbarItem/DefaultNavbarItem/index.tsx +28 -0
  120. package/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +87 -9
  121. package/src/theme/NavbarItem/DropdownNavbarItem/Desktop/index.tsx +86 -0
  122. package/src/theme/NavbarItem/DropdownNavbarItem/Mobile/index.tsx +166 -0
  123. package/src/theme/NavbarItem/DropdownNavbarItem/index.tsx +4 -168
  124. package/src/theme/Tag/index.tsx +1 -0
  125. package/src/theme/ThemeProvider/TitleFormatter/index.tsx +27 -0
  126. package/src/theme/ThemeProvider/index.tsx +14 -0
  127. package/src/theme-classic.d.ts +175 -20
  128. package/lib/theme/NavbarItem/DefaultNavbarItem.js +0 -52
  129. package/src/theme/NavbarItem/DefaultNavbarItem.tsx +0 -66
  130. /package/lib/theme/CodeBlock/{CopyButton → Buttons/CopyButton}/styles.module.css +0 -0
  131. /package/lib/theme/CodeBlock/{WordWrapButton → Buttons/WordWrapButton}/styles.module.css +0 -0
  132. /package/lib/theme/NavbarItem/{DefaultNavbarItem.d.ts → DefaultNavbarItem/index.d.ts} +0 -0
  133. /package/lib/theme/NavbarItem/DropdownNavbarItem/{styles.module.css → Mobile/styles.module.css} +0 -0
  134. /package/src/theme/CodeBlock/{CopyButton → Buttons/CopyButton}/styles.module.css +0 -0
  135. /package/src/theme/CodeBlock/{WordWrapButton → Buttons/WordWrapButton}/styles.module.css +0 -0
  136. /package/src/theme/NavbarItem/DropdownNavbarItem/{styles.module.css → Mobile/styles.module.css} +0 -0
@@ -11,40 +11,105 @@ import useIsBrowser from '@docusaurus/useIsBrowser';
11
11
  import {translate} from '@docusaurus/Translate';
12
12
  import IconLightMode from '@theme/Icon/LightMode';
13
13
  import IconDarkMode from '@theme/Icon/DarkMode';
14
+ import IconSystemColorMode from '@theme/Icon/SystemColorMode';
14
15
  import type {Props} from '@theme/ColorModeToggle';
16
+ import type {ColorMode} from '@docusaurus/theme-common';
15
17
 
16
18
  import styles from './styles.module.css';
17
19
 
18
- function ColorModeToggle({
19
- className,
20
- buttonClassName,
21
- value,
22
- onChange,
23
- }: Props): ReactNode {
24
- const isBrowser = useIsBrowser();
20
+ // The order of color modes is defined here, and can be customized with swizzle
21
+ function getNextColorMode(
22
+ colorMode: ColorMode | null,
23
+ respectPrefersColorScheme: boolean,
24
+ ) {
25
+ // 2-value transition
26
+ if (!respectPrefersColorScheme) {
27
+ return colorMode === 'dark' ? 'light' : 'dark';
28
+ }
25
29
 
26
- const title = translate(
30
+ // 3-value transition
31
+ switch (colorMode) {
32
+ case null:
33
+ return 'light';
34
+ case 'light':
35
+ return 'dark';
36
+ case 'dark':
37
+ return null;
38
+ default:
39
+ throw new Error(`unexpected color mode ${colorMode}`);
40
+ }
41
+ }
42
+
43
+ function getColorModeLabel(colorMode: ColorMode | null): string {
44
+ switch (colorMode) {
45
+ case null:
46
+ return translate({
47
+ message: 'system mode',
48
+ id: 'theme.colorToggle.ariaLabel.mode.system',
49
+ description: 'The name for the system color mode',
50
+ });
51
+ case 'light':
52
+ return translate({
53
+ message: 'light mode',
54
+ id: 'theme.colorToggle.ariaLabel.mode.light',
55
+ description: 'The name for the light color mode',
56
+ });
57
+ case 'dark':
58
+ return translate({
59
+ message: 'dark mode',
60
+ id: 'theme.colorToggle.ariaLabel.mode.dark',
61
+ description: 'The name for the dark color mode',
62
+ });
63
+ default:
64
+ throw new Error(`unexpected color mode ${colorMode}`);
65
+ }
66
+ }
67
+
68
+ function getColorModeAriaLabel(colorMode: ColorMode | null) {
69
+ return translate(
27
70
  {
28
71
  message: 'Switch between dark and light mode (currently {mode})',
29
72
  id: 'theme.colorToggle.ariaLabel',
30
- description: 'The ARIA label for the navbar color mode toggle',
73
+ description: 'The ARIA label for the color mode toggle',
31
74
  },
32
75
  {
33
- mode:
34
- value === 'dark'
35
- ? translate({
36
- message: 'dark mode',
37
- id: 'theme.colorToggle.ariaLabel.mode.dark',
38
- description: 'The name for the dark color mode',
39
- })
40
- : translate({
41
- message: 'light mode',
42
- id: 'theme.colorToggle.ariaLabel.mode.light',
43
- description: 'The name for the light color mode',
44
- }),
76
+ mode: getColorModeLabel(colorMode),
45
77
  },
46
78
  );
79
+ }
47
80
 
81
+ function CurrentColorModeIcon(): ReactNode {
82
+ // 3 icons are always rendered for technical reasons
83
+ // We use "data-theme-choice" to render the correct one
84
+ // This must work even before React hydrates
85
+ return (
86
+ <>
87
+ <IconLightMode
88
+ // a18y is handled at the button level,
89
+ // not relying on button content (svg icons)
90
+ aria-hidden
91
+ className={clsx(styles.toggleIcon, styles.lightToggleIcon)}
92
+ />
93
+ <IconDarkMode
94
+ aria-hidden
95
+ className={clsx(styles.toggleIcon, styles.darkToggleIcon)}
96
+ />
97
+ <IconSystemColorMode
98
+ aria-hidden
99
+ className={clsx(styles.toggleIcon, styles.systemToggleIcon)}
100
+ />
101
+ </>
102
+ );
103
+ }
104
+
105
+ function ColorModeToggle({
106
+ className,
107
+ buttonClassName,
108
+ respectPrefersColorScheme,
109
+ value,
110
+ onChange,
111
+ }: Props): ReactNode {
112
+ const isBrowser = useIsBrowser();
48
113
  return (
49
114
  <div className={clsx(styles.toggle, className)}>
50
115
  <button
@@ -55,18 +120,23 @@ function ColorModeToggle({
55
120
  buttonClassName,
56
121
  )}
57
122
  type="button"
58
- onClick={() => onChange(value === 'dark' ? 'light' : 'dark')}
123
+ onClick={() =>
124
+ onChange(getNextColorMode(value, respectPrefersColorScheme))
125
+ }
59
126
  disabled={!isBrowser}
60
- title={title}
61
- aria-label={title}
62
- aria-live="polite"
63
- aria-pressed={value === 'dark' ? 'true' : 'false'}>
64
- <IconLightMode
65
- className={clsx(styles.toggleIcon, styles.lightToggleIcon)}
66
- />
67
- <IconDarkMode
68
- className={clsx(styles.toggleIcon, styles.darkToggleIcon)}
69
- />
127
+ title={getColorModeLabel(value)}
128
+ aria-label={getColorModeAriaLabel(value)}
129
+
130
+ // For accessibility decisions
131
+ // See https://github.com/facebook/docusaurus/issues/7667#issuecomment-2724401796
132
+
133
+ // aria-live disabled on purpose - This is annoying because:
134
+ // - without this attribute, VoiceOver doesn't announce on button enter
135
+ // - with this attribute, VoiceOver announces twice on ctrl+opt+space
136
+ // - with this attribute, NVDA announces many times
137
+ // aria-live="polite"
138
+ >
139
+ <CurrentColorModeIcon />
70
140
  </button>
71
141
  </div>
72
142
  );
@@ -25,11 +25,16 @@
25
25
  background: var(--ifm-color-emphasis-200);
26
26
  }
27
27
 
28
- [data-theme='light'] .darkToggleIcon,
29
- [data-theme='dark'] .lightToggleIcon {
28
+ .toggleIcon {
30
29
  display: none;
31
30
  }
32
31
 
32
+ [data-theme-choice='system'] .systemToggleIcon,
33
+ [data-theme-choice='light'] .lightToggleIcon,
34
+ [data-theme-choice='dark'] .darkToggleIcon {
35
+ display: initial;
36
+ }
37
+
33
38
  .toggleButtonDisabled {
34
39
  cursor: not-allowed;
35
40
  }
@@ -0,0 +1,24 @@
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 React, {type ReactNode} from 'react';
9
+ import Head from '@docusaurus/Head';
10
+ import {useBreadcrumbsStructuredData} from '@docusaurus/plugin-content-docs/client';
11
+ import type {Props} from '@theme/DocBreadcrumbs/StructuredData';
12
+
13
+ export default function DocBreadcrumbsStructuredData(props: Props): ReactNode {
14
+ const structuredData = useBreadcrumbsStructuredData({
15
+ breadcrumbs: props.breadcrumbs,
16
+ });
17
+ return (
18
+ <Head>
19
+ <script type="application/ld+json">
20
+ {JSON.stringify(structuredData)}
21
+ </script>
22
+ </Head>
23
+ );
24
+ }
@@ -13,6 +13,7 @@ import {useHomePageRoute} from '@docusaurus/theme-common/internal';
13
13
  import Link from '@docusaurus/Link';
14
14
  import {translate} from '@docusaurus/Translate';
15
15
  import HomeBreadcrumbItem from '@theme/DocBreadcrumbs/Items/Home';
16
+ import DocBreadcrumbsStructuredData from '@theme/DocBreadcrumbs/StructuredData';
16
17
 
17
18
  import styles from './styles.module.css';
18
19
 
@@ -28,22 +29,13 @@ function BreadcrumbsItemLink({
28
29
  }): ReactNode {
29
30
  const className = 'breadcrumbs__link';
30
31
  if (isLast) {
31
- return (
32
- <span className={className} itemProp="name">
33
- {children}
34
- </span>
35
- );
32
+ return <span className={className}>{children}</span>;
36
33
  }
37
34
  return href ? (
38
- <Link className={className} href={href} itemProp="item">
39
- <span itemProp="name">{children}</span>
35
+ <Link className={className} href={href}>
36
+ <span>{children}</span>
40
37
  </Link>
41
38
  ) : (
42
- // TODO Google search console doesn't like breadcrumb items without href.
43
- // The schema doesn't seem to require `id` for each `item`, although Google
44
- // insist to infer one, even if it's invalid. Removing `itemProp="item
45
- // name"` for now, since I don't know how to properly fix it.
46
- // See https://github.com/facebook/docusaurus/issues/7241
47
39
  <span className={className}>{children}</span>
48
40
  );
49
41
  }
@@ -52,26 +44,16 @@ function BreadcrumbsItemLink({
52
44
  function BreadcrumbsItem({
53
45
  children,
54
46
  active,
55
- index,
56
- addMicrodata,
57
47
  }: {
58
48
  children: ReactNode;
59
49
  active?: boolean;
60
- index: number;
61
- addMicrodata: boolean;
62
50
  }): ReactNode {
63
51
  return (
64
52
  <li
65
- {...(addMicrodata && {
66
- itemScope: true,
67
- itemProp: 'itemListElement',
68
- itemType: 'https://schema.org/ListItem',
69
- })}
70
53
  className={clsx('breadcrumbs__item', {
71
54
  'breadcrumbs__item--active': active,
72
55
  })}>
73
56
  {children}
74
- <meta itemProp="position" content={String(index + 1)} />
75
57
  </li>
76
58
  );
77
59
  }
@@ -85,40 +67,36 @@ export default function DocBreadcrumbs(): ReactNode {
85
67
  }
86
68
 
87
69
  return (
88
- <nav
89
- className={clsx(
90
- ThemeClassNames.docs.docBreadcrumbs,
91
- styles.breadcrumbsContainer,
92
- )}
93
- aria-label={translate({
94
- id: 'theme.docs.breadcrumbs.navAriaLabel',
95
- message: 'Breadcrumbs',
96
- description: 'The ARIA label for the breadcrumbs',
97
- })}>
98
- <ul
99
- className="breadcrumbs"
100
- itemScope
101
- itemType="https://schema.org/BreadcrumbList">
102
- {homePageRoute && <HomeBreadcrumbItem />}
103
- {breadcrumbs.map((item, idx) => {
104
- const isLast = idx === breadcrumbs.length - 1;
105
- const href =
106
- item.type === 'category' && item.linkUnlisted
107
- ? undefined
108
- : item.href;
109
- return (
110
- <BreadcrumbsItem
111
- key={idx}
112
- active={isLast}
113
- index={idx}
114
- addMicrodata={!!href}>
115
- <BreadcrumbsItemLink href={href} isLast={isLast}>
116
- {item.label}
117
- </BreadcrumbsItemLink>
118
- </BreadcrumbsItem>
119
- );
120
- })}
121
- </ul>
122
- </nav>
70
+ <>
71
+ <DocBreadcrumbsStructuredData breadcrumbs={breadcrumbs} />
72
+ <nav
73
+ className={clsx(
74
+ ThemeClassNames.docs.docBreadcrumbs,
75
+ styles.breadcrumbsContainer,
76
+ )}
77
+ aria-label={translate({
78
+ id: 'theme.docs.breadcrumbs.navAriaLabel',
79
+ message: 'Breadcrumbs',
80
+ description: 'The ARIA label for the breadcrumbs',
81
+ })}>
82
+ <ul className="breadcrumbs">
83
+ {homePageRoute && <HomeBreadcrumbItem />}
84
+ {breadcrumbs.map((item, idx) => {
85
+ const isLast = idx === breadcrumbs.length - 1;
86
+ const href =
87
+ item.type === 'category' && item.linkUnlisted
88
+ ? undefined
89
+ : item.href;
90
+ return (
91
+ <BreadcrumbsItem key={idx} active={isLast}>
92
+ <BreadcrumbsItemLink href={href} isLast={isLast}>
93
+ {item.label}
94
+ </BreadcrumbsItemLink>
95
+ </BreadcrumbsItem>
96
+ );
97
+ })}
98
+ </ul>
99
+ </nav>
100
+ </>
123
101
  );
124
102
  }
@@ -43,34 +43,38 @@ function useCategoryItemsPlural() {
43
43
  }
44
44
 
45
45
  function CardContainer({
46
+ className,
46
47
  href,
47
48
  children,
48
49
  }: {
50
+ className?: string;
49
51
  href: string;
50
52
  children: ReactNode;
51
53
  }): ReactNode {
52
54
  return (
53
55
  <Link
54
56
  href={href}
55
- className={clsx('card padding--lg', styles.cardContainer)}>
57
+ className={clsx('card padding--lg', styles.cardContainer, className)}>
56
58
  {children}
57
59
  </Link>
58
60
  );
59
61
  }
60
62
 
61
63
  function CardLayout({
64
+ className,
62
65
  href,
63
66
  icon,
64
67
  title,
65
68
  description,
66
69
  }: {
70
+ className?: string;
67
71
  href: string;
68
72
  icon: ReactNode;
69
73
  title: string;
70
74
  description?: string;
71
75
  }): ReactNode {
72
76
  return (
73
- <CardContainer href={href}>
77
+ <CardContainer href={href} className={className}>
74
78
  <Heading
75
79
  as="h2"
76
80
  className={clsx('text--truncate', styles.cardTitle)}
@@ -99,6 +103,7 @@ function CardCategory({item}: {item: PropSidebarItemCategory}): ReactNode {
99
103
 
100
104
  return (
101
105
  <CardLayout
106
+ className={item.className}
102
107
  href={href}
103
108
  icon="🗃️"
104
109
  title={item.label}
@@ -112,6 +117,7 @@ function CardLink({item}: {item: PropSidebarItemLink}): ReactNode {
112
117
  const doc = useDocById(item.docId ?? undefined);
113
118
  return (
114
119
  <CardLayout
120
+ className={item.className}
115
121
  href={item.href}
116
122
  icon={icon}
117
123
  title={item.label}
@@ -5,18 +5,31 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import React, {type ReactNode} from 'react';
8
+ import React, {type ComponentProps, type ReactNode} from 'react';
9
9
  import clsx from 'clsx';
10
10
  import {
11
- useCurrentSidebarCategory,
11
+ useCurrentSidebarSiblings,
12
12
  filterDocCardListItems,
13
13
  } from '@docusaurus/plugin-content-docs/client';
14
14
  import DocCard from '@theme/DocCard';
15
15
  import type {Props} from '@theme/DocCardList';
16
+ import styles from './styles.module.css';
16
17
 
17
18
  function DocCardListForCurrentSidebarCategory({className}: Props) {
18
- const category = useCurrentSidebarCategory();
19
- return <DocCardList items={category.items} className={className} />;
19
+ const items = useCurrentSidebarSiblings();
20
+ return <DocCardList items={items} className={className} />;
21
+ }
22
+
23
+ function DocCardListItem({
24
+ item,
25
+ }: {
26
+ item: ComponentProps<typeof DocCard>['item'];
27
+ }) {
28
+ return (
29
+ <article className={clsx(styles.docCardListItem, 'col col--6')}>
30
+ <DocCard item={item} />
31
+ </article>
32
+ );
20
33
  }
21
34
 
22
35
  export default function DocCardList(props: Props): ReactNode {
@@ -28,9 +41,7 @@ export default function DocCardList(props: Props): ReactNode {
28
41
  return (
29
42
  <section className={clsx('row', className)}>
30
43
  {filteredItems.map((item, index) => (
31
- <article key={index} className="col col--6 margin-bottom--lg">
32
- <DocCard item={item} />
33
- </article>
44
+ <DocCardListItem key={index} item={item} />
34
45
  ))}
35
46
  </section>
36
47
  );
@@ -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
+ .docCardListItem {
9
+ margin-bottom: 2rem;
10
+ }
11
+
12
+ .docCardListItem > * {
13
+ height: 100%;
14
+ }
@@ -53,7 +53,7 @@ function DocCategoryGeneratedIndexPageContent({
53
53
  <article className="margin-top--lg">
54
54
  <DocCardList items={category.items} className={styles.list} />
55
55
  </article>
56
- <footer className="margin-top--lg">
56
+ <footer className="margin-top--md">
57
57
  <DocPaginator
58
58
  previous={categoryGeneratedIndex.navigation.previous}
59
59
  next={categoryGeneratedIndex.navigation.next}
@@ -9,10 +9,6 @@
9
9
  .generatedIndexPage {
10
10
  max-width: 75% !important;
11
11
  }
12
-
13
- .list article:nth-last-child(-n + 2) {
14
- margin-bottom: 0 !important;
15
- }
16
12
  }
17
13
 
18
14
  /* Duplicated from .markdown h1 */
@@ -20,7 +16,3 @@
20
16
  --ifm-h1-font-size: 3rem;
21
17
  margin-bottom: calc(1.25 * var(--ifm-leading));
22
18
  }
23
-
24
- .list article:last-child {
25
- margin-bottom: 0 !important;
26
- }
@@ -15,5 +15,11 @@ import DocPaginator from '@theme/DocPaginator';
15
15
  */
16
16
  export default function DocItemPaginator(): ReactNode {
17
17
  const {metadata} = useDoc();
18
- return <DocPaginator previous={metadata.previous} next={metadata.next} />;
18
+ return (
19
+ <DocPaginator
20
+ className="docusaurus-mt-lg"
21
+ previous={metadata.previous}
22
+ next={metadata.next}
23
+ />
24
+ );
19
25
  }
@@ -6,15 +6,16 @@
6
6
  */
7
7
 
8
8
  import React, {type ReactNode} from 'react';
9
+ import clsx from 'clsx';
9
10
  import Translate, {translate} from '@docusaurus/Translate';
10
11
  import PaginatorNavLink from '@theme/PaginatorNavLink';
11
12
  import type {Props} from '@theme/DocPaginator';
12
13
 
13
14
  export default function DocPaginator(props: Props): ReactNode {
14
- const {previous, next} = props;
15
+ const {className, previous, next} = props;
15
16
  return (
16
17
  <nav
17
- className="pagination-nav docusaurus-mt-lg"
18
+ className={clsx(className, 'pagination-nav')}
18
19
  aria-label={translate({
19
20
  id: 'theme.docs.paginator.navAriaLabel',
20
21
  message: 'Docs pages',
@@ -188,7 +188,14 @@ export default function DocSidebarItemCategory({
188
188
  ? (e) => {
189
189
  onItemClick?.(item);
190
190
  if (href) {
191
- updateCollapsed(false);
191
+ if (isActive) {
192
+ e.preventDefault();
193
+ updateCollapsed();
194
+ } else {
195
+ // When navigating to a new category, we always expand
196
+ // see https://github.com/facebook/docusaurus/issues/10854#issuecomment-2609616182
197
+ updateCollapsed(false);
198
+ }
192
199
  } else {
193
200
  e.preventDefault();
194
201
  updateCollapsed();
@@ -7,6 +7,7 @@
7
7
 
8
8
  import React, {type ReactNode} from 'react';
9
9
  import clsx from 'clsx';
10
+ import {ThemeClassNames} from '@docusaurus/theme-common';
10
11
  import type {Props} from '@theme/Footer/Layout';
11
12
 
12
13
  export default function FooterLayout({
@@ -17,7 +18,7 @@ export default function FooterLayout({
17
18
  }: Props): ReactNode {
18
19
  return (
19
20
  <footer
20
- className={clsx('footer', {
21
+ className={clsx(ThemeClassNames.layout.footer.container, 'footer', {
21
22
  'footer--dark': style === 'dark',
22
23
  })}>
23
24
  <div className="container container-fluid">
@@ -7,6 +7,7 @@
7
7
 
8
8
  import React, {type ReactNode} from 'react';
9
9
  import clsx from 'clsx';
10
+ import {ThemeClassNames} from '@docusaurus/theme-common';
10
11
  import LinkItem from '@theme/Footer/LinkItem';
11
12
  import type {Props} from '@theme/Footer/Links/MultiColumn';
12
13
 
@@ -30,7 +31,12 @@ function ColumnLinkItem({item}: {item: ColumnItemType}) {
30
31
 
31
32
  function Column({column}: {column: ColumnType}) {
32
33
  return (
33
- <div className={clsx('col footer__col', column.className)}>
34
+ <div
35
+ className={clsx(
36
+ ThemeClassNames.layout.footer.column,
37
+ 'col footer__col',
38
+ column.className,
39
+ )}>
34
40
  <div className="footer__title">{column.title}</div>
35
41
  <ul className="footer__items clean-list">
36
42
  {column.items.map((item, i) => (
@@ -10,6 +10,10 @@ import type {Props} from '@theme/Icon/ExternalLink';
10
10
 
11
11
  import styles from './styles.module.css';
12
12
 
13
+ // References symbol in docusaurus-theme-classic/src/inlineSvgSprites.ts
14
+ // See why: https://github.com/facebook/docusaurus/issues/5865
15
+ const svgSprite = '#theme-svg-external-link';
16
+
13
17
  export default function IconExternalLink({
14
18
  width = 13.5,
15
19
  height = 13.5,
@@ -19,12 +23,8 @@ export default function IconExternalLink({
19
23
  width={width}
20
24
  height={height}
21
25
  aria-hidden="true"
22
- viewBox="0 0 24 24"
23
26
  className={styles.iconExternalLink}>
24
- <path
25
- fill="currentColor"
26
- d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"
27
- />
27
+ <use href={svgSprite} />
28
28
  </svg>
29
29
  );
30
30
  }
@@ -0,0 +1,20 @@
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 type {ReactNode} from 'react';
9
+ import type {Props} from '@theme/Icon/SystemColorMode';
10
+
11
+ export default function IconSystemColorMode(props: Props): ReactNode {
12
+ return (
13
+ <svg viewBox="0 0 24 24" width={24} height={24} {...props}>
14
+ <path
15
+ fill="currentColor"
16
+ d="m12 21c4.971 0 9-4.029 9-9s-4.029-9-9-9-9 4.029-9 9 4.029 9 9 9zm4.95-13.95c1.313 1.313 2.05 3.093 2.05 4.95s-0.738 3.637-2.05 4.95c-1.313 1.313-3.093 2.05-4.95 2.05v-14c1.857 0 3.637 0.737 4.95 2.05z"
17
+ />
18
+ </svg>
19
+ );
20
+ }
@@ -48,6 +48,7 @@ export default function Layout(props: Props): ReactNode {
48
48
  <div
49
49
  id={SkipToContentFallbackId}
50
50
  className={clsx(
51
+ ThemeClassNames.layout.main.container,
51
52
  ThemeClassNames.wrapper.main,
52
53
  styles.mainWrapper,
53
54
  wrapperClassName,
@@ -13,10 +13,10 @@ import styles from './styles.module.css';
13
13
 
14
14
  export default function NavbarColorModeToggle({className}: Props): ReactNode {
15
15
  const navbarStyle = useThemeConfig().navbar.style;
16
- const disabled = useThemeConfig().colorMode.disableSwitch;
17
- const {colorMode, setColorMode} = useColorMode();
16
+ const {disableSwitch, respectPrefersColorScheme} = useThemeConfig().colorMode;
17
+ const {colorModeChoice, setColorMode} = useColorMode();
18
18
 
19
- if (disabled) {
19
+ if (disableSwitch) {
20
20
  return null;
21
21
  }
22
22
 
@@ -26,7 +26,8 @@ export default function NavbarColorModeToggle({className}: Props): ReactNode {
26
26
  buttonClassName={
27
27
  navbarStyle === 'dark' ? styles.darkNavbarColorModeToggle : undefined
28
28
  }
29
- value={colorMode}
29
+ respectPrefersColorScheme={respectPrefersColorScheme}
30
+ value={colorModeChoice}
30
31
  onChange={setColorMode}
31
32
  />
32
33
  );