| 1 | import { type VariantProps, cva } from "class-variance-authority"; |
| 2 | import { Slot } from "radix-ui"; |
| 3 | import type { ComponentProps } from "react"; |
| 4 | |
| 5 | import { cn } from "../../lib/cn"; |
| 6 | |
| 7 | // shadcn/ui's card, styled with g1t's tokens: the rounded panel every |
| 8 | // section of the site sits in. It has no padding of its own: a card that |
| 9 | // is one block of text takes `p-4` (or `p-5` for a settings section); one |
| 10 | // that is a list of rows is `divided`, each row with its own padding. `tone` |
| 11 | // says what it stands on: `surface` (the default) is a step up from the |
| 12 | // page, `plain` keeps the page's own background for a card inside a card, |
| 13 | // `bg` sinks to the page's. `asChild` puts the panel on a <section>, an |
| 14 | // <article> or a <Link>. |
| 15 | |
| 16 | export const cardVariants = cva("border border-line", { |
| 17 | variants: { |
| 18 | tone: { |
| 19 | surface: "bg-surface", |
| 20 | plain: "", |
| 21 | bg: "bg-bg", |
| 22 | }, |
| 23 | radius: { |
| 24 | xl: "rounded-xl", |
| 25 | lg: "rounded-lg", |
| 26 | }, |
| 27 | divided: { |
| 28 | true: "divide-y divide-line", |
| 29 | }, |
| 30 | }, |
| 31 | defaultVariants: { tone: "surface", radius: "xl" }, |
| 32 | }); |
| 33 | |
| 34 | export type CardProps = ComponentProps<"div"> & VariantProps<typeof cardVariants> & { asChild?: boolean }; |
| 35 | |
| 36 | export function Card({ className, tone, radius, divided, asChild = false, ...props }: CardProps) { |
| 37 | const Comp = asChild ? Slot.Root : "div"; |
| 38 | return <Comp data-slot="card" className={cn(cardVariants({ tone, radius, divided }), className)} {...props} />; |
| 39 | } |
| 40 | |
| 41 | export function CardHeader({ className, ...props }: ComponentProps<"div">) { |
| 42 | return <div data-slot="card-header" className={cn("flex flex-col gap-1 p-4", className)} {...props} />; |
| 43 | } |
| 44 | |
| 45 | export function CardTitle({ className, ...props }: ComponentProps<"h2">) { |
| 46 | return <h2 data-slot="card-title" className={cn("text-sm font-medium", className)} {...props} />; |
| 47 | } |
| 48 | |
| 49 | export function CardDescription({ className, ...props }: ComponentProps<"p">) { |
| 50 | return <p data-slot="card-description" className={cn("text-sm text-muted", className)} {...props} />; |
| 51 | } |
| 52 | |
| 53 | export function CardContent({ className, ...props }: ComponentProps<"div">) { |
| 54 | return <div data-slot="card-content" className={cn("p-4", className)} {...props} />; |
| 55 | } |
| 56 | |
| 57 | export function CardFooter({ className, ...props }: ComponentProps<"div">) { |
| 58 | return <div data-slot="card-footer" className={cn("flex items-center gap-2 p-4", className)} {...props} />; |
| 59 | } |