Design System
Design System
Section titled “Design System”Quick reference for UI patterns. Read before adding UI components.
Critical Overrides
Section titled “Critical Overrides”Tooltips - Gray Background Required
Section titled “Tooltips - Gray Background Required”Default Radix tooltip is blue (bg-primary). Always override to gray:
<TooltipContent side="right" className="bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 text-gray-700 dark:text-gray-300">When to use what:
<InfoButton>- For help/info text next to form fields (shows ℹ️ icon, handles styling automatically)<TooltipContent>with override - For any other tooltip (action labels like “Rename model”, status text, etc.)
Default Value Highlighting - Yellow Background
Section titled “Default Value Highlighting - Yellow Background”When displaying values that haven’t been edited (still at defaults), use:
// Light mode: yellow, Dark mode: subtle amberclassName={cn(isDefault && "bg-amber-50 dark:bg-amber-950/30")}Components with isDefault prop: Input, Select, NumberInput, MonthPicker
Dropdown/Select Hover States - Use bg-muted
Section titled “Dropdown/Select Hover States - Use bg-muted”All dropdown items (Select, DropdownMenu) use focus:bg-muted for hover/focus state. Never use bg-accent (yellow) for menu item highlighting.
Reusable Components
Section titled “Reusable Components”InfoButton
Section titled “InfoButton”Info icon with properly styled tooltip. Supports rich content and optional source citations.
import { InfoButton } from "@/components/ui/info-button";
// Simple usage<InfoButton>Simple explanation text</InfoButton>
// With sources (for reference data fields)<InfoButton sources={sources} defaultValue={85}> Percentage of patients with healthcare access</InfoButton>
// Rich content<InfoButton variant="amber" size="md"> <div className="space-y-2"> <p className="font-semibold">Title</p> <ul className="list-disc pl-4"><li>Point 1</li></ul> </div></InfoButton>| Prop | Options | Default | Description |
|---|---|---|---|
variant | "default" | "amber" | "default" | Icon color variant |
size | "sm" | "md" | "sm" | Icon size |
sources | SourceEntry[] | null | undefined | Reference data sources (shows below description) |
defaultValue | string | number | null | undefined | Default value to display with sources |
Sources Display: When sources is provided, a dashed divider separates the description from source cards showing name, URL, sample size, and comments.
Button
Section titled “Button”import { Button } from "@/components/ui/button";
<Button variant="default">Primary</Button><Button variant="secondary">Secondary</Button><Button variant="outline">Outline</Button><Button variant="ghost">Ghost</Button><Button variant="destructive">Destructive</Button><Button variant="link">Link</Button>
<Button size="icon"><Icon className="h-4 w-4" /></Button>ColorPaletteSelector
Section titled “ColorPaletteSelector”Per-section chart color palette picker. Each chart section has its own independent palette atom.
import { ColorPaletteSelector } from "@/features/reports/forecasting/components/ColorPaletteSelector";import { salesPaletteAtom } from "@/features/reports/forecasting/atoms";
<ColorPaletteSelector paletteAtom={salesPaletteAtom} />| Atom | Default | Used by |
|---|---|---|
salesPaletteAtom | "monochrome" | Sales Chart |
monteCarloPaletteAtom | "monochrome" | Monte Carlo |
tornadoPaletteAtom | "vibrant" | Tornado (Sensitivity) |
Palettes: "monochrome", "corporate-blue", "vibrant", "earth-tones", "high-contrast" (defined in constants.ts).
Other Available Components
Section titled “Other Available Components”The following components from src/components/ui/ are available but rarely need customization: Accordion, AlertDialog, Badge, Calendar, Card, Checkbox, Collapsible, Dialog, Popover, SectionCard, SectionNav, Separator, Switch, Table. Use them with their default styling.
SectionCard props for header customization:
titleTooltip?: ReactNode— renders anInfoButtonnext to the section titleheaderMeta?: ReactNode— content between title/tooltip and header actions (e.g.,SimulationCountBadge)
<SectionCard id="section-montecarlo" title="Monte Carlo" titleTooltip="..." headerMeta={<SimulationCountBadge />}>Patterns
Section titled “Patterns”Icon Action Buttons
Section titled “Icon Action Buttons”For icon-only buttons (edit, delete), use semantic hover colors:
// Edit/Rename - Amber<button className="text-muted-foreground hover:text-amber-600 hover:bg-amber-50 rounded p-1 transition-colors"> <Pencil className="h-4 w-4" /></button>
// Delete - Destructive<button className="text-muted-foreground hover:text-destructive hover:bg-destructive/10 rounded p-1 transition-colors"> <Trash2 className="h-4 w-4" /></button>
// Info - Blue<button className="text-muted-foreground hover:text-blue-600 hover:bg-blue-50 rounded p-1 transition-colors"> <Info className="h-4 w-4" /></button>Always include aria-label for accessibility.
Form Field with Info
Section titled “Form Field with Info”<div className="flex items-center gap-2"> <Label>Field Name</Label> <InfoButton>Explanation of what this field does</InfoButton></div><NumberInput value={value} onChange={setValue} isDefault={isDefault} />Form Field with Reference Sources
Section titled “Form Field with Reference Sources”For fields that have reference data (amber highlighted), use useReferenceSource to look up sources:
import { useReferenceSource } from "@/features/reports/forecasting/hooks/useReferenceSource";
// Inside componentconst { sources, value } = useReferenceSource("variableName", lineId); // lineId optional
<div className="flex items-center gap-2"> <Label>Field Name</Label> <InfoButton sources={sources} defaultValue={value}> Explanation of what this field does </InfoButton></div><NumberInput value={fieldValue} onChange={setValue} isDefault={isDefault} />The hook looks up sources from reference data based on current geo/indication context.
Nested Sub-Variable Fields
Section titled “Nested Sub-Variable Fields”When a parent field has editable sub-breakdowns (e.g., Early Stage → Localized + Locally Advanced):
{/* Parent field - read-only when computed from subs */}<div className="flex items-center justify-between"> <Label>Parent Field</Label> <div className="text-sm text-muted-foreground tabular-nums px-3 py-1 bg-muted rounded-md"> {computedValue}% </div></div>
{/* Sub-variables - indented with left border */}<div className="ml-4 space-y-3 border-l-2 border-muted pl-4"> <div className="flex items-center justify-between"> <Label className="text-muted-foreground">Sub Field</Label> <div className="flex items-center gap-2"> <RollbackButton visible={!isDefault} onRollback={handleReset} /> <NumberInput value={value} onChange={onChange} /> </div> </div></div>Pattern: RollbackButton always sits next to the value input (right side), inside flex items-center gap-2.
Toggle Filter with Conditional Details
Section titled “Toggle Filter with Conditional Details”For optional filters that show additional info when active (e.g., biomarker filtering):
{/* Section header */}<div className="pt-4 border-t"> <div className="flex items-center gap-1.5 mb-3"> <div className="text-xs font-semibold text-muted-foreground uppercase tracking-wider"> Filter Name </div> <InfoButton>Explanation of what the filter does</InfoButton> </div>
{/* Toggle */} <div className="flex items-center justify-between"> <span className="text-sm">{label}</span> <Switch checked={isActive} onCheckedChange={(checked) => onToggle(checked ? id : null)} aria-label={`Toggle ${label} filter`} /> </div>
{/* Conditional details (shown when active) */} {isActive && ( <div className="mt-3 space-y-2"> <div className="flex items-center justify-between text-sm"> <div className="flex items-center gap-1"> <span className="text-muted-foreground">Detail Label</span> <InfoButton sources={sources}>Tooltip text</InfoButton> </div> <span className="font-medium tabular-nums">{value}%</span> </div> </div> )}</div>Used in: FirstLineExtras in LinePanel (biomarker filter)
Biomarker Card
Section titled “Biomarker Card”Standalone card inside Patient Flow (bg-[#F9FAFA] dark:bg-muted/30, border-muted) with Dna icon header. Active sub-fields use border-l-2 border-primary/20 indent guide. Rendered via FirstLineExtras on first-line panels only, positioned before Healthcare Access.
LinePanel Header — Reference Lines
Section titled “LinePanel Header — Reference Lines”Reference lines show line name as uppercase label, addressable count as text-3xl font-bold text-primary, and an italic origin footnote (border-t, CornerDownRight icon) linking back to stage totals. Custom lines use an editable name input + delete button + badge instead.
Treatment Eligible Card
Section titled “Treatment Eligible Card”Displayed at the end of the Patient Flow section for all lines (reference and custom). Uses rounded-lg border border-secondary bg-secondary/40 with Users icon header matching the Biomarker card pattern. Shows eligible count as text-lg font-bold text-primary.
Quick Reference
Section titled “Quick Reference”| Element | Classes |
|---|---|
| Tooltip bg | bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 text-gray-700 dark:text-gray-300 |
| Default highlight | bg-amber-50 dark:bg-amber-950/30 |
| Edit hover | hover:text-amber-600 hover:bg-amber-50 |
| Delete hover | hover:text-destructive hover:bg-destructive/10 |
| Icon size | h-4 w-4 |
| Icon button padding | p-1 |
| Nested sub-variable indent | ml-4 border-l-2 border-muted pl-4 |
| Read-only computed value | text-sm text-muted-foreground tabular-nums px-3 py-1 bg-muted rounded-md |
| Section padding | p-8 |
| Gap (small) | gap-2 |
| Gap (medium) | gap-4 |