Card
Container that groups related content and can render as a link or button.
Playground
<Card>
{#snippet header()}
<div class="font-semibold">Card Title</div>
<div class="text-text-tertiary text-xs">Optional subtitle</div>
{/snippet}
<div class="text-text-secondary text-sm">
Cards group related content. Change variant and padding to see different looks.
</div>
{#snippet footer()}
<div class="flex justify-end gap-2">
<Button variant="ghost" size="sm">Cancel</Button>
<Button variant="filled" intent="primary" size="sm">Confirm</Button>
</div>
{/snippet}
</Card>01 Variants
variant sets a card's visual weight, from a flat in-page tint
to a lifted, shadowed surface. Pick the lightest one that still reads as a distinct region.
variant | What it renders | Example |
|---|---|---|
quiet (default) | A barely-tinted fill, no border or shadow. The reading-flow default for grouping content in place. | Inline section |
outlined | A hairline border over a transparent fill. Delineates a region without lifting it off the page. | Grouped fields |
elevated | A shadow over a raised surface, no border. Lifts the card so it reads above the page. | Summary panel |
floating | A larger shadow over a raised surface, for popover-family surfaces above arbitrary backgrounds. | Popover surface |
02 Examples
A card's default content is its body. Add header and footer snippets for content above and below it, and dividers to draw a hairline between the three regions.
Set href to render the whole card as a link, or onclick to render it as a button.
Stat tile
bridge radius tier for a softer corner.Monthly Revenue
$48,210
<Card tier="bridge" padding="md">
<p class="text-text-tertiary text-xs font-medium tracking-wider uppercase">
Monthly Revenue
</p>
<p class="text-text-primary mt-2 text-3xl font-bold">$48,210</p>
<div class="mt-2 flex items-center gap-2">
<Badge size="xs" intent="success">+12.4%</Badge>
<span class="text-text-tertiary text-xs">vs. last month</span>
</div>
</Card>Profile card
dividers draws a hairline between the three regions for a structured look.Ada Lovelace
Product Designer
Working on the design-token pipeline and the component gallery. Based in London.
<Card variant="outlined" dividers>
{#snippet header()}
<div class="flex items-center gap-3">
<Avatar name="Ada Lovelace" randomColor />
<div>
<p class="text-text-primary font-semibold">Ada Lovelace</p>
<p class="text-text-tertiary text-xs">Product Designer</p>
</div>
</div>
<Badge size="xs" variant="soft" intent="success">Online</Badge>
{/snippet}
<p class="text-text-secondary text-sm">
Working on the design-token pipeline and the component gallery. Based in London.
</p>
{#snippet footer()}
<div class="flex justify-end gap-2">
<Button variant="ghost" size="sm">Message</Button>
<Button variant="filled" intent="primary" size="sm">Follow</Button>
</div>
{/snippet}
</Card>Clickable navigation card
href to render as <a>, or onclick to render as <button>. Hover and focus styles activate automatically. Pair with mint=scale for a subtle press affordance.<Card href="#documentation" mint="scale">
<div class="flex items-start gap-3">
<div
class="bg-primary/10 text-primary rounded-modify flex h-10 w-10 shrink-0 items-center justify-center"
>
<BookOpenIcon size={20} />
</div>
<div>
<p class="text-text-primary font-semibold">Read the documentation</p>
<p class="text-text-secondary mt-1 text-sm">
Browse component APIs, design tokens, and integration guides.
</p>
</div>
</div>
</Card>03 Customization
Glass pricing card
class tints the card into a glass look. It keeps the container radius tier, spacing and behaviour. Only the fill, border and blur change, using raw colours because glass has no token equivalent.Premium
$29/mo
- Unlimited projects
- Priority support
- Custom integrations
<Card
class="w-72 border border-white/20 bg-white/10 text-white shadow-[var(--blocks-shadow-lg)] backdrop-blur-xl"
>
<p class="text-sm font-medium tracking-wider text-white/70 uppercase">Premium</p>
<p class="mt-2 text-4xl font-bold">
$29<span class="text-lg font-normal text-white/60">/mo</span>
</p>
<ul class="mt-4 space-y-2 text-sm text-white/80">
{#each ['Unlimited projects', 'Priority support', 'Custom integrations'] as perk (perk)}
<li class="flex items-center gap-2"><CheckIcon size={16} /> {perk}</li>
{/each}
</ul>
<Button
class="mt-6 w-full border-transparent bg-white font-semibold text-fuchsia-600 hover:bg-white/90"
>
Subscribe
</Button>
</Card>This is one of five ways to restyle a block. See Customization for class, slotClasses, unstyled, preset and provider-level overrides.
04 Accessibility
Built-in ARIA
Cards render as <div> by default. When clickable or onclick is set, the card renders as a <button>. When href is provided, it becomes an <a>. Disabled cards set aria-disabled.
Keyboard
Interactive cards are focusable via Tab and show a focus ring. A card rendered as an <a> activates on Enter. A card rendered as a <button> (via clickable or onclick) activates on Enter or Space. Non-interactive cards are skipped in the tab
order.
Structure
Header and footer snippets render above and below the body in DOM order, so screen readers encounter them in the right sequence. Use a heading inside the header to give the card a scannable outline.
05 API Reference
19 propsProp | Type | Default | Description | |
|---|---|---|---|---|
children | Snippet | — | Main body content. | |
class | string | — | Extra classes merged onto the root element. | |
clickable | boolean | — | Force <button> rendering and interactive hover styles without
providing an onclick handler — useful when delegating clicks
through a wrapper component or library. onclick and href
already enable interactive styles automatically; reach for
clickable only when neither is appropriate.
Don't combine with an outer <a href> or inner interactive
content — <a><Card clickable>…</Card></a> produces nested
interactive elements (invalid HTML, a11y violation). Prefer
<Card href={…}> so the card itself becomes the anchor. | |
disabled variant | true | false | Controls the disabled behavior and appearance of the Card component. Available options: true. | |
dividers variant | true | false | Controls the dividers behavior and appearance of the Card component. Available options: true. | |
footer | Snippet | — | Content rendered below the body. With dividers, a hairline separates body from footer. | |
header | Snippet | — | Content rendered above the body. With dividers, a hairline separates header from body. | |
href | string | — | URL target. When provided, the card renders as <a>. | |
mint | MintProp | 'none' | Micro-interaction preset applied to the card. Only applies while the
card is interactive (clickable, onclick, or href) and not disabled. | |
onclick | (event: MouseEvent) => void | — | Click handler. When provided, the card renders as <button> and gains interactive styles. | |
onHover | (hovered: boolean) => void | — | Called when hover state changes. Receives true on mouse-enter, false on mouse-leave. | |
padding variant | lgmdnonesm +1 more | md | Controls the padding behavior and appearance of the Card component. Available options: lg, md, none, and 2 more. | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ Card: {...} }}>.
Prefer this over class overrides when the requested look falls outside the
semantic intent palette — presets keep hover/active/dark-mode logic coherent
and make the custom look reusable across the project.
A conditional overrides rule on a Card keys on its variant axes, and one
of those is not a prop: interactive is the axis clickable, onclick
and href all resolve to, so overrides: [{ interactive: true, … }]
styles every card that has a click source, whichever of the three gave it
one. It is deliberately not settable — a card made to look interactive
without one would be a <div> with cursor-pointer and a hover-lift and
no way to operate it (WCAG 3.2 Predictable). | |
slotClasses | Partial<Record<CardSlots, string>> | — | Per-slot class overrides. Slots: base | header | content | footer | |
tier variant | bridgecontain | contain | Selects the semantic radius tier of the Card — the shape family it belongs to (--radius-commit/-modify/-contain/-bridge). Shape is retuned per family in your theme, so this picks the family rather than a pixel value. Available options: bridge, contain. | |
unstyled | boolean | — | Remove all default tv classes. | |
variant variant | elevatedfloatingoutlinedquiet | quiet | Controls the visual style and presentation of the Card. Determines the component's visual treatment. Available options: elevated, floating, outlined, quiet. | |
...CardVariants variant | VariantProps | — | Styling variants from CardVariants | |
...HTMLAttributes<HTMLElement> inherited | HTMLAttributes | — | HTML attributes (excluding: 'children') |
06 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
CardProps | interface | props | 0 | — | |
CardVariants | type | variant | 0 | — | |
CardSlots | type | variant | 0 | Slot names derived from the tv() config — single source of truth for slotClasses. | |
MintProp | type | helper | 1 | — | |
MintName | type | helper | 0 | A mint name: a built-in (autocompleted), 'none' to disable, or any
consumer-registered name. (string & {}) keeps the registry open — a
custom name still type-checks, it just isn't suggested. A typo therefore
also still compiles (it resolves like an unregistered custom name and
warns at runtime); the union buys completion and docs, not validation. | |
MintConfig | interface | helper | 0 | — | |
BuiltinMintName | type | helper | 0 | Built-in mint names as a literal union, so the mint prop autocompletes
across every component — the single list the hand-curated playground knobs
and docs used to drift away from. |
07 Installation
Import
import { Card } from '@urbicon-ui/blocks';