A composable content surface with compact spacing, an action slot, and optional interactive feedback.
import { ArrowUpRight, TrendingUp } from "lucide-react"
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardAction,
CardContent,
CardFooter,
} from "@/components/ballmac/card"
import { Badge } from "@/components/ballmac/badge"
export default function CardDemo() {
return (
<Card interactive className="w-full max-w-sm">
<CardHeader>
<CardTitle>Monthly overview</CardTitle>
<CardDescription>Activity across your workspace</CardDescription>
<CardAction>
<TrendingUp aria-hidden="true" className="size-5 text-chart-2" />
</CardAction>
</CardHeader>
<CardContent>
<div className="text-3xl font-semibold tabular-nums">12,840</div>
<div className="mt-2 flex items-center gap-2">
<Badge status="success">+12.8%</Badge>
<span className="text-xs text-muted-foreground">from last month</span>
</div>
</CardContent>
<CardFooter>
<a
href="#analytics"
className="inline-flex items-center gap-1 rounded-sm text-sm font-medium text-primary outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
>
View analytics <ArrowUpRight aria-hidden="true" className="size-4 rtl:-scale-x-100" />
</a>
</CardFooter>
</Card>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/cardCopy the source into your project.
components/ballmac/card.tsx// Ballmac UI: Card. https://ui.ballmac.com/components/card // Based on shadcn/ui Card (MIT, Copyright (c) 2023 shadcn), adding compact spacing and a restrained interactive surface. import * as React from "react" import { cn } from "@/lib/utils" type CardProps = React.ComponentProps<"div"> & { /** Compact spacing for dense dashboards. */ size?: "default" | "sm" /** Add a hover and focus treatment when the card contains a link or control. */ interactive?: boolean } function Card({ className, size = "default", interactive = false, ...props }: CardProps) { return ( <div data-slot="card" data-size={size} className={cn( "group/card bg-card text-card-foreground flex min-w-0 flex-col gap-5 rounded-xl border border-border py-5 shadow-sm transition-[border-color,box-shadow,transform] duration-200 data-[size=sm]:gap-3 data-[size=sm]:py-4", interactive && "hover:border-ring/50 hover:shadow-md focus-within:border-ring/50 motion-safe:hover:-translate-y-0.5 motion-reduce:transition-none", className, )} {...props} /> ) } function CardHeader({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="card-header" className={cn( "grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-start gap-x-3 gap-y-1 px-5 group-data-[size=sm]/card:px-4", className, )} {...props} /> ) } function CardTitle({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="card-title" className={cn("col-start-1 text-base font-semibold leading-6", className)} {...props} /> ) } function CardDescription({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="card-description" className={cn( "text-muted-foreground col-start-1 text-sm leading-relaxed", className, )} {...props} /> ) } function CardAction({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="card-action" className={cn("col-start-2 row-span-2 flex items-start gap-2", className)} {...props} /> ) } function CardContent({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="card-content" className={cn("min-w-0 px-5 group-data-[size=sm]/card:px-4", className)} {...props} /> ) } function CardFooter({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="card-footer" className={cn( "flex flex-wrap items-center gap-2 border-t border-border px-5 pt-4 group-data-[size=sm]/card:px-4", className, )} {...props} /> ) } export { Card, CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter, type CardProps, }Update the import paths to match your project setup.
Usage
import { Card, CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter } from "@/components/ballmac/card"The full example is in the Code tab above.
Examples
States and variants
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
} from "@/components/ballmac/card"
export default function CardStates() {
return (
<div className="grid w-full max-w-md gap-3 sm:grid-cols-2">
<Card size="sm">
<CardHeader>
<CardTitle>Active projects</CardTitle>
<CardDescription>Across all teams</CardDescription>
</CardHeader>
<CardContent>
<span className="text-2xl font-semibold tabular-nums">24</span>
</CardContent>
</Card>
<Card size="sm">
<CardHeader>
<CardTitle>Tasks due</CardTitle>
<CardDescription>In the next seven days</CardDescription>
</CardHeader>
<CardContent>
<span className="text-2xl font-semibold tabular-nums">8</span>
</CardContent>
</Card>
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
sizeCompact spacing for dense dashboards. | "default" | "sm" | "default" |
interactiveAdd a hover and focus treatment when the card contains a link or control. | boolean | false |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| Tab | reaches controls and links inside the card |
Use with AI
A composable content surface with compact spacing, an action slot, and optional interactive feedback. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Card (@ballmac/card) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Group a related set of content and actions
- Present a dashboard metric or settings panel
Not for
- Use an alert for status messages
Registry JSON: https://ui.ballmac.com/r/card.json
Credits
Based on shadcn/ui Card, adapted by Ballmac. Free to use in personal and commercial projects.
- npm
- None
- Registry
- shadcn/utils
Pairs well with
Button
A button with six variants, three sizes, a pill shape and a built-in loading state. buttonVariants() styles links the same way.
Badge
A small pill label in four variants with an optional status dot and success, warning and error tones. Style links with badgeVariants().
Activity Feed
A compact actor and action feed with timestamps and an empty state.
Audio Player
A podcast-style audio player: play and pause, skip back and forward, a scrubber with chapter ticks, a named current chapter and a speed control. Works with a real file or as a silent demo.