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.

NameRoleOfficeDays
Ada LovelaceEngineerMarylebone Road3
Mary SomervilleWriterChelsea12
Charles BabbageDesignerDorset Street7
Michael FaradayResearchAlbemarle Street120
PropDescriptionDefaultControl
sizerow height: 32, 40 (default) or 48px. A control fits in a row without growing it8101210
  • 8
  • 10
  • 12
alignvertical alignment of every cell: baseline (default) lines up first lines, middle centres, top for cells of very different sizesbaselinetopmiddlebaseline
  • baseline
  • top
  • middle
dividerslines between rows (default), between rows and columns (grid), or nonerowsgridnonerows
  • rows
  • grid
  • none
hoverhighlight the row under the pointer, for rows that are things to pickfalse
stripedshade every second row, for wide tablesfalse

The other parts

Per-cell and per-region decisions, which the panel above deliberately does not drive.

TableRow

PropDescriptionDefault
selectedshows the row as selected. You hold the selection; usually paired with a checkbox in the rowfalse

TableCell

PropDescriptionDefault
scopemakes the cell a header: col for a column header, row for the cell that names its rowcolrow
justifyhorizontal alignment of this cell's content. A header takes its column'sstartendcenterstart
numerica number: right-aligned, with equal-width digits so a column lines upfalse
paddingreplaces the cell's padding for special content: none for an image or swatch, control for a button or select, checkbox for a selection checkboxnonecontrolcheckbox
colSpanthe number of columns the cell spans
rowSpanthe number of rows the cell spans

TableContainer

PropDescriptionDefault
labelthe 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.

TokenDescriptionLightDark
--table-stripethe zebra film on every second body row, 2%. Lighter than --color-wash, which a code chip needs at 4% where a whole row does notcolor-mix(in srgb, var(--neutral-0) var(--opacity-0-5), transparent)color-mix(in srgb, var(--neutral-100) var(--opacity-0-5), transparent)
--table-selectedthe 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 navcolor-mix(in srgb, var(--neutral-0) var(--opacity-1-5), transparent)color-mix(in srgb, var(--neutral-100) var(--opacity-1-5), transparent)
--table-hoverthe 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 modescolor-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.