- Accordion
- Agent Thinking
- Alert
- Alert Dialog
- Announcement
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Checkbox
- Checkbox Card
- Code Block
- Collapsible
- Color Picker
- Combobox
- Command
- Composer
- Composer Loader
- Context Menu
- Data Grid
- Data Table
- Date Picker
- Dialog
- Direction
- Drawer
- Dropdown Menu
- Empty
- Field
- File Upload
- Hover Card
- Input
- Input Group
- Input Otp
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Meter
- Native Select
- Navigation Menu
- Number Field
- Pagination
- Period Switcher
- Popover
- Progress
- Questionnaire
- Radio Card
- Radio Group
- Resizable
- Reveal
- Scroll Area
- Search Field
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Social Button
- Sonner
- Spinner
- Status Dot
- Switch
- Switch Card
- Table
- Tabs
- Tag Group
- Textarea
- Theme Toggle
- Time Field
- Toggle
- Toggle Group
- Tooltip
- Trace
- Tree View
- Agent Creator 01
- Agent Limits Card 01
- Agent Progress 01
- AI Chat 01
- AI Image Generation 01
- AI Profile 01
- Alerts Card 01
- Auth Card 01
- Calendar 01
- Composer 01
- Composer Attachments 01
- Composer Panel 01
- Finance Dashboard 01
- Home Dashboard 01
- Marketing Dashboard 01
- Medical Profile 01
- Meeting Scheduler 01
- Multi Agent Chat 01
- Notification Center 01
- Patient Info Card 01
- Project Board 01
- Questionnaire 01
- Settings Modal 01
- Sidebar 01
- Stat Cards 01
- Task List 01
- Web Search 01
- Activity Rings Card 01
- Area Chart Card 01
- Bar List Card 01
- Combo Chart Card 01
- Contributions Card 01
- Earnings Chart Card 01
- Funnel Chart Card 01
- Heatmap Chart Card 01
- Line Chart Card 01
- Most Active Days Card 01
- Orders Chart Card 01
- Radar Chart Card 01
- Radial Chart Card 01
- Revenue Chart Card 01
- Sankey Chart Card 01
- Scatter Chart Card 01
- Sleep Score Card 01
- Stage Bars Card 01
- Steps Card 01
Project notes
Keep the team up to date.
Next review is scheduled for Friday.
Storage
Current workspace usage
12 GB of 50 GB used
default
variant="default"
flat
variant="flat"
muted
variant="muted"
sunken
variant="sunken"
outline
variant="outline"
Tray
variant="muted" size="xs"
Raised tile
1,284
interactive
Hover or focus me
Installation
$ pnpm dlx shadcn@latest add @cobalt/card
Options
variant="muted"removes the border and shadow and uses a muted surface.variant="flat"keeps the card surface but removes the border and shadow.interactiveadds a 2px ring on hover (the--border-hovertoken) and on keyboard focus. The ring is a box-shadow, so nothing lifts, scales or shifts.renderchanges the element, for example<Card render={<section />} />or<CardTitle render={<h3 />} />.variant="sunken"keeps the default border and shadow and uses the--surface-sunkenfill, for panels nested in a surface.mutedandflatare unchanged.variant="outline"keeps the default border and shadow with the page fill (--background), which is darker than--cardin dark mode.variant="raised"is a borderless object with the card shadow and the--surface-nestedfill, for a tile placed on amutedorsunkensurface.size="xs"sets--card-spacingto 10px, for small tiles. On amutedcard the header and footer stay 16px from the edge so a nested tile can sit closer to it.CardTitle size="lg"is a big number:text-2xl, medium weight, tabular figures.size="flush"sets--card-spacingto 0 for content that brings its own padding.radiussets the corner radius in px (10,12,14,16,18,20,24). Header, footer and edge images follow it. The default is12.CardTitle variant="secondary"is a small secondary heading:text-sm, medium weight, muted colour.
Source
import { mergeProps } from "@base-ui/react/merge-props";
import { useRender } from "@base-ui/react/use-render";
import { cn } from "cn";
import * as React from "react";
const CARD_RADIUS = {
10: "[--card-radius:10px]",
12: "[--card-radius:12px]",
14: "[--card-radius:14px]",
16: "[--card-radius:16px]",
18: "[--card-radius:18px]",
20: "[--card-radius:20px]",
24: "[--card-radius:24px]",
} as const;
function Card({
className,
size = "default",
variant = "default",
radius = 12,
interactive = false,
render,
...props
}: useRender.ComponentProps<"div"> & {
/**
* `flush` removes the card spacing, for content that brings its own padding.
* `xs` is 10px, for small tiles. On a `muted` card the header and footer
* stay 16px from the edge, so a nested tile can sit closer to it.
*/
size?: "default" | "sm" | "xs" | "flush";
/**
* `sunken` keeps the default border and shadow with a recessed
* fill (--surface-sunken), for panels nested in a surface.
* `outline` keeps them with the page fill (--background), which is
* darker than --card in dark mode.
* `raised` is a borderless object with the card shadow, for a tile placed
* on a `muted` or `sunken` surface.
*/
variant?:
| "default"
| "muted"
| "flat"
| "sunken"
| "outline"
| "bordered"
| "raised";
/** Corner radius in px. Header, footer and images follow it. */
radius?: 10 | 12 | 14 | 16 | 18 | 20 | 24;
/** Adds a 2px hover and focus ring, with no lift, scale or layout shift. */
interactive?: boolean;
}) {
return useRender({
defaultTagName: "div",
props: mergeProps<"div">(
{
className: cn(
"group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-(--card-radius) py-(--card-spacing) text-sm text-card-foreground [--card-spacing:--spacing(4)] has-[>img:first-child]:pt-0 data-[size=flush]:[--card-spacing:0px] data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=xs]:[--card-spacing:--spacing(2.5)] *:[img:first-child]:rounded-t-(--card-radius) *:[img:last-child]:rounded-b-(--card-radius)",
CARD_RADIUS[radius],
(variant === "default" ||
variant === "sunken" ||
variant === "outline") &&
"border border-[var(--border-subtle,var(--border))] bg-card shadow-[0_1px_1px_0_rgb(0_0_0/0.05)] dark:shadow-[0_1px_1px_0_rgb(0_0_0/0.14)]",
// In light, --surface-sunken and the ghost hover fill are the same
// grey, so children of a sunken card hover on the next step. Dark
// keeps its own hover fill.
variant === "sunken" &&
"bg-[var(--surface-sunken,var(--muted))] not-dark:[--surface-hover:var(--surface-strong,var(--border))]",
variant === "outline" && "bg-background",
// The full --border line, brighter than --border-subtle in dark.
variant === "bordered" &&
"border border-border bg-card shadow-[0_1px_1px_0_rgb(0_0_0/0.05)] dark:shadow-[0_1px_1px_0_rgb(0_0_0/0.14)]",
variant === "muted" &&
"bg-[var(--surface-sunken,var(--muted))] not-dark:[--surface-hover:var(--surface-strong,var(--border))]",
variant === "flat" && "border-0 bg-card shadow-none",
variant === "raised" &&
"border-0 bg-[var(--surface-nested,var(--card))] shadow-[0_1px_1px_0_rgb(0_0_0/0.05)] not-dark:[--surface-hover:var(--muted)] dark:shadow-[0_1px_1px_0_rgb(0_0_0/0.14)]",
interactive &&
"cursor-pointer ring-2 ring-transparent transition-[box-shadow] duration-150 ease-out outline-none hover:ring-[var(--border-hover,var(--border))] focus-visible:ring-ring data-popup-open:ring-ring data-popup-open:hover:ring-ring motion-reduce:transition-none",
className
),
},
props
),
render,
state: { interactive, slot: "card", size, variant },
});
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn(
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-(--card-radius,12px) px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] group-data-[size=xs]/card:group-data-[variant=muted]/card:px-4 group-data-[size=xs]/card:group-data-[variant=muted]/card:pt-1.5 [.border-b]:pb-(--card-spacing)",
className
)}
{...props}
/>
);
}
function CardTitle({
className,
variant = "default",
size = "default",
render,
...props
}: useRender.ComponentProps<"div"> & {
/**
* `secondary` is a small secondary heading: text-sm, medium, muted colour.
* `lg` is a big number.
*/
variant?: "default" | "secondary";
size?: "default" | "lg";
}) {
return useRender({
defaultTagName: "div",
props: mergeProps<"div">(
{
className: cn(
"text-base leading-normal font-medium group-data-[size=sm]/card:text-sm",
variant === "secondary" &&
"text-sm leading-5 font-medium text-muted-foreground group-data-[size=sm]/card:text-sm",
size === "lg" &&
"text-2xl leading-8.5 font-medium tabular-nums group-data-[size=sm]/card:text-2xl",
className
),
},
props
),
render,
state: { slot: "card-title" },
});
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-description"
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
);
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className
)}
{...props}
/>
);
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("flex flex-col gap-3 px-(--card-spacing)", className)}
{...props}
/>
);
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn(
"flex items-center rounded-b-(--card-radius,12px) px-(--card-spacing) group-data-[size=xs]/card:group-data-[variant=muted]/card:px-4 group-data-[size=xs]/card:group-data-[variant=muted]/card:pb-1.5 [.border-t]:pt-(--card-spacing)",
className
)}
{...props}
/>
);
}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
}; On this page