From de319199aeded26ff43001a7c05dba6f7f729f33 Mon Sep 17 00:00:00 2001 From: Aaron Lee Date: Fri, 26 Jun 2026 17:22:45 -0400 Subject: [PATCH] feat(docs-site): swizzle DocCard and restyle to match LinkCard look Swizzle DocCard and DocCardList from @docusaurus/theme-classic so we can own the markup and styling. Restyle the CSS modules to mirror the LinkCard look from #2102: 16px radius, 2rem padding, hairline border, no shadow, hover bumps the border; title at 1.25rem / 600 / -0.01em with dark text in light mode and white in dark mode; description at 0.95rem with line-height 1.6. React structure left unchanged; the existing Infima row/col grid in DocCardList still drives layout. Also bump heading tokens in custom.css to pure black in light mode and pure white in dark mode (was inheriting Infima's soft #e3e3e3 on dark). Co-Authored-By: Claude Opus 4.7 (1M context) --- docs-site/src/css/custom.css | 4 + .../src/theme/DocCard/Description/index.tsx | 21 ++++++ .../DocCard/Description/styles.module.css | 10 +++ .../src/theme/DocCard/Heading/Icon/index.tsx | 12 +++ .../DocCard/Heading/Icon/styles.module.css | 4 + .../src/theme/DocCard/Heading/Text/index.tsx | 21 ++++++ .../DocCard/Heading/Text/styles.module.css | 10 +++ docs-site/src/theme/DocCard/Heading/index.tsx | 22 ++++++ .../theme/DocCard/Heading/styles.module.css | 5 ++ docs-site/src/theme/DocCard/Layout/index.tsx | 49 +++++++++++++ .../theme/DocCard/Layout/styles.module.css | 36 +++++++++ docs-site/src/theme/DocCard/index.tsx | 73 +++++++++++++++++++ docs-site/src/theme/DocCardList/index.tsx | 37 ++++++++++ .../src/theme/DocCardList/styles.module.css | 7 ++ 14 files changed, 311 insertions(+) create mode 100644 docs-site/src/theme/DocCard/Description/index.tsx create mode 100644 docs-site/src/theme/DocCard/Description/styles.module.css create mode 100644 docs-site/src/theme/DocCard/Heading/Icon/index.tsx create mode 100644 docs-site/src/theme/DocCard/Heading/Icon/styles.module.css create mode 100644 docs-site/src/theme/DocCard/Heading/Text/index.tsx create mode 100644 docs-site/src/theme/DocCard/Heading/Text/styles.module.css create mode 100644 docs-site/src/theme/DocCard/Heading/index.tsx create mode 100644 docs-site/src/theme/DocCard/Heading/styles.module.css create mode 100644 docs-site/src/theme/DocCard/Layout/index.tsx create mode 100644 docs-site/src/theme/DocCard/Layout/styles.module.css create mode 100644 docs-site/src/theme/DocCard/index.tsx create mode 100644 docs-site/src/theme/DocCardList/index.tsx create mode 100644 docs-site/src/theme/DocCardList/styles.module.css 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%; +}