Roadmap
Build order is deliberate. Each milestone proves something the next depends on.
M0 — Foundations (docs complete, tokens shipped)
Deliverable: @lopifi/theme installable, and every claim in the design docs demonstrable.
-
tokens.css— the complete set fromfoundations/, both themes, plus the signal palette - Tailwind v4
@theme+ v3 preset -
scripts/check-tokens.ts— contrast ratios computed from tokens, grid conformance on every spacing value, failing the build on violation - Font subsetting +
size-adjustfallback metrics, CLS measured at zero - Docs site skeleton with theme / density / signal / reduced-motion toggles
Exit gate: a contrast-failing token change fails CI.
M1 — The money proof
Deliverable: correctness, provable. The thing that makes this a finance library.
-
lib/money.types.ts,format-money.ts,allocate.ts,fiscal-period.ts -
Money·Delta·Figure·Percent·Rule - Property-based tests (
fast-check):format(parse(x)) === xover all valid inputs;allocate(total, w).sum() === totalover all inputs - Every currency edge case: JPY 0dp, KWD 3dp,
bigint, negative zero -
tnum+caseverified visually — a rendered column of mixed-sign values with proven decimal alignment - The greyscale test automated as a visual-regression assertion
Exit gate: a public "Why most money components are wrong" doc page, with live side-by-side comparisons. This is the marketing artifact and it writes itself if M1 is done properly.
Why first: every component above this inherits its correctness. And the case/paren-alignment craft is the hardest thing to retrofit.
M2 — Surfaces
-
Cardbase ·BalanceCard·KpiCard·StatCard·KpiRow -
Sparkline·Trend·StatusPill·AsOf·Redacted·Reference·EntityMark -
EmptyState(3 variants) ·ErrorState·Skeleton·StaleIndicator - The isometric object library + the first five scenario illustrations
- Elevation, borders, and dark-mode top-highlight validated across every surface
Exit gate: a dashboard built entirely from these that looks finished, at 390px and 1280px.
M3 — The table
Deliverable: the workhorse, at every width.
-
TransactionTable·TransactionRow·TransactionCard·TransactionDetail - Virtualization with correct
aria-rowcount/aria-rowindex - Full grid keyboard model — arrows navigate, one tab stop
- All four responsive presentations, including the 768–1024 sticky-both-edges pattern
-
FilterBar(URL-backed) ·BulkActionBar·ColumnManager·ExportMenu -
use-density+DensityToggle
Exit gate: 10,000 rows at 60fps and INP under 200ms on a mid-range Android at 4× CPU throttle; full keyboard-only operation; the 390px card list is genuinely usable, not a compromise.
M3.5 — The ledger core
Deliverable: the general ledger the statements are a view over. Built before the statements, because a statement that can't drill down to journal entries is a picture of a statement.
-
JournalEntry·JournalEntryForm·JournalEntryTable— two columns, always -
AccountPicker·ChartOfAccounts·AccountRegister·DimensionPicker -
AdjustingEntryFormwith auto-reverse defaulting on for accruals -
EntryReversalDialog— and no delete path anywhere on a posted entry -
PeriodStatus·CutoffWarning— the period state machine -
RollForward·ScheduleTable·AccountTieOut·SchedulePreview -
AuditTrail·EvidencePanel -
IntegrityCheckPanelwith all eleven assertions wired
Exit gate: post an entry, reverse it, and prove from the UI alone that history was never rewritten. Every balance sheet account has a tie-out, and a failing tie-out owns the screen.
M4 — Statements
The differentiator. Nothing else in the ecosystem has this.
-
StatementTable·StatementRow·Rulein anger -
ProfitAndLoss·BalanceSheet·CashFlowStatement -
StatementToolbar·PeriodPicker(fiscal-aware) ·VarianceCell -
DrilldownSheet·FluxAnalysis - Full statement semantics for screen readers
- Print stylesheet — multi-page, repeating headers, no orphaned totals
- Phone presentation: one period at a time, swipeable, fixed label column
Exit gate: an accountant who closes books reviews a rendered P&L and balance sheet and finds nothing wrong. That's the real test, and it should happen before the code is called stable.
Why here: it's the hardest thing to retrofit, the biggest differentiator, and it stress-tests the primitives harder than anything else.
M4.5 — The close
Deliverable: the workflow the statements exist for. This is the Numeric/FloQast lane, operating on the same components that render the statements rather than beside them.
-
CloseStatus— the seven-section gated pipeline -
CloseChecklistwith owners, blockers, weighted progress, and audited sign-off -
ReconciliationViewwith basis-stated match suggestions +CategoryPickerwith inline rule creation -
FluxAnalysis— required explanations on material variances, with drivers -
MaterialityPolicy·RestatementBanner·SubsequentEventLog -
ApprovalChain·ApprovalInbox(mobile-first) ·RoleMatrix
Exit gate: a full month-end close run end to end in the product, by someone who closes books for a living, with no spreadsheet.
M5 — Receivables (AR)
-
InvoiceFormwith the per-line revenue-recognition preview ·InvoiceDetail·InvoiceRegister -
AgingTabletied to the AR control account -
CashApplication+PaymentSuggestion— every case, not the happy path -
UnappliedCashPanel·CreditNoteForm·CustomerStatement -
CollectionsWorklistwith promise-to-pay ·DunningTimeline -
RevenueWaterfallfrom performance obligations
Exit gate: a lump-sum payment covering seven invoices with a short pay and a bank fee applies correctly, with every difference given an explicit disposition.
M6 — Payables (AP)
-
BillForm+DuplicateBillAlert(blocking) ·BillDetail·BillRegister -
ThreeWayMatchwith line-level variance ·MatchExceptionQueue -
ApprovalChainversion-binding — editing a bill voids its approvals -
PaymentRunBuilder·PaymentRunPreviewwith hold-to-confirm and resulting cash -
VendorMasterwith versioned bank details ·BankDetailChangeAlert -
ExpenseCapture(camera-first) ·CardTransactionMatch -
UninvoicedReceiptsReportdriving the GR/IR accrual
Exit gate: segregation of duties cannot be bypassed by any path in the UI, and a vendor bank-detail change before a payment run is impossible to miss.
M7 — Payments
-
AmountInput— with the full parse/format/caret test suite -
PaymentForm·CurrencySelect·PayeeSelect·PaymentMethodList -
ConfirmPaymentDialog·HoldToConfirmwith all accessible fallbacks -
PaymentStatusTimeline·PaymentSuccess·CheckoutSummary -
Sheet(Vaul) ·Toast(Sonner) configured - Full gesture layer: velocity dismissal, boundary damping, pointer capture, multi-touch protection
Exit gate: the whole payment flow completed one-handed on a real iPhone SE, over a throttled network, with VoiceOver on.
M8 — Swaps & FX
-
SwapWidget+ the flip ·RateLine·RateLock·ConversionBreakdown -
CurrencyPairChart·RateTicker·SlippageControl·FxExposure - Live-value infrastructure:
use-live-value, the value flash, throttledaria-live
Exit gate: a rate expiring mid-transaction is handled correctly — the action blocks until the user acknowledges the new rate. Never executes at an unseen rate.
M9 — Charts, dashboard & metrics
-
MoneyChart·MoneyBarChart·WaterfallChart·CategoryBreakdown -
ChartLegend·ChartTooltip·DataTableToggle -
DashboardShell·AnomalyCard·RunwayCard·CashCalendar - Money Law 2 enforced in tests: assert no animation on data change
-
MetricDefinition— and no metric card ships without one -
NonGaapReconciliation·MrrBridge·BudgetVsActual·CovenantMonitor
Exit gate: every chart has a working table equivalent; every metric shows its formula with values substituted; the greyscale test passes on all of them.
M10 — Multi-entity & consolidation
-
EntitySelector— standalone vs. consolidated visually distinct -
ConsolidationView·NciPanel·EliminationEntry -
IntercompanyMatrix— must net to zero, break types named -
TranslationSummary·CtaRollForward·FxRevaluation·FxGainLossPanel·RateTable
Exit gate: a three-entity group with two functional currencies consolidates, eliminates, translates, and ties — with the CTA visible as its own equity line.
M11 — Completeness & polish
- Remaining specified components
-
CommandPalette(motionless), the reconciliation match animation -
LedgerTable·TrialBalance·AuditorMode·SamplingTool·EvidenceBundle -
LeaseSchedule·AllowanceRollForward·ForecastTable - Full visual regression matrix: every component × {light, dark, forced-colors, signal} × {compact, default, comfortable} × {320, 768, 1280}
- Motion review pass — every animation at 4× slowdown, frame-stepped
- An audit pass per shipped surface — three findings maximum, plans written to
design-plans/ - The next-day review on every component
Deferred
| Item | Gate |
|---|---|
corner-shape: squircle |
Baseline Widely Available. Progressive enhancement, no layout change. |
| View Transitions | Not for in-app finance navigation. Revisit only if interruption handling improves. |
| Figma library | After M4, so it's generated from settled components rather than guiding them. |
| Additional themes | Never as a marketplace. A consumer overrides --accent and --font-display; anything more is a fork. |
| Vue / Svelte ports | After the React system is stable. The tokens and the docs port; the components don't. |
Definition of done
A component is stable only when all of these are true:
- The four-layer spec exists, was reviewed before the code, and ships in its sibling
README.md - The full quality bar passes — every section, 4b included
- Every edge case in the checklist has a test or a story
axeclean; keyboard-only pass; VoiceOver and NVDA pass- Visual regression across the full matrix
- Motion reviewed at 4× slowdown on a throttled real device
- Financial review by someone who has actually closed a month — and for anything in families 9–13, a review against the domain docs clause by clause
- The next-day review is done
Merged is not stable. Step 8 is not optional — it catches more real problems than every automated check combined.