Clickable Card
Card as one fully clickable element — dashboard tile, list card, navigation tile. Card automatically renders as <a> or <button> depending on the prop; nested <a><Card></Card></a> is unnecessary (and usually wrong).
Live Preview
Dashboard tile with navigation (href)
List card with action (onclick)
Navigation tile with icon (href + icon)
Features
- href automatically sets the rendered element to <a> with correct tab order
- onclick sets it to <button type="button"> for actions without navigation
- Hover/focus styles kick in automatically once href/onclick/clickable is set — no "decorative hover" mode on passive elements
- mint="translate" or mint="glow" for a gentle micro-interaction
- Anti-pattern comparison: why <a><Card></Card></a> is the worse solution
Code
Stat tile with href (renders as <a>)
<Card variant="outlined" padding="md" href="/revenue" mint="translate">
<div class="flex items-start justify-between">
<div>
<p class="text-text-tertiary text-xs font-medium">Revenue</p>
<p class="text-text-primary text-2xl font-semibold tabular-nums">€42,150</p>
<p class="text-success text-xs">+12.4% vs. last month</p>
</div>
<ArrowRightIcon class="text-text-tertiary h-4 w-4" />
</div>
</Card>List card with onclick (renders as <button>)
<Card
variant="outlined"
padding="sm"
onclick={() => navigateTo(user.id)}
>
<div class="flex items-center gap-3">
<Avatar name={user.name} size="sm" />
<div class="min-w-0 flex-1">
<p class="text-text-primary text-sm font-medium">{user.name}</p>
<p class="text-text-tertiary text-xs">{user.role}</p>
</div>
</div>
</Card>Delegate click without onclick (clickable prop)
<!-- Render Card as a <button> without it carrying a handler itself —
for patterns where a wrapper component intercepts clicks (e.g. a
selection picker bound across multiple Card children).
Don't nest it inside an outer <a> — that produces
<a><button> nesting (an a11y violation). -->
<Card variant="outlined" padding="md" clickable>
...
</Card>Anti-pattern: wrapping Card in <a>
Don't
<a href="/revenue">
<Card variant="outlined" padding="md">
Content
</Card>
</a> - Card keeps
elementType="div"— no hover/focus styles - Inner inputs/links produce nested interactive elements (a11y violation)
- Two tab stops (outer a + card content) instead of one
- Screen readers announce "link, region, …" twice
Do
<Card
variant="outlined"
padding="md"
href="/revenue"
>
Content
</Card> - Card renders directly as
<a> - Hover/focus styles apply automatically
- One tab stop, one element for screen readers
- No conflicts with inner buttons (stop bubbling where needed)
Best Practices
href for navigation, onclick for actions
Routes, detail pages → href. Opening a modal,
changing a selection, server actions → onclick.
That way Cmd-click "open in new tab" works as expected.
Inner buttons: stopPropagation
If the card contains a secondary button (e.g. "Favorite"), end the button's onclick with event.stopPropagation() — otherwise the card fires too.
mint="translate" for a gentle hover animation
mint="translate" lifts the card slightly on
hover; mint="glow" adds a subtle glow. prefers-reduced-motion is respected.
clickable for delegated click handlers
When click behavior is handled by a wrapper component and the card should render as a
real button without carrying an onclick itself — set clickable without href/onclick. The card then
renders as <button type="button"> with interactive
styles. Don't combine it with an outer <a> or inner buttons — <a><Card clickable> produces nested interactive elements.
No "decorative hover"
Card deliberately has no mode with hover styles but no click source. A pointer cursor
plus a lift animation on a passive element violates WCAG 3.2 Predictable. If the card
sits inside an outer <a>, move the href onto the card (see the anti-pattern section).