Card

Free · MIT

Eight surfaces (elevated, glass, gradient border, neo, inset…) with shadow, padding, radius and accent stripes, plus hover layers (lift, 3D tilt, pointer spotlight, glow, scale) and a looping border beam.

MarkdownOpen in v0
Open in v0

Installation

$ npx shadcn@latest add https://goodui.dev/r/card.json

The file lands in components/goodui/card.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/card works too.

Usage

import { CalendarPlus } from "lucide-react"
import { Card, CardFooter } from "@/components/goodui/card"
import { Badge } from "@/components/goodui/badge"
import { Button } from "@/components/goodui/button"

export function NextBooking() {
  return (
    <Card
      className="max-w-sm"
      accent="left"
      tone="success"
      icon={<CalendarPlus />}
      title="Balayage with Maya"
      description="Tomorrow, 09:30 · Chair 2"
      action={<Badge tone="success">Paid</Badge>}
      footer={
        <>
          <Button size="sm">Check in</Button>
          <Button size="sm" variant="ghost" tone="neutral">
            Reschedule
          </Button>
        </>
      }
    >
      2 hours · $180 deposit received. Client asked for a patch test.
    </Card>
  )
}

When to use

Use it for
  • Grouping one thing on a dashboard or settings page: a plan, a booking, a client, a report.
  • Pricing and feature grids where one card should stand out (accent, gradient-border, shadow="glow").
  • Clickable tiles that need hover feedback (lift, tilt, spotlight) without another library.
  • Empty states and drop zones: variant="dashed".
Reach for something else
  • A single KPI with a delta and sparkline: use stat-card, which is built on the same look.
  • Content that opens into a bigger view: use expandable-card or morphing-dialog.
  • Splitting sections of one long form: a separator keeps the page lighter than a stack of cards.
If you needUse
Default dashboard surfacevariant="elevated" (default) or "outline" for flat UIs
Grouped settings inside a page that is already a cardvariant="inset" or "soft"
The recommended pricing planvariant="gradient-border" or accent="top" with shadow="glow"
Status or category color on a list of cardsaccent="left" with a tone
Clickable tiles in a gridanimation="lift" (calm) or "spotlight" (pointer-following border light)
Hero or showcase card on a marketing pageanimation="tilt" or "border-beam"
Over photos or gradientsvariant="glass"
Empty state or upload areavariant="dashed"

Examples

Featured pricing plan

<Card variant="gradient-border" shadow="glow" animation="lift" title="Studio" description="For teams up to 10 stylists">
  <p className="text-3xl font-semibold">$29<span className="text-sm text-muted-foreground">/month</span></p>
  <CardFooter separated>
    <Button className="w-full">Start free trial</Button>
  </CardFooter>
</Card>

Media card with parts

<Card padding="sm" animation="spotlight" tone="info">
  <CardMedia>
    <img src="/reports/october.png" alt="Revenue chart for October" />
  </CardMedia>
  <CardHeader>
    <CardTitle>October revenue</CardTitle>
    <CardDescription>Up 12% on September</CardDescription>
  </CardHeader>
</Card>

API reference

Card

