Browse docs
Browse docs
Wrap content in a surface. Make it clickable. Show selection state.
import { Card, CardContent } from '@dashforge/tw';
<Card>
<CardContent>
<h3>Card title</h3>
<p>Card body.</p>
</CardContent>
</Card><Card variant="outlined">
<CardContent>Outlined surface.</CardContent>
</Card>
<Card variant="elevated">
<CardContent>Elevated surface (shadow).</CardContent>
</Card>
<Card variant="plain">
<CardContent>Plain surface (no border, no shadow).</CardContent>
</Card>import { Card, CardContent, Stack } from '@dashforge/tw';
<Stack direction="row" gap={3} wrap>
<Card variant="outlined">
<CardContent>Outlined — border, no shadow.</CardContent>
</Card>
<Card variant="elevated">
<CardContent>Elevated — shadow, no border.</CardContent>
</Card>
<Card variant="plain">
<CardContent>Plain — no border, no shadow.</CardContent>
</Card>
</Stack><CardContent> is a padded inner Box (p=4 default). Compose freely:
import { Card, CardContent, Button, Stack } from '@dashforge/tw';
<Card>
<CardContent>
<Stack gap={2}>
<div>
<div style={{ fontSize: 12, opacity: 0.6 }}>CUSTOMER</div>
<div style={{ fontSize: 18, fontWeight: 600 }}>ACME Inc.</div>
<div style={{ fontSize: 13, opacity: 0.7 }}>Enterprise plan</div>
</div>
<Stack direction="row" gap={2}>
<Button size="sm" variant="solid">View</Button>
<Button size="sm" variant="outline">Archive</Button>
</Stack>
</Stack>
</CardContent>
</Card>Wrap interactive content in <CardActionArea> to make the whole card a single clickable surface — with focus ring, hover state, and full keyboard support.
<Card>
<CardActionArea onClick={() => navigate('/customer/123')}>
<CardContent>
<h3>ACME Inc.</h3>
<p>Tap to view details.</p>
</CardContent>
</CardActionArea>
</Card><CardActionArea> uses Radix Slot — pass asChild to render onto another element (e.g. a router Link):
<Card>
<CardActionArea asChild>
<Link to="/customer/123">
<CardContent>
<h3>ACME Inc.</h3>
</CardContent>
</Link>
</CardActionArea>
</Card>Pass selected to toggle aria-pressed — useful for filter-card UX.
import { useState } from 'react';
import { Card, CardActionArea, CardContent, Stack } from '@dashforge/tw';
function PlanPicker() {
const [selected, setSelected] = useState('pro');
return (
<Stack direction="row" gap={3}>
{['starter', 'pro', 'enterprise'].map((plan) => (
<Card key={plan}>
<CardActionArea selected={selected === plan} onClick={() => setSelected(plan)}>
<CardContent>{plan}</CardContent>
</CardActionArea>
</Card>
))}
</Stack>
);
}<Card> is a thin opinionated <Box> alias — all Box props pass through. Override p, rounded, elevation, etc. directly on Card:
<Card p="lg" rounded="xl" elevation={3}>
<CardContent p="md">Heavily padded card with bigger radius + shadow.</CardContent>
</Card><Box> got access + visibleWhen in Sprint 4.4 — <Card> inherits both transparently. Use cases: admin-only revenue cards, role-gated dashboard tiles.
<Card access={{ requires: 'finance.view', when: 'denied:hide' }}>
<CardContent>
<h3>Monthly revenue</h3>
<p className="text-3xl font-bold">€48,250</p>
</CardContent>
</Card><Card visibleWhen={(engine) => engine.getValue('user.role') === 'admin'}>
<CardContent>Admin-only tile.</CardContent>
</Card><Card> props<Card> is a thin alias of <Box> — all Box props apply (p, m, bg, rounded, elevation, access, visibleWhen, ...). The card-specific defaults:
| Default | Value |
|---|---|
variant | 'outlined' |
rounded | 'lg' |
elevation | 1 |
p | 0 |
The variant prop is narrowed to 'outlined' \| 'elevated' \| 'plain' (a subset of the full Box variant set).
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'outlined' | 'elevated' | 'plain' | 'outlined' | Card-specific variant. Narrowed from Box. |
| ...rest | Box props | — | All Box props (p, m, bg, rounded, elevation, access, visibleWhen, className, sx, etc.). |
<CardContent> propsPadded inner Box wrapper. Default p='md'. All Box props apply.
<CardActionArea> propsSlot-polymorphic clickable wrapper.
| Prop | Type | Default | Description |
|---|---|---|---|
onClick | () => void | — | Click handler. |
selected | boolean | false | Toggles aria-pressed. |
disabled | boolean | false | Disables clicks and focus. |
asChild | boolean | false | Render onto child element (Radix Slot). |
access | AccessSpec | — | RBAC gating. |
visibleWhen | (engine: Engine) => boolean | — | Engine-reactive predicate. |
className | string | — | Merged via tailwind-merge. |
<Card> alone (with raw children) works fine for one-off layouts. <CardContent> is for the standard padded interior. <CardActionArea> is only when the whole card is interactive.access + visibleWhen come from Box (Sprint 4.4 retrofit). Same contract as the rest of the catalog.@dashforge/ui wraps @mui/material/Card directly. Same compositional pattern.