Task: Card / Surface Component
Type: Component
Milestone: M0.5 — Shared Component Library
Estimate: S
Component Type
Props Interface
interface CardProps {
children: ReactNode;
header?: ReactNode; // top slot — title, badges
footer?: ReactNode; // bottom slot — actions, pagination
meta?: ReactNode; // sub-header slot — timestamps, reporter
variant?: 'default' | 'danger' | 'warning' | 'success' | 'info';
padding?: 'none' | 'sm' | 'md' | 'lg';
isClickable?: boolean;
isExpanded?: boolean; // stronger border + shadow when true
onClick?: (e: MouseEvent<HTMLDivElement>) => void;
className?: string;
testId?: string;
}
Variants / States
| State |
Description |
| Default |
Neutral border, white/primary bg |
| Danger / Warning / Success / Info |
Tinted bg + matching border |
| Expanded |
--border-primary + --shadow-md — used by ReportCard when open |
| Clickable hover |
--border-secondary on hover, pointer cursor |
| No header/footer |
Slots simply don't render — no empty padding divs left behind |
| Padding variants |
none / sm / md / lg — for different density contexts |
Acceptance Criteria
Notes
ReportCard is built on top of this — validate the slot API against ReportCard's needs before finalising
- The
footer slot is where ActionButtons renders — spacing and flex wrap must work for 2–4 buttons
Task: Card / Surface Component
Type: Component
Milestone: M0.5 — Shared Component Library
Estimate: S
Component Type
Props Interface
Variants / States
--border-primary+--shadow-md— used by ReportCard when open--border-secondaryon hover, pointer cursornone / sm / md / lg— for different density contextsAcceptance Criteria
header,footer,metaslots render only when provided — no empty<div>ghost nodesfooterslot always has a top divider separatorisExpandedapplies stronger border and shadowisClickableadds hover border and pointer cursor — only when truepaddingvariants apply correctly to body, not header/footer (they have fixed px-4)ReportCardbuilt on top of it must not need any layout overridesNotes
ReportCardis built on top of this — validate the slot API againstReportCard's needs before finalisingfooterslot is whereActionButtonsrenders — spacing and flex wrap must work for 2–4 buttons