Skip to content

Component Library

The Patrimoine frontend is built on shadcn/ui (base-nova style) with custom domain-specific components organized by feature area.


Base UI primitives live in src/components/ui/ and follow the shadcn/ui pattern (owned by the project, customizable directly).

alert-dialog, avatar, badge, button, card, checkbox, combobox, dialog, dropdown-menu, input, label, popover, select, separator, skeleton, sonner, switch, table, tabs, tooltip

Additional shared components: ErrorState, LoadingSpinner, SortableTableHead

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

components.json configures shadcn with:

  • Style: base-nova
  • Tailwind: v4 (CSS-first)
Terminal window
npx shadcn@latest add <component>

shadcn is listed in devDependencies.


Component Description
AccountCard Account card with color accents (left border, gradient bar, background tint), institution header, balance coloring, mini sparkline, keyboard accessibility in selection mode (role, tabIndex, onKeyDown, aria-pressed)
AccountForm Create/edit form with ColorPicker and institution selectors; uses setValue (RHF best practice) instead of private control._formValues
AccountTable Table view with inline edit, column sorting (name, balance, type, institution), bulk selection; selection mode uses the shared Checkbox (keyboard focusable, Space toggles)
AccountTableCell Individual table cell with inline editing
AccountFormDialog Dialog around AccountForm
AccountLabel Account name with its colour and type icon
AccountRow One account row
BudgetToggleButton Switches an account on or off the budget
CloseAccountDialog Close an account: transfer of the remaining balance and closing snapshot
LoanPayoffDialog / LoanPayoffLine Loan payoff projection from the account’s rate and payment
AccountTypeBadge Badge displaying account type with icon
AccountTypeSelect Dropdown selector for account types
ColorPicker 24 OKLCH preset swatches + custom hex input + clear button
CurrencySelect Dropdown selector for currencies
EmptyState Empty state with icon, heading, and CTA
InstitutionSelect Dropdown selector for managed institutions
LiquidityHorizonSelect Dropdown selector for liquidity horizon (immediate, semi-liquid, blocked, long-term)
Component Description
NetWorthChart Area chart with optional CAGR projection and linear regression
AccountTrendsChart Stacked area chart per account with percentage progress in legend
StackedAreaChart Stacked area by account type
AllocationByTypeDonut Donut chart of allocation by account type
AllocationByInstitutionDonut Donut chart of allocation by institution
AssetsLiabilitiesDonut Donut chart of assets vs liabilities
AssetsLiabilitiesGauge Radial gauge showing asset ratio
MonthlyChangeBarChart Bar chart of monthly net worth changes
YearlyGrowthBarChart Bar chart of yearly growth
AllocationEvolutionBarChart Bar chart showing allocation evolution over time
AccountBalanceBarChart Horizontal bar chart ranking accounts by balance
AllocationTreemap Treemap of allocation
SnapshotHeatmap GitHub-style frequency heatmap of snapshots
DeltaHeatmap Heatmap of variations by weekday/monthday
SavingsGoalsProgress Progress bars for savings goals
SavingsRateTrend Trend chart of savings rate
ConsistencyGauge Gauge showing recording consistency
CurrencyExposureDonut Donut chart of currency exposure
DividendCalendarChart Calendar chart of dividends
DrawdownChart Drawdown visualization (peak-to-trough declines)
WaterfallChart Monthly waterfall chart of account contributions
ChartCard Chart wrapper: heading, described chart group, optional summary line and CSV download
ChartEmptyState The create and import call-to-action pair for charts, built on EmptyState
ChartTooltip Custom tooltip for charts
DateRangeSelector Period filter (1M, 3M, 6M, YTD, 1Y, 3Y, All + custom dates)
useChartHeight Hook for viewport-adaptive chart height
BudgetVsActualChart Combo bar+line showing budget limit vs actual spending per category
ChartTabContent Chart tab panel for ChartsPage and BudgetChartsPage: TabsContent + ChartCard with a screen-reader-only heading (titleId) + empty state
ForecastChart / ForecastTabContent / ForecastWhatIf / ForecastAccountSelect Day-by-day balance forecast with a what-if replay
YoYComparisonChart Year-over-year comparison
MonthlyIncomeExpenseChart Monthly income against expenses
NominalVsAdjustedChart Nominal against inflation-adjusted net worth
ConcentrationChart / LiquidityChart Concentration and liquidity analysis
SpendingHeatmap / CategorySpendingDonut Spending by day and by category
DividendGrowthChart Dividend growth over time
ChartAnnotationsDialog / ChartAnnotationForm Create and edit annotations overlaid on chart timelines
GoalProjectionTimeline Timeline chart projecting savings goal completion based on savings rate
Component Description
DashboardGrid The cards on a 12-column react-grid-layout grid at 640 px and up, a stacked list below
DashboardTabs / ManageDashboardsDialog One tab per dashboard and the dialog that manages them
CardLibraryDialog / DashboardCardMenu The Card library gallery and a card’s options menu in Customize mode
TidyUpDialog / LayoutDiagram The Tidy up preview dialog and the block diagram it draws of a layout
SetupChecklist First-run checklist
DashboardCards (barrel re-export) + cards/ subdirectory with 12 files: types.ts, NetWorthCards.tsx, BalanceCards.tsx, AccountCards.tsx, RecordCards.tsx, GrowthCards.tsx, GoalCards.tsx, StructureCards.tsx, MoneyMonthCards.tsx, PlanCards.tsx, InvestmentCards.tsx, index.ts — 25 card components across 6 modules, plus the 14 visual cards across 4 more modules
VisualBody / SegmentBar / ProgressBar / Ring / Donut / DonutLegend / WaterfallRows / MiniBars / SignedBars / DotRow 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
Component Description
AccountMappingSummary Summary of account mapping during import
FileDropZone Drag-and-drop file upload zone
ImportErrorAlert Error alert for import failures
ImportSummary Summary of imported data
PreviewTable Preview table for parsed import data
BackupPreviewTable Preview of a JSON backup before restore
RulesTimedOutNotice Notice that a slow regex rule was skipped during an import
Component Description
InstitutionsGridView / InstitutionsGridRow Grid view (not a table) with inline name/color editing, aggregated balance with trend coloring, status badge, actions menu (edit/archive/unarchive/delete)
InstitutionCard Card presentation of a single institution
InstitutionFormDialog Create/edit form dialog
Component Description
AppLayout Root layout with Outlet, sidebar (desktop), bottom nav (mobile)
Sidebar Desktop sidebar navigation with PiggyBank icon + Instrument Serif wordmark
BottomNav Mobile bottom navigation — 5 primary links + “More” button opening Dialog with 8 remaining destinations
TopNavbar Top navigation bar — profile menu (Help, Settings, Logout, Start the tour, etc.), command palette trigger
ModeToggle Light/dark mode toggle button
LanguageToggle Language cycle button (FR → EN → ES → DE → IT) in sidebar footer
AboutDialog About dialog with git build info (version, SHA, commit date, committer)
KeyboardShortcutsHelp Dialog listing all keyboard shortcuts
AlertsIndicator Bell icon with unread-alert badge, opens a dropdown of recent alerts
GlobalSearchDialog Cmd/Ctrl+K command palette: a Commands group (from paletteCommands.ts) above the server search results
PrivacyToggle Button that hides or shows amounts on this device (display-only, see Frontend Architecture)
PageHeaderSlot Shared page-header layout slot used across list pages
SkinToggle Skin picker (Fintech, Azur, Noir, Swiss, Aurora)
NotificationList / NotificationItem The notification centre list opened from AlertsIndicator
PaletteTrigger Button that opens the command palette
PrivacyAnnouncer Screen-reader announcement when privacy mode changes
navItems.ts NAV_GROUPS, EXTRA_PAGES and SETTINGS_SECTIONS: the single source for the sidebar and the command palette
Component Description
TransactionsTable / TransactionRow The transactions list and its rows
TransactionForm / TransactionFormDialog Create and edit a transaction
TransactionFilterPanel Filters of the transactions list
BudgetsSection / BudgetForm / BudgetEditDialog Monthly budgets
BulkEditTransactionsDialog Edit several transactions at once
ImportCsvTransactionsDialog / ImportCsvCategoryDialog / ImportOfxDialog Bank-file and category imports
ReconcileDialog Reconcile an account against a statement
TransferFormDialog / TransferSuggestionsDialog Create a transfer or review suggested pairs
TransactionTrashDialog Trash with restore and permanent delete
ApplyToSimilarDialog Apply a category to similar transactions
MergePayeesDialog / MergeTagsDialog Merge payees or tags
PayeeCombobox / TagMultiCombobox / AccountMultiCombobox / CategoryTreePicker / TransactionAccountSelect / AccountCell Pickers and cells used by the forms and filters
Component Description
CategoryTree / CategoryTreeNode Category and subcategory tree with drag reorder
CategoryCard / SubcategoryCard Cards of a category or subcategory
CategoryCombobox / CategorySelect Category pickers
MoveSubcategoryDialog Move a subcategory to another category
Component Description
RuleEditor / RuleEditorDialog The rule form, shared by the rules page and a dialog
ConditionGroupEditor Nested condition groups
PayeeConditionCombobox The Payee condition’s value
Component Description
RatesTable / RateFiltersBar / RateFormDialog The rates list, its filters and the form
RateHistoryChart History of one currency pair
CoverageAuditPanel / MissingRatesNotice Coverage audit and the missing-rates notice
Component Description
HoldingsTable / HoldingGroupRow / HoldingLotRow / HoldingsFilterPanel Holdings grouped by ticker, with lots and filters
HoldingForm / HoldingFormDialog Create and edit a holding
DividendsDialog / DividendForm / DividendRow Dividends of a holding
PortfolioSummaryCards Portfolio totals
AccountSelect Investment account picker
Component Description
ProfileCard / PasswordCard / AppearanceCard / CustomThemeCard Profile, password, appearance and custom theme
ApiTokensSection / WebhooksSection API tokens and webhooks
NetWorthThresholdsSection / NetWorthThresholdForm Net worth thresholds
AlertRulesSection / NotificationPreferencesSection Alert rules and the channel per notification type
TotpSetup / TotpVerify / RecoveryCodesSection / RecoveryCodesDialog / RecoveryCodesList Two-factor setup and recovery codes
SessionsCard / SessionRow Signed-in sessions
DangerZoneSection Account deletion
Component Description
PlannedRow / PlannedStatusBadge / PlannedAccountCell Planned list rows and their cells
PlannedCalendar / PlannedCalendarDayDialog / PlannedOccurrencesDialog Calendar view and occurrences
PlannedSuggestionsDialog Suggested planned transactions
SubscriptionsTab Active repeating expenses
Component Description
EnvelopeForm / PaydayPlanCard Envelope form and the payday plan
ScheduleFormDialog / FundingScheduleSelect Pay schedule dialog and the schedule picker used by the envelope and goal forms
SpendableHeaderFigure / SpendableBreakdownDialog / SpendablePoolMenu End-of-month spendable figure, its breakdown and pool choice
AdminAuditLogTab Admin audit view
Component Description
TourProvider State of the one-time offer on the dashboard and of the five-step walk
GuidedTour Spotlight and step card drawn over the data-tour anchors

