Table
The presentational table primitive — semantic elements, tokenised chrome, no data layer.
Table vs Data Table: this one is markup only — semantic elements with tokenised chrome and no data layer. You bring the rows and render them.
DataTableRoot is this table plus TanStack Table: sorting, filtering and pagination. It costs a
dependency, so it lives on its own entry point (@kanzo-tech/ui/table) with the peer marked
optional — importing it never affects consumers who only want the markup.
This pair is a rule, not a one-off — see the engine rule. A component that
needs an engine ships as two: the presentational half in the root barrel, the connected half on the
engine's subpath, rendering the first. StatTile (a figure you pass) and
ChartStat (a figure it queries, reacting to the crossfilter) are the same pair over Mosaic. It is
not duplication: the connected half renders the presentational one. It is what keeps showing a
number from a REST call free of DuckDB-WASM.
| Contract | Region | State |
|---|---|---|
| Something is eating the bell-ropes | Thornmarch | |
| A wyrm under the granary | Thornmarch | |
| Nine goats, one road | Thornmarch | |
| The miller's second cellar | Thornmarch |
Usage
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@kanzo-tech/ui";<Table>
<TableHeader>
<TableRow>
<TableHead>Contract</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>Nine goats, one road</TableCell>
</TableRow>
</TableBody>
</Table>This is the markup layer only. For sorting, filtering and pagination, use
DataTable — it composes these same parts over TanStack Table
on a separate entry point.
Anatomy
<Table>
<TableCaption />
<TableHeader>
<TableRow>
<TableHead />
<TableBody>
<TableRow>
<TableCell />
<TableFooter>
<TableRow>
<TableCell />Table renders its own overflow-auto wrapper, so a wide table scrolls inside itself rather
than pushing the page sideways.
Striped
variant and isHoverable land as data-* attributes on the <table>; the rows read them
through a group selector, so no row has to be told which variant it is in.
| Role | Duty |
|---|---|
| Warden | Holds the line and signs for the party |
| Scout | Walks it first, alone, and comes back |
| Cantor | Keeps the wards lit and the dead quiet |
| Sapper | Opens doors that were not doors |
| Alchemist | Carries what must not be dropped |
| Archivist | Reads the contract before anyone signs it |
Footer vs caption
Two parts sit at the bottom of a table and are easy to mix up:
TableFooteris a<tfoot>— a row of the table, for a summary the columns add up to: a total, a count. It lines up with the columns above it (right-align a figure,colSpana label), and reads as the emphasised last row.TableCaptionis a<caption>— a label for the whole table, not a row and not tied to the columns. It names what the table is ("Contracts the Amber Hall has on the board") and, viacaption-bottom, renders beneath it.
So a bottom row of totals is the footer; a sentence describing the table is the caption. If you reached for a footer and got descriptive text, you wanted the caption — and the other way round.
| Hall | Posted |
|---|---|
| Amber Hall | 200 gold |
| Salt | 334 gold |
| The Nine | 329 gold |
| Ash Co. | 704 gold |
| Lanternwood | 397 gold |
| On the board | 1,964 gold |
API Reference
Table
| Prop | Type | Default |
|---|---|---|
variant | "plain" | "striped" | "plain" |
isHoverable | boolean | true |
Every part extends the matching ark.* element (ark.table, ark.thead, ark.tr, …), so
ref and all native table attributes — colSpan, scope, align — pass through.
TableBodyProps is exported, so a wrapper can take the same props without restating them.