diff --git a/src/css/custom.css b/src/css/custom.css index 869d7fe..ea51e6d 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -660,16 +660,129 @@ article h4 { float: none; } -[class*="generatedIndexPage"] .row .col--6 { - flex: 0 0 100% !important; - max-width: 100% !important; +/* Style overview pages as bullet point list */ +[class*="generatedIndexPage"] .row { + display: block !important; + list-style: none !important; + padding: 1rem 0 2rem 2rem !important; } -[class*="generatedIndexPage"] .row { - display: flex !important; - flex-direction: column !important; - gap: 1rem !important; - align-items: stretch !important; +[class*="generatedIndexPage"] .col { + display: list-item !important; + list-style-type: disc !important; + list-style-position: outside !important; + margin-left: 1rem !important; + margin-bottom: 0.5rem !important; + padding: 0 !important; + color: #9ca3af !important; + cursor: pointer !important; +} + +[data-theme="dark"] [class*="generatedIndexPage"] .col { + color: #6b7280 !important; +} + +[class*="generatedIndexPage"] article, +[class*="generatedIndexPage"] .card, +[class*="generatedIndexPage"] .item { + all: unset !important; + display: inline !important; +} + +[class*="generatedIndexPage"] a { + all: unset !important; + display: inline !important; + cursor: pointer !important; + text-decoration: none !important; +} + +/* Style title inline with light blue color */ +[class*="generatedIndexPage"] .cardTitle_rnsV, +[class*="generatedIndexPage"] h2 { + all: unset !important; + display: inline !important; + font-weight: 600 !important; + color: var(--ifm-color-primary) !important; + font-size: 1rem !important; +} + +[data-theme="dark"] [class*="generatedIndexPage"] h2 { + color: var(--ifm-color-primary-lightest) !important; +} + +[class*="generatedIndexPage"] h2::before { + content: "" !important; +} + +/* Display description inline after heading */ +[class*="generatedIndexPage"] p { + all: unset !important; + display: inline !important; + color: var(--ifm-font-color-base) !important; + font-size: 1rem !important; +} + +/* Add colon separator between heading and description */ +[class*="generatedIndexPage"] h2::after { + content: ": " !important; + font-weight: normal !important; + color: var(--ifm-font-color-base) !important; +} + +/* Exclude pagination navigation from bullet list styling - use higher specificity */ +nav.pagination-nav, +[class*="generatedIndexPage"] nav.pagination-nav { + display: grid !important; + grid-template-columns: repeat(2, 1fr) !important; + gap: var(--ifm-spacing-horizontal) !important; + padding: 0 !important; + list-style: none !important; +} + +nav.pagination-nav .pagination-nav__link, +[class*="generatedIndexPage"] nav.pagination-nav .pagination-nav__link { + display: block !important; + line-height: var(--ifm-heading-line-height) !important; + padding: var(--ifm-global-spacing) !important; + border: 1px solid var(--ifm-color-emphasis-300) !important; + border-radius: var(--ifm-pagination-nav-border-radius) !important; + text-decoration: none !important; + transition: border-color var(--ifm-transition-fast) + var(--ifm-transition-timing-default) !important; + cursor: pointer !important; +} + +nav.pagination-nav .pagination-nav__link--next { + text-align: right !important; + grid-column: 2 / 3 !important; +} + +nav.pagination-nav .pagination-nav__link:hover, +[class*="generatedIndexPage"] nav.pagination-nav .pagination-nav__link:hover { + border-color: var(--ifm-color-primary) !important; + text-decoration: none !important; +} + +nav.pagination-nav .pagination-nav__sublabel, +[class*="generatedIndexPage"] nav.pagination-nav .pagination-nav__sublabel { + display: block !important; + color: var(--ifm-color-content-secondary) !important; + font-size: var(--ifm-h5-font-size) !important; + font-weight: var(--ifm-font-weight-semibold) !important; + margin-bottom: 0.25rem !important; +} + +nav.pagination-nav .pagination-nav__label, +[class*="generatedIndexPage"] nav.pagination-nav .pagination-nav__label { + display: block !important; + color: var(--ifm-link-color) !important; + font-size: var(--ifm-h4-font-size) !important; + font-weight: var(--ifm-heading-font-weight) !important; + word-break: break-word !important; +} + +nav.pagination-nav .pagination-nav__link *:last-child { + margin-bottom: 0 !important; } article, diff --git a/src/theme/DocCard/index.tsx b/src/theme/DocCard/index.tsx new file mode 100644 index 0000000..967e42f --- /dev/null +++ b/src/theme/DocCard/index.tsx @@ -0,0 +1,186 @@ +import React, { type ReactNode } from "react" +import clsx from "clsx" +import Link from "@docusaurus/Link" +import { + useDocById, + findFirstSidebarItemLink, +} from "@docusaurus/plugin-content-docs/client" +import { usePluralForm } from "@docusaurus/theme-common" +import isInternalUrl from "@docusaurus/isInternalUrl" +import { translate } from "@docusaurus/Translate" + +import type { Props } from "@theme/DocCard" +import Heading from "@theme/Heading" +import type { + PropSidebarItemCategory, + PropSidebarItemLink, +} from "@docusaurus/plugin-content-docs" + +import styles from "./styles.module.css" + +function useCategoryItemsPlural() { + const { selectMessage } = usePluralForm() + return (count: number) => + selectMessage( + count, + translate( + { + message: "1 item|{count} items", + id: "theme.docs.DocCard.categoryDescription.plurals", + description: + "The default description for a category card in the generated index about how many items this category includes", + }, + { count }, + ), + ) +} + +function CardContainer({ + className, + href, + children, +}: { + className?: string + href: string + children: ReactNode +}): ReactNode { + return ( + + {children} + + ) +} + +function CardLayout({ + className, + href, + title, + description, +}: { + className?: string + href: string + title: string + description?: string +}): ReactNode { + return ( + + + {title} + + {description && ( +

+ {description} +

+ )} +
+ ) +} + +function NestedItem({ + item, +}: { item: PropSidebarItemLink | PropSidebarItemCategory }) { + if (item.type === "link") { + const doc = useDocById(item.docId ?? undefined) + const description = item.description ?? doc?.description + return ( +
  • + + {item.label} + {description && ( + <> + : + {description} + + )} + +
  • + ) + } else if (item.type === "category") { + const subHref = findFirstSidebarItemLink(item) + return subHref ? ( +
  • + + {item.label} + {item.description && ( + <> + : + + {item.description} + + + )} + +
  • + ) : null + } + return null +} + +function CardCategory({ item }: { item: PropSidebarItemCategory }): ReactNode { + const href = findFirstSidebarItemLink(item) + + // Unexpected: categories that don't have a link have been filtered upfront + if (!href) { + return null + } + + return ( + + + {item.label} + + {item.description && ( +

    + {item.description} +

    + )} + {item.items.length > 0 && ( + + )} +
    + ) +} + +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/src/theme/DocCard/styles.module.css b/src/theme/DocCard/styles.module.css new file mode 100644 index 0000000..7a22a8c --- /dev/null +++ b/src/theme/DocCard/styles.module.css @@ -0,0 +1,62 @@ +.cardContainer { + --ifm-link-color: var(--ifm-color-emphasis-800); + --ifm-link-hover-color: var(--ifm-color-emphasis-700); + --ifm-link-hover-decoration: none; + + box-shadow: 0 1.5px 3px 0 rgb(0 0 0 / 15%); + border: 1px solid var(--ifm-color-emphasis-200); + transition: all var(--ifm-transition-fast) ease; + transition-property: border, box-shadow; +} + +.cardContainer:hover { + border-color: var(--ifm-color-primary); + box-shadow: 0 3px 6px 0 rgb(0 0 0 / 20%); +} + +.cardContainer *:last-child { + margin-bottom: 0; +} + +.cardTitle { + font-size: 1.2rem; +} + +.cardDescription { + font-size: 0.8rem; +} + +.nestedList { + margin-top: 0; + padding-left: 1.5rem; + list-style-type: circle; + font-weight: 400; + color: var(--ifm-color-emphasis-900); +} + +.nestedList a { + color: var(--ifm-link-color); + font-weight: 400; +} + +.nestedLink { + text-decoration: none; + transition: color 0.2s ease; +} + +.nestedTitle { + color: var(--ifm-color-primary); + font-weight: 600; +} + +.nestedColon { + color: var(--ifm-font-color-base); +} + +.nestedDescription { + color: var(--ifm-font-color-base); +} + +.nestedLink:hover .nestedTitle { + color: var(--ifm-color-primary-dark); +}