Card
The card groups related content on one surface, like a login form, a stat, or a settings section.
<div class="card">
<div class="card-header">
<h3 class="card-title">Login to your account</h3>
<p class="card-description">
Enter your email below to login to your account
</p>
<div class="card-action">
<button class="btn btn-link">Sign Up</button>
</div>
</div>
<div class="card-content">
<form class="flex flex-col gap-6">
<div class="field">
<label class="label" for="card-demo-email">Email</label>
<input
class="input"
id="card-demo-email"
type="email"
placeholder="[email protected]"
required
/>
</div>
<div class="field">
<div class="flex w-full items-center">
<label class="label" for="card-demo-password">Password</label>
<a
href="#"
class="ml-auto inline-block text-sm underline-offset-4 hover:underline"
>
Forgot your password?
</a>
</div>
<input class="input" id="card-demo-password" type="password" required />
</div>
</form>
</div>
<div class="card-footer flex-col gap-2">
<button type="submit" class="btn w-full">Login</button>
<button class="btn btn-outline w-full">Login with Google</button>
</div>
</div>Usage
Add the card class to the surface and compose the sections inside.
<div class="card">
<div class="card-header">
<h3 class="card-title">Card Title</h3>
<p class="card-description">Card Description</p>
<div class="card-action">Card Action</div>
</div>
<div class="card-content">Card Content</div>
<div class="card-footer">Card Footer</div>
</div>Examples
Size
Add card-sm for a more compact card: the spacing tightens and the title drops a step.
<div class="card card-sm mx-auto w-full max-w-xs">
<div class="card-header">
<h3 class="card-title">Scheduled reports</h3>
<p class="card-description">Weekly snapshots. No manual exports.</p>
</div>
<div class="card-content">
<ul class="grid gap-2 py-2 text-sm">
<li class="flex gap-2">
<svg class="mt-0.5 size-4 shrink-0 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
<span>Choose a schedule (daily, or weekly).</span>
</li>
<li class="flex gap-2">
<svg class="mt-0.5 size-4 shrink-0 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
<span>Send to channels or specific teammates.</span>
</li>
<li class="flex gap-2">
<svg class="mt-0.5 size-4 shrink-0 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
<span>Include charts, tables, and key metrics.</span>
</li>
</ul>
</div>
<div class="card-footer flex-col gap-2">
<button class="btn btn-sm w-full">Set up scheduled reports</button>
<button class="btn btn-outline btn-sm w-full">See what's new</button>
</div>
</div>Spacing
Set --card-spacing to control the section gaps and edge insets in one place.
<div class="btn-group mx-auto" role="group" aria-label="Card spacing">
<button
class="btn btn-outline btn-sm aria-pressed:bg-accent aria-pressed:text-accent-foreground"
data-spacing="4"
aria-pressed="false"
>
16px
</button>
<button
class="btn btn-outline btn-sm aria-pressed:bg-accent aria-pressed:text-accent-foreground"
data-spacing="5"
aria-pressed="false"
>
20px
</button>
<button
class="btn btn-outline btn-sm aria-pressed:bg-accent aria-pressed:text-accent-foreground"
data-spacing="6"
aria-pressed="true"
>
24px
</button>
<button
class="btn btn-outline btn-sm aria-pressed:bg-accent aria-pressed:text-accent-foreground"
data-spacing="8"
aria-pressed="false"
>
32px
</button>
</div>
<div class="card" id="card-spacing-demo">
<div class="card-header">
<h3 class="card-title">Login to your account</h3>
<p class="card-description">
Enter your email below to login to your account
</p>
<div class="card-action">
<button class="btn btn-link">Sign Up</button>
</div>
</div>
<div class="card-content">
<form class="flex flex-col gap-6">
<div class="field">
<label class="label" for="card-spacing-email">Email</label>
<input
class="input"
id="card-spacing-email"
type="email"
placeholder="[email protected]"
required
/>
</div>
<div class="field">
<div class="flex w-full items-center">
<label class="label" for="card-spacing-password">Password</label>
<a
href="#"
class="ml-auto inline-block text-sm underline-offset-4 hover:underline"
>
Forgot your password?
</a>
</div>
<input
class="input"
id="card-spacing-password"
type="password"
required
/>
</div>
</form>
</div>
<div class="card-footer flex-col gap-2">
<button type="submit" class="btn w-full">Login</button>
<button class="btn btn-outline w-full">Login with Google</button>
</div>
</div>
<script>
const card = document.getElementById("card-spacing-demo");
const buttons = document.querySelectorAll("[data-spacing]");
buttons.forEach((button) => {
button.addEventListener("click", () => {
buttons.forEach((b) =>
b.setAttribute("aria-pressed", String(b === button)));
card.style.setProperty(
"--card-spacing",
`calc(var(--spacing) * ${button.dataset.spacing})`,
);
});
});
</script>Edge to edge
Use negative margins with -mx-(--card-spacing) to run content to the card edges while keeping it aligned with the inset.
<div class="card">
<div class="card-header">
<h3 class="card-title">Terms of Service</h3>
<p class="card-description">
Review the terms before accepting the agreement.
</p>
</div>
<div class="card-content">
<div
class="-mx-(--card-spacing) max-h-48 space-y-4 overflow-y-scroll border-y bg-muted/50 px-(--card-spacing) py-4 text-sm leading-relaxed"
>
<p>
These terms govern your use of the workspace, including access to shared
documents, project files, and collaboration tools.
</p>
<p>
You are responsible for the content you upload and for ensuring that
your team has the appropriate permissions to view or edit it.
</p>
<p>
We may update features or limits as the service evolves. When those
changes materially affect your workflow, we will notify your workspace
administrators.
</p>
<p>
By continuing, you agree to keep your account credentials secure and to
follow your organization's acceptable use policies.
</p>
</div>
</div>
<div class="card-footer justify-end gap-2">
<button class="btn btn-outline">Decline</button>
<button class="btn">Accept</button>
</div>
</div>Image
Add an image before the card header to create a card with an image.
<div class="card relative pt-0">
<div class="absolute inset-0 z-30 aspect-video bg-black/35"></div>
<img
src="https://avatar.vercel.sh/shadcn1"
alt="Event cover"
class="relative z-20 aspect-video w-full object-cover brightness-60 grayscale dark:brightness-40"
/>
<div class="card-header">
<div class="card-action">
<span class="badge badge-secondary">Featured</span>
</div>
<h3 class="card-title">Design systems meetup</h3>
<p class="card-description">
A practical talk on component APIs, accessibility, and shipping faster.
</p>
</div>
<div class="card-footer">
<button class="btn w-full">View Event</button>
</div>
</div>CSS variables
The card's spacing rhythm is one component-local variable: the card derives its vertical padding and section gap from it, and each section its horizontal inset. Override it per instance with an arbitrary property, or for the whole app with a plain CSS rule.
<div class="card [--card-spacing:--spacing(8)]"></div>