← All components
layout / stable

Card

Surface that groups related content: 8px radius, border, soft shadow in light mode.

LIVE PREVIEW
Loading preview…

When to use

  • Dashboard widgets, settings sections and summary panels.
  • Kanban and grid items — `variant="interactive"` when the card opens something.

Consider an alternative

  • Lists of work items — use rows, not a card per item.
  • Inside another card — flatten with a separator instead.
  • Dialog bodies — the dialog is already a surface.

Import

import { Card } from '@revido-llc/kit';

Props

Props documented by the Kit. Refer to the TypeScript types for the complete interface, including inherited props.

accent{ color: string; width?: 2 | 3; }—Stored entity-identification edge. Width 2 or 3 uses the existing border scale.
colors{ background: string; foreground: string; }—Stored business-brand preview fill and contrasting text; ordinary surfaces use theme tokens.
variant"default" | "interactive""default"
childrenReactNode—
classNamestring—

Related exports

CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter

Examples

Anatomy

Header (title, description, action), content and footer. The parts own the padding: 24px outer edge, 16px between header and body — don't add `py-*` to the Card.

<Card className="w-full max-w-sm">
  <CardHeader>
    <CardTitle>Open proposals</CardTitle>
    <CardDescription>Sent in the last 30 days</CardDescription>
    <CardAction>
      <MenuTrigger>
        <Button variant="ghost" size="icon-sm" aria-label="More">
          <MoreHorizontal />
        </Button>
        <Menu placement="bottom end" aria-label="Proposal actions">
          <MenuItem id="edit" leading={{ kind: "icon", icon: <Pencil /> }}>Edit</MenuItem>
          <MenuItem id="duplicate" leading={{ kind: "icon", icon: <Copy /> }}>Duplicate</MenuItem>
          <MenuSeparator />
          <MenuItem id="delete" leading={{ kind: "icon", icon: <Trash2 /> }} tone="danger">Delete</MenuItem>
        </Menu>
      </MenuTrigger>
    </CardAction>
  </CardHeader>
  <CardContent className="flex flex-col gap-3">
    <div className="flex items-baseline gap-2">
      <span className="text-2xl font-semibold tabular-nums">€ 62.000</span>
      <Badge variant="soft" tone="success">+12% vs last month</Badge>
    </div>
    <div className="flex items-center justify-between">
      <AvatarGroup max={3} size="sm">
        <Avatar name="Ana Silva" />
        <Avatar name="Bram de Vries" />
        <Avatar name="Chloé Martin" />
        <Avatar name="Daan Jansen" />
      </AvatarGroup>
      <span className="text-xs text-foreground-tertiary">4 awaiting a reply</span>
    </div>
  </CardContent>
  <CardFooter>
    <Button size="sm" variant="ghost">Dismiss</Button>
    <Button size="sm">Review</Button>
  </CardFooter>
</Card>

Interactive

Hover strengthens the border; put the link or button inside.

<Card variant="interactive">
  <CardHeader className="pb-6">…</CardHeader>
</Card>

Settings section

Header, form body and a footer action row. Footer actions sit 24px below the body and 24px above the card edge; with `border-t` the footer gets 24px above the actions too.

<Card>
  <CardHeader>
    <CardTitle>Defaults</CardTitle>
    <CardDescription>Applied to new projects.</CardDescription>
  </CardHeader>
  <CardContent className="flex flex-col gap-4">
    <TextField label="Default hourly rate (€)" defaultValue="95" />
    <TextField label="Proposal validity (days)" defaultValue="30" />
  </CardContent>
  <CardFooter>
    <Button variant="ghost">Cancel</Button>
    <Button>Save changes</Button>
  </CardFooter>
</Card>

Content only

CardContent has no top padding by design (header owns the top edge). Without CardHeader, add `pt-6` on the first CardContent — or prefer CardHeader + CardTitle for titled sections.

<Card>
  <CardContent className="pt-6">…</CardContent>
</Card>

Literal anatomy

Static component anatomy; bind events in the consuming app.

<Card><CardHeader><CardTitle>Draft</CardTitle><CardAction><Button variant="ghost">Edit</Button></CardAction></CardHeader><CardContent>Ready for review.</CardContent></Card>

Stored brand preview

Only the preview banner uses the stored business fill and contrasting text. Ordinary panels retain semantic surfaces.

<Card accent={{ color: moduleColor, width: 3 }}>
  <CardHeader colors={{ background: brandFill, foreground: contrastingText }}>
    <CardTitle>Proposal</CardTitle>
  </CardHeader>
  <CardContent>Proposal details</CardContent>
</Card>

Related components