Overview
The Card component is a versatile container for grouping related content. Built with class-variance-authority (cva) and semantic design tokens, it provides a composable API with header, content, and footer sections.
Installation
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter,
} from "@/components/ui/Card";
Anatomy
The Card component is composed of multiple sub-components:
- Card: Root container that manages context
- CardHeader: Optional header section with spacing
- CardTitle: Title heading (h3 by default, customizable)
- CardDescription: Secondary text for descriptions
- CardContent: Main content area that grows to fill space
- CardFooter: Optional footer with border and actions
Basic Usage
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>
Card description goes here to describe the content.
</CardDescription>
</CardHeader>
<CardContent>
<p>This is the main content of the card.</p>
</CardContent>
<CardFooter>
<Button variant="ghost" size="sm">Cancel</Button>
<Button size="sm">Save</Button>
</CardFooter>
</Card>
Variants
The Card supports multiple visual variants:
Outlined (Default)
Standard card with a border.
<Card variant="outlined">
<CardHeader>
<CardTitle>Outlined Card</CardTitle>
<CardDescription>This card has a border outline</CardDescription>
</CardHeader>
<CardContent>
<p>Content for the outlined card variant.</p>
</CardContent>
</Card>
Elevated
Card with shadow elevation and no border.
<Card variant="elevated">
<CardHeader>
<CardTitle>Elevated Card</CardTitle>
<CardDescription>This card has a shadow elevation</CardDescription>
</CardHeader>
<CardContent>
<p>Content for the elevated card variant with shadow.</p>
</CardContent>
</Card>
Interactive
Card with hover effects and cursor pointer for clickable cards.
<Card variant="interactive">
<CardHeader>
<CardTitle>Interactive Card</CardTitle>
<CardDescription>This card responds to hover and click</CardDescription>
</CardHeader>
<CardContent>
<p>Hover over this card to see the interactive effects.</p>
</CardContent>
</Card>
Sizes
Three size options control padding throughout the card:
{/* Small - Compact spacing */}
<Card size="sm">
<CardHeader>
<CardTitle>Small Card</CardTitle>
</CardHeader>
<CardContent>
<p>Less padding for tight spaces.</p>
</CardContent>
</Card>
{/* Medium - Default size */}
<Card size="md">
<CardHeader>
<CardTitle>Medium Card</CardTitle>
</CardHeader>
<CardContent>
<p>Standard padding for most use cases.</p>
</CardContent>
</Card>
{/* Large - Generous spacing */}
<Card size="lg">
<CardHeader>
<CardTitle>Large Card</CardTitle>
</CardHeader>
<CardContent>
<p>More padding for comfortable reading.</p>
</CardContent>
</Card>
Props
Card
Visual style variant: "default" | "outlined" | "elevated" | "interactive"
Size of the card affecting padding: "sm" | "md" | "lg"
Additional CSS classes to apply
When true, expands header to card edges (useful for images)
Override the card’s size for this header: "sm" | "md" | "lg"
Additional CSS classes to apply
CardTitle
HTML heading element: "h1" | "h2" | "h3" | "h4" | "h5" | "h6"
Additional CSS classes to apply
CardDescription
Additional CSS classes to apply
CardContent
Additional CSS classes to apply
Override the card’s size for this footer: "sm" | "md" | "lg"
Additional CSS classes to apply
Advanced Examples
<Card>
<CardHeader>
<CardTitle>Sign In</CardTitle>
<CardDescription>
Enter your credentials to access your account
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Input
label="Email"
type="email"
placeholder="Enter your email"
/>
<Input
label="Password"
type="password"
placeholder="Enter your password"
/>
</CardContent>
<CardFooter>
<Button variant="ghost" className="flex-1">
Forgot password?
</Button>
<Button className="flex-1">Sign In</Button>
</CardFooter>
</Card>
Product Card
<Card>
<CardHeader fullWidth>
<div className="aspect-video bg-gradient-to-br from-blue-400 to-purple-500 rounded-t-lg" />
</CardHeader>
<CardHeader>
<div className="flex justify-between items-start">
<div>
<CardTitle as="h2">Amazing Product</CardTitle>
<CardDescription>
High-quality product with excellent features
</CardDescription>
</div>
<Button variant="ghost" size="sm">
<MoreHorizontal size={16} />
</Button>
</div>
</CardHeader>
<CardContent>
<div className="flex items-center gap-2">
<div className="flex">
{[...Array(5)].map((_, i) => (
<Star key={i} size={16} className="fill-yellow-400 text-yellow-400" />
))}
</div>
<span className="text-sm [color:var(--text-secondary)]">
(128 reviews)
</span>
</div>
</CardContent>
<CardFooter>
<div className="flex items-center gap-2 flex-1">
<span className="text-2xl font-bold [color:var(--text-primary)]">
$99
</span>
<span className="text-sm [color:var(--text-tertiary)] line-through">
$129
</span>
</div>
<div className="flex gap-2">
<Button variant="ghost" size="sm">
<Heart size={16} />
</Button>
<Button size="sm">Add to Cart</Button>
</div>
</CardFooter>
</Card>
Event Card
<Card variant="interactive">
<CardHeader>
<CardTitle>Design Conference 2024</CardTitle>
<CardDescription>
Join us for an amazing conference about modern design
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex items-center gap-2">
<Calendar size={16} className="[color:var(--text-secondary)]" />
<span className="[color:var(--text-secondary)]">
March 15-17, 2024
</span>
</div>
<div className="flex items-center gap-2">
<MapPin size={16} className="[color:var(--text-secondary)]" />
<span className="[color:var(--text-secondary)]">
San Francisco, CA
</span>
</div>
</div>
</CardContent>
<CardFooter>
<div className="flex items-center gap-2 flex-1">
<span className="text-lg font-semibold [color:var(--text-primary)]">
$299
</span>
<span className="text-sm [color:var(--text-tertiary)]">
Early bird
</span>
</div>
<Button>Register Now</Button>
</CardFooter>
</Card>
Design Tokens
The Card component uses semantic design tokens for consistent theming:
--card-radius: Border radius
--card-padding-sm/md/lg: Size-specific padding
--bg-primary: Card background
--border-primary: Card border color
--text-primary/secondary: Text colors
--shadow-md/lg: Elevation shadows
--transition-normal: Animation duration
Implementation Details
Context Management
The Card component uses React Context to share size and footer state between sub-components:
- Size is automatically inherited by CardHeader and CardFooter
- CardFooter automatically updates the Card’s padding when rendered
- This ensures proper spacing without manual prop passing
Use the fullWidth prop on CardHeader to create edge-to-edge headers (perfect for images):
<Card>
<CardHeader fullWidth>
<img src="/image.jpg" alt="Header image" className="rounded-t-lg" />
</CardHeader>
{/* Rest of card */}
</Card>
The CardFooter uses justify-between by default, making it perfect for action buttons:
<CardFooter>
<Button variant="ghost">Cancel</Button>
<Button>Save</Button>
</CardFooter>
Accessibility
- Use semantic heading levels with the
as prop on CardTitle
- Ensure sufficient color contrast for text content
- Add appropriate ARIA labels to interactive cards
- Use CardDescription to provide context for screen readers
Best Practices
- Use consistent sizing: Keep card sizes consistent within the same layout
- Interactive variant for clickable cards: Use
variant="interactive" when the entire card is clickable
- Proper heading hierarchy: Set appropriate heading levels with the
as prop
- Avoid nesting cards: Cards should not be nested within other cards
- Footer for actions: Use CardFooter for primary actions related to the card content