PropTypeDefault
variant
Surface: elevated (default), outline, soft tint, frosted glass, gradient border, neo-brutalist, inset well or dashed.
"elevated" | "outline" | "soft" | "glass" | "gradient-border" | "neo" | "inset" | "dashed""elevated"
tone
Color for the accent stripe, glow, soft tint, gradient border, hover light and icon tile.
"primary" | "neutral" | "success" | "warning" | "danger" | "info""primary"
shadow
Drop shadow; glow uses the tone color. Ignored by neo, which has its own offset shadow.
"none" | "sm" | "md" | "lg" | "xl" | "glow""none"
animation
Motion layer. lift, scale and glow react to hover; tilt and spotlight follow the pointer; border-beam is not a hover effect: it loops a light around the border continuously (off with reduced motion).
"none" | "lift" | "tilt" | "spotlight" | "glow" | "border-beam" | "scale""none"
padding
Inner spacing and gap between parts; CardMedia and separated footers bleed by this amount.
"none" | "sm" | "md" | "lg""md"
radius"none" | "sm" | "md" | "lg" | "xl" | "2xl""lg"
accent
A tone-colored stripe on the top or left edge.
"none" | "top" | "left""none"
fullWidth
false shrinks the card to its content.
booleantrue
title
Shorthand: renders a CardHeader with this title.
React.ReactNode—
description
Shorthand: muted line under the title.
React.ReactNode—
icon
Shorthand: icon in a tinted tile beside the title.
React.ReactNode—
action
Shorthand: element on the right of the header (a badge, a menu button).
React.ReactNode—
footer
Shorthand: renders a CardFooter.
React.ReactNode—

Also accepts every <div> attribute.

CardHeader

PropTypeDefault
icon
Icon shown in a tinted tile on the left.
React.ReactNode—
action
Element on the right (badge, menu, button).
React.ReactNode—

Also accepts every <div> attribute.

CardTitle

Also accepts every <h3> attribute.

CardDescription

Also accepts every <p> attribute.

CardContent

Also accepts every <div> attribute.

CardFooter

PropTypeDefault
separated
Full-bleed top border above the footer.
boolean—

Also accepts every <div> attribute.

CardMedia

Also accepts every <div> attribute.

Accessibility

  • Renders a plain <div>; the title shorthand and CardTitle render an <h3>. If your heading order needs another level, put your own heading inside CardHeader instead of CardTitle.
  • The header icon tile is aria-hidden; put the meaning in the title.
  • A focus ring is styled for keyboard focus, but the card is not focusable by default. If the whole card is clickable, put a real <a> or <button> inside rather than adding onClick to the div.

Motion

  • lift: rises 4px with a deeper shadow in 250 ms (neo cards shift up-left into a bigger offset shadow). scale: grows to 1.02 in 250 ms. glow: a tone-colored ring and bloom fade in over 300 ms.
  • tilt: rotates up to 4° on X and 5° on Y toward the pointer with a soft glare, and eases back in 500 ms on leave.
  • spotlight: a tone-colored light and a brighter border follow the pointer and fade in over 300 ms.
  • border-beam: a light runs around the border every 4 s, all the time, not only on hover.
  • Touch pointers do not trigger tilt or spotlight.
  • Reduced motion: transforms and transitions are off, so lift, scale and tilt do not move and border-beam is hidden. Spotlight and glow still appear, without fading.
  • tilt and spotlight write CSS variables on pointermove (no React re-render). border-beam animates a registered @property angle, so keep it to one or two cards per screen.

Notes for AI

Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.

  • Layers are independent props: variant, tone, shadow, animation, padding, radius, accent. tone colors accent, glow, soft tint, gradient-border, spotlight, beam and the icon tile.
  • For the common layout use the shorthand props (title, description, icon, action, footer). Use the parts (CardHeader, CardTitle, CardDescription, CardContent, CardFooter, CardMedia) for custom layouts; do not mix the shorthand header with a second CardHeader.
  • A string child is wrapped in CardContent only when a header is present; other children render as they are.
  • CardMedia and CardFooter separated go full-bleed using the card padding, so they need padding other than "none".
  • shadow is ignored on variant="neo", which has its own offset shadow. fullWidth={false} shrinks the card to its content.

FAQ

Is it compatible with shadcn's Card?

The parts have the same names (CardHeader, CardTitle, CardDescription, CardContent, CardFooter) and data-slot attributes, plus CardMedia and the layer props. CardAction is replaced by the action prop on Card or CardHeader.

Why does my tilt card not move on a phone?

Tilt and spotlight ignore touch pointers on purpose, so scrolling a list of cards does not wobble them.

Related