These live in src/components/ui/ next to the shadcn/ui components. Use them instead of rebuilding the same markup on a page.
Component
Description
Checkbox
The only place a checkbox is drawn, built on the base-ui checkbox, with an indeterminate state and a 44 px touch target around the 20 px box. Give it an id and put a sibling Label next to it instead of nesting it in a label. A raw checkbox input fails the drift guards
RowDeleteButton
The only row-delete control: a ghost icon button with a destructive trash icon (44 px on touch screens) and a tooltip repeating its accessible name from common.deleteNamed. It only fires onClick; the caller opens a ConfirmDialog. A disabled delete keeps its disabledReason reachable by keyboard
TrendMark
An arrow plus screen-reader text (common.trend.up / common.trend.down) for a value that rose or fell against its previous value. Render it beside a green or red amount so colour is never the only signal; the colour stays with the caller’s class. trendOf(current, previous) computes the direction and gives null (nothing rendered) when the value is unchanged or has no previous value
EmptyState
No data at all: icon, title, body and one call to action or several (create and import). size is page or compact (inside a card, tab panel or popover)
NoResultsState
Data exists but the search or filters match none of it; shows a clear-filters button when onClear is passed. Same layout as EmptyState and ErrorState
ErrorState
A failed load. size is page, compact or inline (one row inside a section); title and retryLabel are optional. It is announced as an alert, and Retry shows a spinner while onRetry returns a pending promise (such as refetch) or isRetrying is true
ConfirmDialog
Confirmation for one action. variant is destructive (default) or default for a non-destructive confirm such as Mark paid. There is no default confirm label: name the action (Delete, Revoke), never a bare Confirm. cancelLabel replaces Cancel when dismissing means something else, such as Continue editing on a leave-without-saving confirm. A component that owns its mutation wraps it and passes its pending state as isConfirming
Label
optional appends a muted “(optional)” marker; required fields carry no marker
PageHeader
Takes primaryAction (the page’s main action, icon-only below the lg breakpoint and hidden in selection mode), description (the subtitle under the header) and onEnterSelection; spread headerProps from useSelection to wire the Select button
PaginationFooter
Exports DEFAULT_PAGE_SIZE (50) and PAGE_SIZE_OPTIONS (50, 100, 250); every paged list uses them
SectionTitle
The one section heading style: h2, or h3 through as. Inside a Card use CardTitle
The visuals/ primitives of the visual cards and of the charts on the standard cards: VisualBody frames a card’s graphic with its text equivalent (one image named by a sentence, shown as visible text on a narrow card), the others draw bars, rings, donuts and columns in SVG and CSS; DonutLegend is a donut beside its legend, SignedBars signed columns about a zero axis and DotRow weekly dots with the current one ringed
Grid view (not a table) with inline name/color editing, aggregated balance with trend coloring, status badge, actions menu (edit/archive/unarchive/delete)
Empty state shown when a report has no spending to analyse
FinancialIndependenceSection
The Independence tab: the headline for each outcome, notices, the projection as chart or table, the effective assumptions and the settings card
FireChart
Projected portfolio line with a dashed target and a crossover marker; FireChart.utils.ts builds the chart rows, table rows and CSV rows
FireSettingsForm
Investable accounts, withdrawal rate, real return and yearly spending; sends only the changed fields and shows what the server stored
CashFlowSection
The Cash flow tab: period selector, summary strip, chart or table view and every loading, empty and error state
SankeyChart
The cash flow diagram (Recharts Sankey, server order kept); SankeyChart.utils.ts builds the nodes, links and table rows
CashFlowTable
The text equivalent of the diagram, one row per flow
ReportBuilderSection
The Builder tab: owns the working definition and the last result, the toolbar with the open report, and the run
ReportDefinitionForm
The definition form (groupings, interval, measure, chart, period, filters); its rules live in ReportDefinitionForm.utils.ts
ReportResult
The result card: bar, stacked bar, line, area, pie or a paginated table, with notices and a CSV export; ReportResult.utils.ts shapes the chart data
ReportTooltipCard
The tooltip card shared by the builder charts
SavedReportsPopover
Save the current report under a name, open, rename and delete saved reports in place
RetirementSimulationSection
The Retirement tab: the form, then the result card (success-rate headline, stats, fan chart or table, notices) with its placeholder, loading, error and empty states; a run saves the settings first, then simulates from the saved values
SimulationForm
Current age, retirement age, retirement length, volatility and seed, with the bounds checked on the client and a server refusal shown on its field; “New seed” fills a random seed without running
SimulationChart
The fan chart: 10th to 90th and 25th to 75th percentile bands around the median line; SimulationChart.utils.ts builds the chart rows, the table rows and the end age
reports.constants.ts mirrors the server’s bounds for the settings and simulation forms.
Grid with sticky header/column, keyboard navigation (Arrow/Enter/Escape), WAI-ARIA grid roles, paged with a rows-per-page selector (50, 100 or 250 rows)
Multi-account entry form with colored cards, institution grouping, real-time total with delta, sticky submit bar; prefill guarded with hasPrefilled ref to prevent overwriting user input on background refetch
SnapshotEmptyState
Empty state with CTA
SnapshotLoadingSkeleton
Loading skeleton mirroring content layout
SnapshotList
List view of snapshots with progressive loading (10 date groups initially, “Load more” button)
Generic drag-and-drop grid built on @dnd-kit/core and @dnd-kit/sortable. Used by the accounts page and the Manage dashboards dialog (the dashboard grid is react-grid-layout, see DashboardGrid). Features:
DragOverlay with shadow, ring, scale, and rotate for visual feedback
Real-time reordering during drag
Touch and keyboard support
Optional handleLabel(item) gives each item’s grip its own accessible name (for example the dashboard it moves); without it the grip keeps the generic reorder label
Order persistence via API (POST /api/v1/accounts/reorder)
Shared wrapper for chart components. It renders the title (titleId) as an h2, or as an h3 with headingLevel={3} inside a section that already has its own h2 (the cash flow and financial independence reports). The chart sits in a role="group" element labelled by that title and described by a visually hidden text alternative, so a screen reader announces the title, then the description. Never put role="img" on the group: charts hold legend buttons that must stay reachable.
textAlternative: the description, as a plain string. Format amounts with formatCurrency and shares with formatPercent, so the number format and privacy mode apply.
csvExport (filename, headers, rows): adds a CSV download button. When textAlternative is absent, the card also builds a generic description from the rows: the title, the number of data points and the first and last label (first column), named once when they are the same. Labels in yyyy-MM-dd or yyyy-MM form are read as a date in the user’s format or as a month and year. Other labels are read as given, so when the CSV keeps internal keys, pass translated labels in alternativeRows.
summary: an optional visible line under the title, in small text.
alternativeRows: rows for the generic description, used instead of the CSV rows. Use it when the card has no CSV download (the chart tabs, the dividend charts, the exchange rate history), or when the CSV keeps internal keys that must not change (assets/liabilities on the Assets / Liabilities card).
hideTitle: keeps the heading for screen readers only; ChartTabContent sets it because the active tab already shows the name.
A drift guard in src/__tests__/drift-guards.test.ts fails when a ChartCard or ChartTabContent has none of csvExport, textAlternative, alternativeRows or summary, outside a short allow-list of text-only metric cards.
A card with neither csvExport, textAlternative nor summary has no description. Empty and loading states are up to the caller.
Wraps the entire application in src/main.tsx. Catches unhandled React render errors and displays a French fallback UI with a “Refresh page” button.
Automatically resets its error state when the children prop changes (via componentDidUpdate), so navigating to a different route clears the error without requiring a manual page refresh.
Per-route error boundary used in App.tsx via React Router’s errorElement prop. Displays “Back” and “Dashboard” navigation buttons, preventing a full-app crash on route-level errors.