diff --git a/docs-site/src/css/custom.css b/docs-site/src/css/custom.css
index 78c6233b1..e551c6cc1 100644
--- a/docs-site/src/css/custom.css
+++ b/docs-site/src/css/custom.css
@@ -48,6 +48,8 @@ html {
--gusto-code-block-bg: #f7f7f8;
--gusto-body-text-color: rgba(27, 27, 29, 0.82);
+ --ifm-heading-color: #000000;
+
--docusaurus-highlighted-code-line-bg: rgba(241, 94, 73, 0.1);
}
@@ -89,6 +91,8 @@ html {
--gusto-code-block-bg: #2a2a2d;
--gusto-body-text-color: rgba(255, 255, 255, 0.72);
+ --ifm-heading-color: #ffffff;
+
--docusaurus-highlighted-code-line-bg: rgba(241, 94, 73, 0.15);
}
diff --git a/docs-site/src/theme/DocCard/Description/index.tsx b/docs-site/src/theme/DocCard/Description/index.tsx
new file mode 100644
index 000000000..4c131f9f3
--- /dev/null
+++ b/docs-site/src/theme/DocCard/Description/index.tsx
@@ -0,0 +1,21 @@
+import React, { type ReactNode } from 'react'
+import clsx from 'clsx'
+import { ThemeClassNames } from '@docusaurus/theme-common'
+import type { Props } from '@theme/DocCard/Description'
+
+import styles from './styles.module.css'
+
+export default function DocCardDescription({ description }: Props): ReactNode {
+ return (
+
+ {description}
+
+ )
+}
diff --git a/docs-site/src/theme/DocCard/Description/styles.module.css b/docs-site/src/theme/DocCard/Description/styles.module.css
new file mode 100644
index 000000000..7a7caacbd
--- /dev/null
+++ b/docs-site/src/theme/DocCard/Description/styles.module.css
@@ -0,0 +1,10 @@
+.cardDescription {
+ font-size: 0.95rem;
+ line-height: 1.6;
+ color: #525860;
+ margin: 0;
+}
+
+[data-theme='dark'] .cardDescription {
+ color: rgba(255, 255, 255, 0.65);
+}
diff --git a/docs-site/src/theme/DocCard/Heading/Icon/index.tsx b/docs-site/src/theme/DocCard/Heading/Icon/index.tsx
new file mode 100644
index 000000000..972c4a0c4
--- /dev/null
+++ b/docs-site/src/theme/DocCard/Heading/Icon/index.tsx
@@ -0,0 +1,12 @@
+import React, { type ReactNode } from 'react'
+import clsx from 'clsx'
+import { ThemeClassNames } from '@docusaurus/theme-common'
+import type { Props } from '@theme/DocCard/Heading/Icon'
+
+import styles from './styles.module.css'
+
+export default function DocCardHeadingIcon({ icon }: Props): ReactNode {
+ return (
+ {icon}
+ )
+}
diff --git a/docs-site/src/theme/DocCard/Heading/Icon/styles.module.css b/docs-site/src/theme/DocCard/Heading/Icon/styles.module.css
new file mode 100644
index 000000000..10ed5d4f5
--- /dev/null
+++ b/docs-site/src/theme/DocCard/Heading/Icon/styles.module.css
@@ -0,0 +1,4 @@
+.cardTitleIcon {
+ font-size: 1.6rem;
+ margin-right: 0.6rem;
+}
diff --git a/docs-site/src/theme/DocCard/Heading/Text/index.tsx b/docs-site/src/theme/DocCard/Heading/Text/index.tsx
new file mode 100644
index 000000000..f06d86d42
--- /dev/null
+++ b/docs-site/src/theme/DocCard/Heading/Text/index.tsx
@@ -0,0 +1,21 @@
+import React, { type ReactNode } from 'react'
+import clsx from 'clsx'
+import { ThemeClassNames } from '@docusaurus/theme-common'
+import type { Props } from '@theme/DocCard/Heading/Text'
+
+import styles from './styles.module.css'
+
+export default function DocCardHeadingText({ title }: Props): ReactNode {
+ return (
+
+ {title}
+
+ )
+}
diff --git a/docs-site/src/theme/DocCard/Heading/Text/styles.module.css b/docs-site/src/theme/DocCard/Heading/Text/styles.module.css
new file mode 100644
index 000000000..28b3726cd
--- /dev/null
+++ b/docs-site/src/theme/DocCard/Heading/Text/styles.module.css
@@ -0,0 +1,10 @@
+.cardTitleText {
+ font-size: 1.25rem;
+ font-weight: 600;
+ letter-spacing: -0.01em;
+ color: #1b1b1d;
+}
+
+[data-theme='dark'] .cardTitleText {
+ color: #ffffff;
+}
diff --git a/docs-site/src/theme/DocCard/Heading/index.tsx b/docs-site/src/theme/DocCard/Heading/index.tsx
new file mode 100644
index 000000000..ea39ea87f
--- /dev/null
+++ b/docs-site/src/theme/DocCard/Heading/index.tsx
@@ -0,0 +1,22 @@
+import React, { type ReactNode } from 'react'
+import clsx from 'clsx'
+import { ThemeClassNames } from '@docusaurus/theme-common'
+import Heading from '@theme/Heading'
+import Icon from '@theme/DocCard/Heading/Icon'
+import Text from '@theme/DocCard/Heading/Text'
+import type { Props } from '@theme/DocCard/Heading'
+
+import styles from './styles.module.css'
+
+export default function DocCardHeading({ item, title, icon }: Props): ReactNode {
+ return (
+
+ {icon && }
+
+
+ )
+}
diff --git a/docs-site/src/theme/DocCard/Heading/styles.module.css b/docs-site/src/theme/DocCard/Heading/styles.module.css
new file mode 100644
index 000000000..220975288
--- /dev/null
+++ b/docs-site/src/theme/DocCard/Heading/styles.module.css
@@ -0,0 +1,5 @@
+.cardTitle {
+ display: inline-flex;
+ align-items: center;
+ margin: 0 0 0.5rem 0;
+}
diff --git a/docs-site/src/theme/DocCard/Layout/index.tsx b/docs-site/src/theme/DocCard/Layout/index.tsx
new file mode 100644
index 000000000..0de3d2cf9
--- /dev/null
+++ b/docs-site/src/theme/DocCard/Layout/index.tsx
@@ -0,0 +1,49 @@
+import React, { type ReactNode } from 'react'
+import clsx from 'clsx'
+import Link from '@docusaurus/Link'
+import { ThemeClassNames } from '@docusaurus/theme-common'
+import Heading from '@theme/DocCard/Heading'
+import Description from '@theme/DocCard/Description'
+import type { Props } from '@theme/DocCard/Layout'
+
+import styles from './styles.module.css'
+
+function Container({
+ className,
+ href,
+ children,
+}: {
+ className?: string
+ href: string
+ children: ReactNode
+}): ReactNode {
+ return (
+
+ {children}
+
+ )
+}
+
+export default function DocCardLayout({
+ item,
+ className,
+ href,
+ icon,
+ title,
+ description,
+}: Props): ReactNode {
+ return (
+
+
+ {description && }
+
+ )
+}
diff --git a/docs-site/src/theme/DocCard/Layout/styles.module.css b/docs-site/src/theme/DocCard/Layout/styles.module.css
new file mode 100644
index 000000000..d3279dee6
--- /dev/null
+++ b/docs-site/src/theme/DocCard/Layout/styles.module.css
@@ -0,0 +1,36 @@
+.cardContainer {
+ --ifm-link-color: var(--ifm-color-emphasis-800);
+ --ifm-link-hover-color: var(--ifm-color-emphasis-700);
+ --ifm-link-hover-decoration: none;
+
+ display: block;
+ height: 100%;
+ padding: 2rem;
+ background-color: #ffffff;
+ border: 1px solid #ebedf0;
+ border-radius: 16px;
+ box-shadow: none;
+ color: inherit;
+ text-decoration: none;
+ transition: border-color 0.15s ease;
+}
+
+.cardContainer:hover {
+ border-color: #d0d3d8;
+ box-shadow: none;
+ text-decoration: none;
+ color: inherit;
+}
+
+[data-theme='dark'] .cardContainer {
+ background-color: rgba(255, 255, 255, 0.03);
+ border-color: rgba(255, 255, 255, 0.1);
+}
+
+[data-theme='dark'] .cardContainer:hover {
+ border-color: rgba(255, 255, 255, 0.22);
+}
+
+.cardContainer *:last-child {
+ margin-bottom: 0;
+}
diff --git a/docs-site/src/theme/DocCard/index.tsx b/docs-site/src/theme/DocCard/index.tsx
new file mode 100644
index 000000000..be2281001
--- /dev/null
+++ b/docs-site/src/theme/DocCard/index.tsx
@@ -0,0 +1,73 @@
+import React, { type ReactNode } from 'react'
+import { useDocById, findFirstSidebarItemLink } from '@docusaurus/plugin-content-docs/client'
+import {
+ extractLeadingEmoji,
+ useDocCardDescriptionCategoryItemsPlural,
+} from '@docusaurus/theme-common/internal'
+import isInternalUrl from '@docusaurus/isInternalUrl'
+import Layout from '@theme/DocCard/Layout'
+
+import type { Props } from '@theme/DocCard'
+import type { PropSidebarItemCategory, PropSidebarItemLink } from '@docusaurus/plugin-content-docs'
+
+function getFallbackEmojiIcon(item: PropSidebarItemLink | PropSidebarItemCategory): string {
+ if (item.type === 'category') {
+ return '🗃'
+ }
+ return isInternalUrl(item.href) ? '📄️' : '🔗'
+}
+
+function getIconTitleProps(item: PropSidebarItemLink | PropSidebarItemCategory): {
+ icon: ReactNode
+ title: string
+} {
+ const extracted = extractLeadingEmoji(item.label)
+ const emoji = extracted.emoji ?? getFallbackEmojiIcon(item)
+ return {
+ icon: emoji,
+ title: extracted.rest.trim(),
+ }
+}
+
+function CardCategory({ item }: { item: PropSidebarItemCategory }): ReactNode {
+ const href = findFirstSidebarItemLink(item)
+ const categoryItemsPlural = useDocCardDescriptionCategoryItemsPlural()
+
+ // Unexpected: categories that don't have a link have been filtered upfront
+ if (!href) {
+ return null
+ }
+ return (
+
+ )
+}
+
+function CardLink({ item }: { item: PropSidebarItemLink }): ReactNode {
+ const doc = useDocById(item.docId ?? undefined)
+ return (
+
+ )
+}
+
+export default function DocCard({ item }: Props): ReactNode {
+ switch (item.type) {
+ case 'link':
+ return
+ case 'category':
+ return
+ default:
+ throw new Error(`unknown item type ${JSON.stringify(item)}`)
+ }
+}
diff --git a/docs-site/src/theme/DocCardList/index.tsx b/docs-site/src/theme/DocCardList/index.tsx
new file mode 100644
index 000000000..6fba2b485
--- /dev/null
+++ b/docs-site/src/theme/DocCardList/index.tsx
@@ -0,0 +1,37 @@
+import React, { type ComponentProps, type ReactNode } from 'react'
+import clsx from 'clsx'
+import {
+ useCurrentSidebarSiblings,
+ filterDocCardListItems,
+} from '@docusaurus/plugin-content-docs/client'
+import DocCard from '@theme/DocCard'
+import type { Props } from '@theme/DocCardList'
+import styles from './styles.module.css'
+
+function DocCardListForCurrentSidebarCategory({ className }: Props) {
+ const items = useCurrentSidebarSiblings()
+ return
+}
+
+function DocCardListItem({ item }: { item: ComponentProps['item'] }) {
+ return (
+
+
+
+ )
+}
+
+export default function DocCardList(props: Props): ReactNode {
+ const { items, className } = props
+ if (!items) {
+ return
+ }
+ const filteredItems = filterDocCardListItems(items)
+ return (
+
+ {filteredItems.map((item, index) => (
+
+ ))}
+
+ )
+}
diff --git a/docs-site/src/theme/DocCardList/styles.module.css b/docs-site/src/theme/DocCardList/styles.module.css
new file mode 100644
index 000000000..5091bd337
--- /dev/null
+++ b/docs-site/src/theme/DocCardList/styles.module.css
@@ -0,0 +1,7 @@
+.docCardListItem {
+ margin-bottom: 2rem;
+}
+
+.docCardListItem > * {
+ height: 100%;
+}