Skip to content

Statements

The largest gap in the market. No component library ships a <BalanceSheet />. This family is the reason Lopifi exists.


59. StatementTable

The shared shell. P&L, balance sheet, and cash flow are all configurations of this.

Structure

┌─ Toolbar: basis · period · comparison · currency · consolidation ─┐
├───────────────────────────────────────────────────────────────────┤
│ Account                 Sep 2026    Aug 2026    Change      %     │  ← sticky header
├───────────────────────────────────────────────────────────────────┤
│ Revenue                                                            │  section
│   Product revenue        482,100     441,200    +40,900   +9.3%   │  L2
│     Subscriptions        390,400     360,100    +30,300   +8.4%   │  L3
│     One-time              91,700      81,100    +10,600  +13.1%   │  L3
│   Services               118,400     102,800    +15,600  +15.2%   │  L2
│                         ─────────   ─────────  ─────────          │  ← single rule
│ Total revenue            600,500     544,000    +56,500  +10.4%   │  subtotal
│                                                                    │
│ …                                                                  │
│                         ═════════   ═════════  ═════════          │  ← double rule
│ Net income               142,880     118,400    +24,480  +20.7%   │  total
└───────────────────────────────────────────────────────────────────┘

The craft

  • Indentation: 24px per level (16px on phone). Levels 1–5. Deeper than 5 is a chart-of-accounts problem, not a UI problem.
  • Rule lines span the numeric columns only, each period column independently, gutters unruled. This is the detail that makes it read as an accounting statement rather than a styled table.
  • Sticky account column (left) and sticky header (top). Both, always. A P&L you scroll horizontally without the account names is unreadable.
  • Sticky total row at the bottom for the grand total.
  • Expand/collapse is instant — hundreds of times per close. The chevron rotates 140ms; the rows do not slide, grow, or stagger. This is the Tier 0 concession.
  • Expansion state is in the URL, so a shared link opens to the same view.
  • Zero values render as —. Null renders as n/a. They are different facts.
  • Right-aligned, tnum, case, paren column reserved. Negatives in parentheses, always — this is a statement.
  • Every column header states its period explicitly. "Sep 2026", never "Current".
  • The statement header carries the full context: entity, statement type, resolved period, basis (cash/accrual), presentation currency, and generation timestamp. Required for print, and required for the number to mean anything.

Responsive

One period column at a time on phone, with a swipeable period selector and a fixed account column. Values crossfade at 140ms with no translation — translation would imply the labels moved too. Full spec in responsive.

Accessibility

Real table semantics, <th scope="row"> on account names, <caption> with the resolved period, data-level for hierarchy (not aria-level, which is invalid outside a treegrid), aria-expanded/aria-controls on toggles, and subtotal/total status in the row header text — the rule line is invisible to a screen reader. Full spec in accessibility.

Print

Repeating headers, break-inside: avoid on rows, break-before: avoid on totals so a total never orphans from its rule.


60. StatementRow

One line. Indent level, label, N period values, expand toggle, subtotal/total variant, drill-down affordance.

Server-safe (no "use client") — a 400-row statement renders on the server. This is why Money, Rule, and StatementRow are all RSC-compatible; it's a real performance decision, not a purity one.

61. ProfitAndLoss

Revenue → COGS → Gross profit → Operating expenses → Operating income → Other → Net income.

  • Margin rows (gross %, operating %, net %) rendered as a Percent level, with their period-over-period change in points, visually distinguished from the money rows — a percentage in a column of dollars needs to not look like dollars.
  • Comparison modes: prior period, prior year, budget, forecast. Each with its own variance column.
  • Expense rows are polarity="inverted". An increase in COGS is not good news, and defaulting it to green is the bug this whole system exists to prevent.
  • Optional per-line WaterfallChart drill-in showing what drove the change.

62. BalanceSheet

Assets → Liabilities → Equity, with the accounting identity checked and displayed.

  • The balance check is a first-class UI element, not a footnote. Assets 4,281,900 = Liabilities 1,840,200 + Equity 2,441,700 ✓ — and when it doesn't balance, that's a prominent error state with the difference stated, not a silently wrong statement.
  • Current vs. non-current sub-grouping.
  • As-of date, not a period — a balance sheet is a point in time and labeling it with a range is a category error the component prevents by taking a Date, not a range.
  • Comparative columns: this date vs. prior period end vs. prior year end.

63. CashFlowStatement

Operating / Investing / Financing, with the reconciliation to the change in cash. Supports both direct and indirect methods. The beginning-cash → ending-cash tie-out is displayed and asserted, same as the balance sheet's identity.

64. StatementToolbar

Basis (cash / accrual) · period · comparison · presentation currency · consolidation scope · expand-all/collapse-all · export.

The basis toggle is prominent and always visible. A P&L on cash basis versus accrual basis are different statements, and a user who doesn't know which they're looking at is being misled. This is not a settings-menu item.

65. PeriodPicker

Fiscal-aware. Takes fiscalYearStartMonth and labels accordingly — FY26 Q1 may start in July.

  • Presets: this month, last month, QTD, YTD, this fiscal quarter, this fiscal year, trailing 12.
  • Every preset shows its resolved dates. "Last quarter (1 Apr – 30 Jun 2026)".
  • Custom range with a two-month calendar.
  • Closed/locked periods are visually marked and warn on selection, because a "closed" period's numbers shouldn't be changing.

66. VarianceCell

Absolute change + percent change + significance. Significance is a threshold on both magnitude and materiality (a 200% swing on $12 isn't news), and the threshold is configurable because materiality is a business judgment.

Respects polarity. Division by zero → n/a, never Infinity.

67. FluxAnalysis

Period-over-period explanations attached to statement lines. Each variance can carry a written explanation with an author and timestamp — this is the actual month-end close workflow, and no library supports it.

Unexplained material variances are flagged. The flag list is the reviewer's worklist.

68. DrilldownSheet

Click a statement line → the GL entries behind it. Sheet on phone, side panel on desktop. The entries sum to the line, and the sum is displayed — that verification is the whole point of drilling down. Links onward to source documents.

69. TrialBalance

Every account with debit and credit columns and the proof that they're equal. The most boring component in the library and the one an accountant checks first.

70. CloseChecklist

Tasks, owners, dependencies, blockers, sign-off. Progress by section. Overdue items flagged with day counts. Sign-off is an AlertDialog with a hold-to-confirm — signing off a close is irreversible.

The 12 components specified here.

Built against this document

The surfaces that implement what this argues — running, computing, or shaped by it.

Told when this spec changes

The docs are the contract the components are built against. When one changes, the component changes with it — and the copy in your repo does not. One email when that happens.

One address, no list rental, unsubscribe in one click.

Works in your QuickBooks Online or Xero file

Get your books closed every month

Lopifi is building AI bookkeeping that keeps your books closed on US GAAP as transactions happen, with a CPA review every quarter, for $200 a month. We’re not open yet.

$200/month when we open. No card now.