tourSteps.ts is the fixed list of steps and the anchor lookup, not a component.

Component Description
SplitDialog Edit the category lines of a split transaction, with a live remainder; Save stays disabled until the lines add up
MergeDialog Two duplicates side by side; the transaction to keep must be chosen before Merge is enabled
SavedFiltersPopover Save the current filters under a name, apply, rename and delete saved filters in place
CategoryMultiSelect The category and subcategory multi-select extracted from the transaction filter panel; the report builder’s category filter uses it too
Component Description
NoSpendingState 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.

Component Description
SavingsGoalForm Create/edit form for savings goals
TemplateFormDialog Dialog that creates a contribution template
GoalTemplateSelect Contribution template picker with a New template link
GoalProjection Projected completion of a goal, shown on its card and in the edit form
TemplatesSection Contribution templates section of the Savings goals page: list, add, edit and delete, with loading, error and empty states
Component Description
SnapshotGridView 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)
SnapshotGridRow Individual grid row
SnapshotGridCell Individual grid cell with value coloring
SnapshotDateGroup Date group card with themed rows
RecurringSnapshotsSection Recurring snapshot rule CRUD (schedule, reminder settings)
SnapshotRow Individual snapshot row in card view
SnapshotRowEditDialog Full-row edit dialog (all accounts + date change)
SnapshotEditDialog Single snapshot edit dialog
SnapshotDeleteConfirm Delete confirmation dialog
SnapshotDateRowDelete Delete all snapshots for a date
SnapshotEntryForm 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)
AccountAmountInput Input component for account amount entry
Component Description
ErrorBoundary App-level error boundary wrapping the entire application
RouteErrorFallback Per-route error fallback with retry/back buttons

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.


List item components are wrapped in React.memo to prevent unnecessary re-renders when the parent re-renders but the item’s props haven’t changed:

  • AccountCard
  • SnapshotRow
  • SnapshotGridCell