ContainersCard
Card
The default content container. A card groups a title, body, and optional action under a single border.
Demo
The default card has a 1 px --line-strong border, 4 px radius, and no shadow. The shadow is reserved for overlays.
Default
With icon
With icon
Foundations
The tokens. Color, type, space, motion, and the rest of the language.
Tones
Tones
Default surface
Cream on the page, slightly lighter than the page itself.
Inset surface
Slightly darker than the page, used for code blocks and table rows.
Anatomy
- 1 px
--line-strongborder, 4 px radius --surface-elevatedbackground (or--surface-insetfor the inset tone)- Padding: 24 px (default), 16 px in dense lists
- Title is set in display, 1.5 rem / 1.15 line-height
- Body is set in body, 0.9375 rem / 1.6 line-height
- No shadow. The card sits on the page; only overlays have shadows.
Implementation
card.tsx
import { type ElementType, type ReactNode } from "react"
import { cn } from "@/lib/utils"
import SiteIcon, type SiteIconProps } from "@/components/site-icon"
interface CardProps {
title?: string
icon?: SiteIconProps["icon"]
tone?: "default" | "inset"
as?: ElementType
className?: string
children: ReactNode
}
export function Card({ title, icon, tone = "default", as: As = "div", className, children }: CardProps) {
return (
<As className={cn("ds-card", tone === "inset" && "ds-card--inset", className)}>
{icon ? (
<div className="ds-card__icon-wrap">
<SiteIcon icon={icon} size="md" tone="emphasis" />
</div>
) : null}
{title ? <h3 className="ds-card__title">{title}</h3> : null}
{children}
</As>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | None | Optional title |
icon | LucideIcon | None | Optional icon, top-left |
tone | "default" | "inset" | "default" | Surface tone |
as | ElementType | "div" | Render as a different element |