dense-ui
Tables and Dashboards
A dense surface exists so someone can compare values, not read them. Every default here follows from that: the eye travels down a column, so a column must be internally consistent before a row is pretty. Default to the tightest density the pointer allows, put every quantity on the right in tabular figures, and cut columns before you cut rows — a column you keep taxes every future scan, a row you keep costs only pixels. When the data and the grid disagree, the data wins; a table is not a place to express a layout idea.
The boundary with the siblings that look like this one: rows and cells are here; axes and marks are charts. The query input, facets, and result ranking above a grid belong to search-filter — this skill starts once the rows have arrived. Empty, loading, and error specs belong to ui-states, which owns the threshold ladder; do not restate it here.
Work inside the table layer the project already has. Check for a headless table library (TanStack Table, AG Grid, an existing <DataTable>) and a virtualizer before writing markup, and express density, alignment, and sticky behavior through what it exposes. Row padding and text sizes come from the project's spacing and type scales — spacing owns the scale — never from pixel values invented for one table.
Quick Reference
| Open it when | File |
|---|---|
| You are specifying real columns and need the alignment, number format, width behavior, truncation, and empty-value rule for a given column type | table-spec.md |
Decision: how dense?
Ship at most three modes — comfortable, default, compact — and change exactly two things between them: vertical row padding and text size. Column widths, alignment, radii, and icon sizes stay fixed, or switching reflows the table and the user loses their place mid-scan.