Skip to content
59 linesCodeBlameRaw
1import { type VariantProps, cva } from "class-variance-authority";
2import { Slot } from "radix-ui";
3import type { ComponentProps } from "react";
4
5import { 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
16export 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
34export type CardProps = ComponentProps<"div"> & VariantProps<typeof cardVariants> & { asChild?: boolean };
35
36export 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
41export 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
45export function CardTitle({ className, ...props }: ComponentProps<"h2">) {
46 return <h2 data-slot="card-title" className={cn("text-sm font-medium", className)} {...props} />;
47}
48
49export function CardDescription({ className, ...props }: ComponentProps<"p">) {
50 return <p data-slot="card-description" className={cn("text-sm text-muted", className)} {...props} />;
51}
52
53export function CardContent({ className, ...props }: ComponentProps<"div">) {
54 return <div data-slot="card-content" className={cn("p-4", className)} {...props} />;
55}
56
57export 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}