Table
Rows of records, in a real table.
import { Table, TableHead, TableBody, TableRow, TableCell } from "@dede-style/ui/table", then compose it the way you would write the HTML. The element already knows how to be a table; these parts add the system’s chrome and the few decisions a table has to carry, and nothing else.
| Name | Role | Office | Days |
|---|---|---|---|
| Ada Lovelace | Engineer | Marylebone Road | 3 |
| Mary Somerville | Writer | Chelsea | 12 |
| Charles Babbage | Designer | Dorset Street | 7 |
| Michael Faraday | Research | Albemarle Street | 120 |
| Prop | Description | Default | Control |
|---|---|---|---|
| size | row height: 32, 40 (default) or 48px. A control fits in a row without growing it81012 | 10 |
|
| align | vertical alignment of every cell: baseline (default) lines up first lines, middle centres, top for cells of very different sizesbaselinetopmiddle | baseline |
|
| dividers | lines between rows (default), between rows and columns (grid), or nonerowsgridnone | rows |
|
| hover | highlight the row under the pointer, for rows that are things to pick | false | |
| striped | shade every second row, for wide tables | false |
The other parts
Per-cell and per-region decisions, which the panel above deliberately does not drive.
TableRow
| Prop | Description | Default |
|---|---|---|
| selected | shows the row as selected. You hold the selection; usually paired with a checkbox in the row | false |
TableCell
| Prop | Description | Default |
|---|---|---|
| scope | makes the cell a header: col for a column header, row for the cell that names its rowcolrow | |
| justify | horizontal alignment of this cell's content. A header takes its column'sstartendcenter | start |
| numeric | a number: right-aligned, with equal-width digits so a column lines up | false |
| padding | replaces the cell's padding for special content: none for an image or swatch, control for a button or select, checkbox for a selection checkboxnonecontrolcheckbox | |
| colSpan | the number of columns the cell spans | |
| rowSpan | the number of rows the cell spans |
TableContainer
| Prop | Description | Default |
|---|---|---|
| label | the scrolling region's accessible name, announced when it takes focus. Required |
These are documented here and set per instance, never by a panel. A control that set numeric would set it on every cell at once, which is exactly the misuse placing it on the cell prevents; so they are shown live on the Overview, in the rows that mix text and numbers, and listed here so a reader can find them. A row’s selected is the same kind of thing: state the consumer holds per row, shown live in the selection band on the Overview. TableHead, TableBody and TableFoot take no props of their own.
Tokens
What a theme can move, where the props above are what an instance can move.
| Token | Description | Light | Dark |
|---|---|---|---|
| --table-stripe | the zebra film on every second body row, 2%. Lighter than --color-wash, which a code chip needs at 4% where a whole row does not | color-mix(in srgb, var(--neutral-0) var(--opacity-0-5), transparent) | color-mix(in srgb, var(--neutral-100) var(--opacity-0-5), transparent) |
| --table-selected | the film on a selected row, 6% — above the stripe and the hover, so a selected row never reads as either. Its own token so the table can move without the nav | color-mix(in srgb, var(--neutral-0) var(--opacity-1-5), transparent) | color-mix(in srgb, var(--neutral-100) var(--opacity-1-5), transparent) |
| --table-hover | the film on the row under the pointer, composed over stripe and selected alike. 4%, between the stripe and selected, so all three read apart in both modes | color-mix(in srgb, var(--neutral-0) var(--opacity-1), transparent) | color-mix(in srgb, var(--neutral-100) var(--opacity-1), transparent) |
A token appears here only when the value is not already reachable. Anything that resolves through a foundation token or a role needs none, because overriding that already moves it — and a second dial wired to the same value would let this one component drift from everything sharing the role. What is left is the values with no other lever at all.
These are public and overridable — there is no private custom property, because anything on :root is reachable by any stylesheet. What replaces privacy is a stability contract: primitives may move, roles are the guarantee, and these are the per-component escape from both.