Card

Displays a card with header, content, and footer.

Cards are surface-level containers that host related content. With no title or description, the body fills the whole padded surface.
Installation
$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/card.json
Usage
Pass `title`, `description`, `action`, `footer` as props. Children render inside `<CardContent>` automatically.
TSImport
import { Card } from "@/components/ui/card";
TSExample
import { Card } from "@/components/ui/card";

export function Example() {
  return (
    <Card
      title="Heads up"
      description="You can add components to your app using the squad-ui CLI."
    >
      Card content goes here.
    </Card>
  );
}
Composition
Anatomy of the Card primitive.
Card                              (single component — pass content as props)
├── icon={LucideIcon}            (optional — FeaturedIcon above the title)
│   ├── iconColor="brand"         (color token; default "brand")
│   ├── iconVariant="gradient"    (gradient | flat; default "gradient")
│   └── iconSize="lg"             (sm | md | lg | xl | 2xl; default "lg")
├── title={<ReactNode>}           (renders into the header title slot)
├── description={<ReactNode>}     (multi-line / JSX is fine)
├── action={<Button … />}         (optional — top-right of the header)
├── footer={<ReactNode>}          (optional — bordered footer strip)
├── type="default" | "comment" | "image-card" | "toolbar"
├── count={3}                      (type="toolbar" — the pill beside the title)
├── countColor="warning"           (type="toolbar" — tints that pill)
├── link={{ label, href }}         (type="toolbar" — link button + right arrow, band's right end)
├── linkAs={Link}                  (type="toolbar" — your router's link; default <a>)
├── header="band" | "label"        (type="toolbar" — shaded band, or the project viewer's quiet label)
├── iconMuted                      (type="toolbar" — the header icon in the muted colour, for an empty card)
├── collapsible / open / onOpenChange (type="toolbar" — the band folds the body, caret on the right)
├── headerless / headerProps       (type="toolbar" — no band / props for the band)
├── bodyClassName="…"              (type="toolbar" — the padded body's classes)
├── image={url | <Image/>}         (type="image-card" — full-bleed media at the top)
├── imageAlt="…"                   (alt text when image is a URL)
├── ratio="W:H" | "W/H"            (type="image-card" — aspect ratio of the WHOLE card; image fills the remaining space)
├── imageRatio="W:H" | "W/H"       (type="image-card" — aspect ratio of just the image; default "16:9"; ignored when ratio is set)
├── href="…"                       (type="image-card" — makes the whole card a clickable <a>; pair with target / rel)
├── onClick={fn}                   (makes the whole card interactive — cursor-pointer, hover shadow, focus ring, keyboard activation)
├── tag={"New" | <Badge …/>}       (type="image-card" — overlay badge in the top-left of the image)
├── showCornerTab                 (pins a corner tab tinted to match the card outline)
├── cornerTabContent={text | <Comp/>}  (tab contents — string or any ReactNode)
├── cornerTabPosition="top-right" (top-right | top-left | bottom-right | bottom-left)
├── cornerTabClassName="…"        (override the tab's padding / text / background)
├── cornerTabIcon={<Icon/>}       (collapsed-state glyph)
├── cornerTabExpandOnHover        (square → label on card hover)
├── onCornerTabClick={fn}         (tab becomes a <button>)
├── children                      (auto-wrapped in <CardContent> unless contentWrapper="none")
├── size="default" | "sm"
└── contentWrapper="card-content" | "none"

Need a non-standard layout (e.g. an image-only card, custom header grid, or
two stacked content sections)? The slot exports — CardHeader, CardTitle,
CardDescription, CardAction, CardContent, CardFooter — are still exported
from the same file as an escape hatch. Drop the prop API for that one card
and compose the slots directly.
Basic
Title + description + body — the most common layout.
Basic
Title + description + body — the most common layout.
Cards are surface-level containers that host related content. Everything in this library uses them to scope a demo.
Content only
Pass just `children` — no `title` or `description`. The body fills the whole padded surface.
Cards are surface-level containers that host related content. With no title or description, the body fills the whole padded surface.
With icon
Pass any lucide icon to `icon` to render a `FeaturedIcon` above the title. Tune it with `iconColor`, `iconVariant`, and `iconSize`.
Brand
`onClick` makes the whole card interactive. Click me for a toast.
Amber
`iconColor` tints the FeaturedIcon — mirrors the Badge palette.
Flat indigo
`iconVariant="flat"` swaps the gradient chip for a soft flat tint.
With action
`action` slots a secondary control into the top-right of the header.
With action
action slots a secondary control into the top-right of the header.
Use this for links that jump to a fuller view or toggles that affect the card's contents.
Small size
size="sm" tightens the paddings and shrinks the title — useful for dense grids.
Small size
size="sm" tightens the paddings and shrinks the title.
Useful for dense grids of cards where a lot of surfaces share the viewport.
Image card
Pass `type="image-card"` plus `image`, `title`, and `description` for a full-bleed media tile. Use `imageRatio` to shape the image alone (default `"16:9"`); use `ratio` to lock the **whole card** to a target aspect — the image then fills the remaining space above the caption.
Modern kitchen with grey cabinetry
New
Linked card
`href` makes the whole card clickable. `tag` overlays a Badge in the top-left.
Modern kitchen with grey cabinetry
Square card
`ratio="1:1"` — whole card is square; image fills the remaining space.
Modern kitchen with grey cabinetry
Portrait card
`ratio="4:5"` — portrait card; image fills above the caption.
Toolbar
Pass `type="toolbar"` for the data grid's header band as a card: `icon` (in the band's green) and `title` on the left, then `count` in a pill (`countColor` tints it), `action` and a `link` button (right arrow; `linkAs` for your router) on the right, and `children` in a padded body that scrolls inside the card. Leave out `children` for the band on its own, or pass `collapsible` to make the band fold the body away.
Waiting on you3
  • Trunk or Treat Invite Video
  • Throw the First Punch
  • Legacy Builders
Inbox12

A plain `count` wears the band's neutral pill.

Toolbar — label header
`header="label"` draws the project viewer's quiet header instead of the shaded band: a small muted name beside a brand-coloured icon, no fill and no divider, padded to match the body. A plain `count` beside it becomes the small coral badge, and it folds like any toolbar with `collapsible`.
Church

True Hope Church

Pacific · Account 4114

Booklet · Awaiting Feedback · 2d

Nothing waiting on you
Corner tab
Set `showCornerTab` and `cornerTabContent` to pin a little tab to a corner of the card — its background matches the card's outline, and its two inner edges flow concavely into the card's edges. The content can be plain text or any component. Move it with `cornerTabPosition` and restyle it with `cornerTabClassName`. Add `cornerTabIcon` + `cornerTabExpandOnHover` for a square tab that glides open into a label on hover, and `onCornerTabClick` to make it a real button.
New
Text tab
`cornerTabContent` takes a plain string for the default styled tab.
The tab background matches the card's outline.
Pro
Component tab
Pass any ReactNode — here an icon + label.
Render whatever you like inside the tab.
Component tab
Drop any component into the tab — here a tiny `CircularProgress`.
The tab concavely flows into the card's top-right corner.
Expanding tab
`cornerTabExpandOnHover` keeps the tab a square until the card is hovered, then it glides open.
Hover the card, then click the tab to toggle.
Pricing grid
Two cards laid out in a responsive two-column grid with per-card footers.
Plan — Team
For growing squads that need more room.

$29/mo

Plan — Business
For larger orgs with advanced needs.

$79/mo

API Reference
Props exposed by the Card component.
PropTypeDefaultDescription
titleReactNode—Heading rendered inside the auto-emitted CardHeader.
descriptionReactNode—Subheading. Accepts strings or JSX (multi-line, links, code).
iconComponentType<{ className?: string }>—Lucide icon (or any `({ className }) => JSX`) rendered as a `FeaturedIcon` above the title.
iconColorFeaturedIconColor"brand"Color token for the `icon`'s FeaturedIcon.
iconVariant"gradient" | "flat""gradient"Surface treatment for the `icon`'s FeaturedIcon.
iconSize"sm" | "md" | "lg" | "xl" | "2xl""lg"Size of the `icon`'s FeaturedIcon.
actionReactNode—Top-right slot inside the header — typically a small button or link.
footerReactNode—Footer strip. Wrap multiple buttons in a flex row to align them.
size"default" | "sm""default"`sm` tightens paddings and shrinks the title — good for dense grids.
contentWrapper"card-content" | "none""card-content"When the prop API is in use, children are wrapped in `<CardContent>` by default. Set to `"none"` to render them inline (no padding).
type"default" | "comment" | "image-card" | "toolbar""default"`image-card` swaps the layout for a full-bleed image at the top with a controllable aspect ratio, then title + description below. `comment` renders an avatar + name + time header. `toolbar` draws the data grid's header band over a padded body.
imagestring | ReactNode—`type="image-card"` — image URL (rendered as `<img>` with `object-cover`) or any ReactNode (e.g. `<Image>` from next/image).
imageAltstring—Alt text used when `image` is a URL.
ratiostring—`type="image-card"` — aspect ratio for the **whole card** (image + caption). Accepts `"W:H"` / `"W/H"`. When set, the image fills the remaining space above the caption. Leave undefined for auto-height.
imageRatiostring"16:9"`type="image-card"` — aspect ratio for the **image** only. Accepts `"W:H"` / `"W/H"` (e.g. `"4:3"`, `"1:1"`). Ignored when `ratio` is set.
hrefstring—`type="image-card"` — makes the entire card a clickable link (renders as `<a>`). Pair with `target` / `rel` as needed. Adds a hover shadow + subtle image zoom.
targetHTMLAttributeAnchorTarget—Anchor target when `href` is set.
relstring—Anchor rel when `href` is set.
tagReactNode—`type="image-card"` — overlay badge in the top-left corner of the image. Pass a string for a default `<Badge>` or any ReactNode (e.g. `<Badge variant="secondary">…</Badge>`) for full control.
showCornerTabbooleanfalseRender a small corner tab clipped to one corner of the card, tinted to match the card's outline (`ring-foreground/10`). Requires `cornerTabContent`.
cornerTabContentReactNode—Content shown inside the corner tab. Pass a string/number for the default styled tab, or any ReactNode (including your own component).
cornerTabPosition"top-right" | "top-left" | "bottom-right" | "bottom-left""top-right"Which corner the tab sits in.
cornerTabClassNamestring—Extra classes for the tab element — override its padding, text, or background.
cornerTabIconReactNode—Glyph shown in the collapsed tab. With `cornerTabExpandOnHover` the tab stays a square holding just this icon until the card is hovered.
cornerTabExpandOnHoverbooleanfalseCollapse the tab to `cornerTabIcon` and glide it open on card hover/focus, revealing `cornerTabContent` as a label. Requires `cornerTabIcon`.
onCornerTabClick(e: MouseEvent) => void—Makes the tab a real `<button>` with its own click handler, hover tint, and focus ring — independent of the card's `onClick`.
countReactNode—`type="toolbar"` — the figure beside the title, in the band's neutral pill. Pass the figure (or an animated one), never a `<Badge>` — the card draws the pill.
countColor"success" | "warning" | "destructive" | "info" | "muted" | "brand"—`type="toolbar"` — tints the count pill with a status tone (one pill, not a badge inside a pill).
link{ label: ReactNode; href: string }—`type="toolbar"` — a link button with a right arrow at the band's right end, after `action`.
linkAsElementType"a"`type="toolbar"` — what `link` renders as. Pass your router's link (Next's `Link`) for client-side navigation.
header"band" | "label""band"`type="toolbar"` — `band` is the shaded records-toolbar band; `label` is the project viewer's quiet header: a small muted name beside a brand-coloured icon, no fill, no divider, padded to match the body.
iconTone"muted" | "coral"—`type="toolbar"` — the header icon's tone: `muted` (same as `iconMuted`) or `coral`, the attention colour — e.g. a list icon that turns coral while something in it needs doing.
iconMutedbooleanfalse`type="toolbar"` — draws the header's icon in the muted text colour instead of the header's colour. For a quiet card with nothing in it ("Nothing waiting on you"), so the icon does not call attention to an empty answer.
collapsiblebooleanfalse`type="toolbar"` — the whole band becomes a button that folds the body away, with a caret at its right end. Don't combine with `link` (an `<a>` can't sit inside a `<button>`).
open / defaultOpen / onOpenChangeboolean / boolean / (open: boolean) => void— / true / —`type="toolbar"` + `collapsible` — control the fold, or let it keep its own state.
fillbooleanfalse`type="toolbar"` — stretch to the height the parent gives it instead of hugging the content; the body flexes so what scrolls inside takes the rest. A folded collapsible card still shrinks to its band.
headerlessbooleanfalse`type="toolbar"` — draw no band, just the surface and the body.
headerPropsComponentProps<"div">—`type="toolbar"` — extra props for the band: a drag-handle class, an aria-label.
bodyClassNamestring—`type="toolbar"` — classes for the padded body (`p-3`, clipped, by default). `contentWrapper="none"` drops the body wrapper entirely; no `children` at all leaves the band standing on its own.
onClick(e) => void—Click handler. On any card it makes the whole surface interactive — adds `cursor-pointer`, a hover shadow, a focus ring, and keyboard activation (Enter/Space) with `role="button"`.
classNamestring—Override or extend the resolved Card classes.