"use client" import * as React from "react" import { cn } from "../../lib/utils" /** * replacement for `@wordpress/components`' Card family - **471 sites across 71 files** * (`Card` 131, `CardBody` 175, `CardHeader` 119, `CardDivider` 42, `CardMedia` 4). * * WHY THIS EXISTS RATHER THAN USING `ui/card`. We already have shadcn's Card, and it is a DIFFERENT * COMPONENT, not a differently-named one. Measured side by side: * * WordPress (today) shadcn ui/card * border box-shadow 0 0 0 1px (no layout cost) real `border` (adds 1px to the box) * corner radius 7px (radiusLarge 8px − 1) 12px (`rounded-xl`) * gap headerbody none 24px (`gap-6`) * header divider `border-bottom: 1px solid` none * drop shadow none - the shadow IS the border `shadow-sm` * * Adopting shadcn's would, on every card in the app, round the corners noticeably more, insert 24px of * gap that does not exist today, DELETE the divider line under ~65 card headers, and add a drop shadow * on top of the `` many of these cards already carry. That is a redesign of the app's * dominant surface. Spencer ruled (2026-08-10) to reproduce WordPress's geometry instead and leave the * look to the design pass - the same call made for Spacer, Divider and Elevation. * see akashic-docs/_CLAUDE/2026-08-09-ui-design-pass-backlog.md. * * Reproduced from WP's own source, not eyeballed * `build/card/styles.js`, the per-section `hook.js` files under `build/card`, and * `build/utils/config-values.js`. (Paths are written out in words rather than with a glob: a * star-slash inside a block comment CLOSES it, which is the same family of trap as the slash-star that * opened a phantom comment in ImagingStudies.) * Card box-shadow: 0 0 0 1px rgba(0,0,0,.1); outline: none; border-radius: calc(8px - 1px); * background: #fff. Defaults: size 'medium', isRounded true, isBorderless false. * Header border-bottom: 1px solid rgba(0,0,0,.1), removed when it is the LAST child; padding by size * Body height: auto; max-height: 100%; padding by size * Divider display: block; width: 100%; a 1px rgba(0,0,0,.1) rule * Media overflow: hidden; direct img/iframe go block, width 100%, height auto * padding by size - medium (the default) is `16px 24px`, small 16px, large `24px 32px`, xSmall 8px * Every child also takes the card's corner radius on first/last-of-type, so content clips correctly. * * The geometry lives in index.css under `.wp-card*`, NOT in inline styles, because the first/last- * of-type corner rules and the "header stops drawing its divider when it is last" rule are pseudo-class * selectors that inline styles cannot express. Reproducing them with JS index maths would be a * different behaviour dressed up as the same one. */ type CardSize = "xSmall" | "extraSmall" | "small" | "medium" | "large" const sizeClass = (size: CardSize = "medium") => `wp-card--${size === "extraSmall" ? "xSmall" : size}` export interface WpCardProps extends React.ComponentProps<"div"> { size?: CardSize isRounded?: boolean isBorderless?: boolean } function Card({ className, size = "medium", isRounded = true, isBorderless = false, ...props }: WpCardProps) { return (
) } export interface WpCardSectionProps extends React.ComponentProps<"div"> { /** Overrides the size inherited from the Card, exactly as WP's per-section `size` prop does. */ size?: CardSize isShady?: boolean } function CardBody({ className, size, isShady = false, ...props }: WpCardSectionProps) { return (
) } function CardHeader({ className, size, isShady = false, isBorderless = false, ...props }: WpCardSectionProps & { isBorderless?: boolean }) { return (
) } function CardFooter({ className, size, isShady = false, ...props }: WpCardSectionProps) { return (
) } function CardMedia({ className, ...props }: React.ComponentProps<"div">) { return
} /** WP renders this as an `
`; keeping the element means it is a separator to assistive tech too. */ function CardDivider({ className, ...props }: React.ComponentProps<"hr">) { return
} export { Card, CardBody, CardHeader, CardFooter, CardMedia, CardDivider }