How to align numbers in a table
Right-aligned is not aligned. Getting a column of money to read as a column is four settings and one rule line, and it is the difference between a finance product and an admin template with a dollar sign.
1. Turn on tabular figures
By default most typefaces set numerals proportionally — a 1 is narrower than a 0. That is correct for prose and wrong for a column, where it means the digits in row four do not sit above the digits in row five.
.numeric {
font-variant-numeric: tabular-nums;
font-feature-settings: "tnum" 1;
}
Inter has tnum. So does almost every UI typeface worth using. Switch it on for every numeral a reader might compare with another numeral — which in a finance interface is all of them, including dates, reference numbers and quantities.
2. Turn on case-shifted parentheses
case (cv## in some fonts) shifts brackets, dashes and other punctuation up to sit optically centred against lining figures rather than lowercase text. Without it, (1,234.00) has parentheses that hang slightly low against the digits.
font-feature-settings: "tnum" 1, "case" 1;
Both features, always, on anything numeric. This pairing is the single cheapest credibility gain in a financial interface.
3. Align on the decimal point, not the right edge
Right-alignment gets you most of the way only because fixed decimal places make the two equivalent. Which is the actual rule:
Fixed decimals throughout.
$1,200.00, never$1,200.
Intl does this by default. The bug is introduced by someone trimming trailing zeros to "clean up" the column. Once one value in a column has two decimals and another has none, right-alignment stops aligning anything.
Where a column genuinely mixes scales — a rate at three decimals beside an amount at two — align on the decimal separator explicitly with a grid, rather than hoping padding holds.
4. Reserve the sign column
A column mixing 1,234.00 and (1,234.00) loses its alignment to the closing paren. Reserve the slot on both sides. The three ways to do it are ranked in negative numbers in a financial table.
5. Rule lines carry the arithmetic
A single rule above a subtotal, a double rule under a total. This is four centuries of convention and two CSS declarations:
<Rule variant="subtotal" /> // border-top: 1px solid
<Rule variant="total" /> // border-bottom: 3px double
The rule spans the numeric column only, not the label column. That is what makes it read as accounting rather than as a divider. In a multi-period statement it spans each period column independently, with the gutters left unruled. Never combine a rule with a background fill on the same row — pick one, and it should be the rule.
6. Do not stripe the rows
Zebra striping is what you reach for when the numbers are not aligned. Fix the alignment and the stripes become noise: the tabular figures do the work, and a hairline row separator handles the rest at any density.
7. Never animate a value the reader might trust
A number counting up from zero is unreadable for the duration of the animation, and a reader who glances mid-tween takes away a figure that was never true. Crossfade a changed value if you must signal the change; never interpolate it.
Where this is implemented
Rule— ten lines of code, the highest credibility-per-byte component hereStatementTable— hierarchy, period columns and rules togetherLedgerTable— debit and credit as two aligned columnsTransactionTable— virtualised, and still aligned at 240 rows- Tables, running — with a density toggle, which is the real test of an alignment claim
- Typography — the type system these features live in
- Numerics — rule lines, in full