Skip to content

Commit 44f3869

Browse files
authored
docs: explain swizzling DocCard (#1207)
1 parent 23db916 commit 44f3869

File tree

1 file changed

+165
-0
lines changed

1 file changed

+165
-0
lines changed

demo/docs/customization/doccard.mdx

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
---
2+
id: doccard
3+
hide_title: true
4+
sidebar_label: DocCard Markdown
5+
title: DocCard Markdown
6+
description: Swizzle the DocCard component to render Markdown in card descriptions.
7+
---
8+
9+
## Overview
10+
11+
The default DocCard component used by Docusaurus displays the `description` field
12+
as plain text. Any Markdown inside the description is escaped rather than
13+
rendered. To support Markdown, you can swizzle the component and update it to use
14+
`@theme/Markdown`.
15+
16+
## Swizzling the component
17+
18+
Run the swizzle command from your site directory and choose the **eject** option
19+
when prompted:
20+
21+
```bash
22+
npx docusaurus swizzle @docusaurus/theme-classic DocCard
23+
```
24+
25+
This will create `src/theme/DocCard/index.tsx` in your project. Replace its
26+
contents with the following:
27+
28+
```tsx title="src/theme/DocCard/index.tsx"
29+
import React, { type ReactNode } from "react";
30+
31+
import isInternalUrl from "@docusaurus/isInternalUrl";
32+
import Link from "@docusaurus/Link";
33+
import type {
34+
PropSidebarItemCategory,
35+
PropSidebarItemLink,
36+
} from "@docusaurus/plugin-content-docs";
37+
import {
38+
useDocById,
39+
findFirstSidebarItemLink,
40+
} from "@docusaurus/plugin-content-docs/lib/client/docsUtils";
41+
import { usePluralForm } from "@docusaurus/theme-common";
42+
import { translate } from "@docusaurus/Translate";
43+
import type { Props } from "@theme/DocCard";
44+
import Heading from "@theme/Heading";
45+
import Markdown from "@theme/Markdown";
46+
import clsx from "clsx";
47+
48+
import styles from "./styles.module.css";
49+
50+
function useCategoryItemsPlural() {
51+
const { selectMessage } = usePluralForm();
52+
return (count: number) =>
53+
selectMessage(
54+
count,
55+
translate(
56+
{
57+
message: "1 item|{count} items",
58+
id: "theme.docs.DocCard.categoryDescription.plurals",
59+
description:
60+
"The default description for a category card in the generated index about how many items this category includes",
61+
},
62+
{ count }
63+
)
64+
);
65+
}
66+
67+
function CardContainer({
68+
className,
69+
href,
70+
children,
71+
}: {
72+
className?: string;
73+
href: string;
74+
children: ReactNode;
75+
}): ReactNode {
76+
return (
77+
<Link
78+
href={href}
79+
className={clsx("card padding--lg", styles.cardContainer, className)}
80+
>
81+
{children}
82+
</Link>
83+
);
84+
}
85+
86+
function CardLayout({
87+
className,
88+
href,
89+
icon,
90+
title,
91+
description,
92+
}: {
93+
className?: string;
94+
href: string;
95+
icon: ReactNode;
96+
title: string;
97+
description?: string;
98+
}): ReactNode {
99+
return (
100+
<CardContainer href={href} className={className}>
101+
<Heading
102+
as="h2"
103+
className={clsx("text--truncate", styles.cardTitle)}
104+
title={title}
105+
>
106+
{icon} {title}
107+
</Heading>
108+
{description && (
109+
<Markdown
110+
className={clsx("text--truncate", styles.cardDescription)}
111+
title={description}
112+
>
113+
{description}
114+
</Markdown>
115+
)}
116+
</CardContainer>
117+
);
118+
}
119+
120+
function CardCategory({ item }: { item: PropSidebarItemCategory }): ReactNode {
121+
const href = findFirstSidebarItemLink(item);
122+
const categoryItemsPlural = useCategoryItemsPlural();
123+
124+
// Unexpected: categories that don't have a link have been filtered upfront
125+
if (!href) {
126+
return null;
127+
}
128+
129+
return (
130+
<CardLayout
131+
className={item.className}
132+
href={href}
133+
icon="🗃️"
134+
title={item.label}
135+
description={item.description ?? categoryItemsPlural(item.items.length)}
136+
/>
137+
);
138+
}
139+
140+
function CardLink({ item }: { item: PropSidebarItemLink }): ReactNode {
141+
const icon = isInternalUrl(item.href) ? "📄️" : "🔗";
142+
const doc = useDocById(item.docId ?? undefined);
143+
return (
144+
<CardLayout
145+
className={item.className}
146+
href={item.href}
147+
icon={icon}
148+
title={item.label}
149+
description={item.description ?? doc?.description}
150+
/>
151+
);
152+
}
153+
154+
export default function DocCard({ item }: Props): ReactNode {
155+
switch (item.type) {
156+
case "link":
157+
return <CardLink item={item} />;
158+
case "category":
159+
return <CardCategory item={item} />;
160+
default:
161+
throw new Error(`unknown item type ${JSON.stringify(item)}`);
162+
}
163+
}
164+
```
165+

0 commit comments

Comments
 (0)