# PeriodGrid

> The ruled grid. Frozen panes, hidden columns, hover change, ⌘C.

Source: https://lopifi.com/components/period-grid
Part of: Lopifi — US GAAP books on autopilot, reviewed by a CPA (https://lopifi.com)

---

- **Family:** Ledger & tables — 
- **Status:** Implemented
- **Spec:** https://lopifi.com/docs/catalog/05-ledger-tables

## Install

```bash
npx shadcn@latest add @lopifi/theme
npx shadcn@latest add @lopifi/period-grid
```

## Specification

The ruled grid: rows down, periods across, and the six spreadsheet behaviours an accountant
performs on a workbook within a minute of opening it. Every period-by-period surface in the
system — `ForecastTable`, `BudgetVsActual`, `RevenueTracker`, `ExpenseTracker`, `DriverModel`,
`ScenarioCompare`, `PlByDimension` — is a configuration of this one grid rather than another
table written from scratch.

### Ruled cells, and why the statements are not
A statement is read across one row at a time, and its horizontal rules carry meaning — single
above a subtotal, double under a total. A period grid is read *down* a column as often as
across a row, and a column with no left edge is one the eye loses the moment two neighbouring
figures happen to be the same width. So the grid boxes every cell and the statements keep their
accounting rules with a vertical seam added between period columns. Two different documents,
two different rulings, one reason each.

`border-collapse: separate` is required rather than preferred: a sticky cell in a collapsed
table loses its borders when it detaches, because the border belongs to the table and the cell
scrolls away from it.

### Freeze panes
Top row and left column, the two anyone freezes in every workbook they open. The freeze shadow
appears only once something is hidden behind it — a permanent shadow on a grid that fits its
container is a scroll cue for a scroll that cannot happen.

### Hidden columns
Nobody reviews fourteen months at once. Hiding is not deleting: rows index into the full column
list, so a hidden column never renumbers the data behind it, and a declared comparison
(`column.compare`) still measures against its base while that base is off screen. The count of
hidden columns is always shown with a one-click path back — a reader who hid six columns last
quarter needs to know this grid is not the whole story.

### The change, printed under the figure
The figure answers "how much"; the next question is always "versus what". That answer is printed
under every figure at once — small, toned, right-aligned — rather than revealed one cell at a
time on hover. The reason anyone opens this grid is to compare, and a comparison you can only
make one cell at a time is not a comparison. The pointer and the keyboard cursor still fill the
formula bar under the grid with the full sentence, including the amount and the column being
measured against.

The sub-line is a percentage, not an amount: the amounts are already on screen directly above
and to the left, and the percentage is the thing the eye cannot do for itself while scanning
seven columns. On a percentage row it is points instead — calling a move from 51.8% to 53.0%
"+2.3%" is the most common way a deck lies by accident.

**No invented percentages.** No ratio against a zero base, none against a negative base, and a
missing figure stays missing. The sub-line says which of the three it was instead of printing a
number that reads backwards, and the first column on screen shows an en dash rather than a zero.

### Annotations, not views
Two independent toggles — % change, and % of a named base row — each adding a line under the
figures. Neither replaces them. A segmented Value / Δ / Δ% switch forces the reader to hold one
of the two numbers they are comparing in their head while they look at the other, and a grid
whose numbers are swapped out for percentages under a tab press is a grid that has to be
re-read every time it is touched.

### Typing
Off by default: a grid over posted figures is a report, and a report that accepts typing is one
someone will believe they have changed. Where a caller marks cells editable — the projected
inputs of a driver model, never its actuals or its computed rows — a cell opens on double-click
or `Enter` and accepts what a spreadsheet accepts, including `1.2k`, `(1,240)` and `=1200+340`.
A value it cannot parse commits nothing rather than committing zero.

### Keyboard and clipboard
One tab stop, then arrow keys, `PageUp`/`PageDown`, `Home`/`End`, shift to extend a range and
⌘C to copy it. The clipboard carries `data-raw` values, so the paste sums in the spreadsheet it
is going back to.

## Rules this component obeys

Money is minor units, never floats. `null` is not `0` and neither is redacted. Negatives render in accounting parentheses with the parenthesis column reserved on both sides. Debit and credit are two columns. Per-currency decimal places. Polarity is explicit — up is not always good. Every figure states its period, entity, currency, basis and as-of.
