Table
Lists records that share the same fields.
Frame
Run Duration Status run_8f2a 1.2s Passed run_8f29 41.0s Failed run_8f28 0.8s Running carddefault
Run Duration Status run_8f2a 1.2s Passed run_8f29 41.0s Failed run_8f28 0.8s Running none
States
Run Status run_8f2a Passed run_8f29 Failed run_8f28 Running rest
Run Status run_8f2a Passed run_8f29 Failed run_8f28 Running hover
Run Status run_8f2a Passed run_8f29 Failed run_8f28 Running selected
Run Status run_8f29 41.0s Failed run_8f2a 1.2s Passed run_8f28 0.8s Running sorted
Run Duration Status loading
Run Duration Status No runs match timeout
empty
Usage
When to use it
- Use
- A handful to a few hundred records a person compares across columns: runs, keys, deploys.
- Use
- Exact values that must line up: durations, counts, costs.
- Use
- A short list written by hand. For sorting, column menus and paging driven by data, use DataGrid.
- Not for
- Each row is one line of mixed content and no columns compare. Use Item.
- Not for
- Label and value pairs about one thing. Use Data list.
- Not for
- Thousands of rows, sorting, column hiding and paging. Use Data grid.
Rules
9 rules
- Must
- Give every Table a title or an aria-label. Because a screen reader announces an unnamed "table", and a page with two cannot be told apart in the tables list.
- Must
- Set numeric on the TableHead and every TableCell of a column of numbers, and give the column one unit. Because numeric right-aligns in tabular figures, so the digits stack by place; mixed units (950ms beside 1.2s) make the eye compare the wrong digits.
- Must
- Set mono on cells holding an ID, a key or a path; never put font-mono or code styling on a cell. Because mono sets type-code-sm, 0.8 of the 13px cells, so the wider Glaze Mono does not read a size bigger than the column beside it.
- Must
- Show a row's outcome with Status in its own cell; never tint the row or colour the text of other cells. Because colour means status only in Glaze and always comes with a word, and a red row loses its meaning in forced colours and to a screen reader.
- Never
- Wrap a Table in a Card or another bordered box; it brings its own shell and panel, or use frame="none" inside a Card's flush content. Because a card around a card is two edges where one closes the group.
- Must
- Show no rows with TableEmpty spanning every column, and loading with TableLoading, inside TableBody. Because the head row stays, so the columns do not jump when rows land, and the message sits where the first row would be.
- Must
- Make a column sortable with sort and onSort on its TableHead, never a click handler on the th. Because TableHead renders a real button and sets aria-sort, so the sort is reachable by Tab and announced with its direction.
- Should
- Put the selection Checkbox first, name each one with its row ("Select run_8f2a"), and pass selected on the TableRow. Because the column keeps 40px and the selected wash, and "Select row 3" says nothing after a sort.
- Should
- Pass stickyHeader with a maxHeight for a table longer than the screen. Because the heads say which column a value is in, and without them a long scroll loses the column names.
State order
6 states, highest first
- 1
- loading
- 2
- empty
- 3
- selected
- 4
- open
- 5
- hover
- 6
- rest
Accessibility
5 checks
- Name the table with title or aria-label; a scroll box that overflows takes a tab stop and the same name.
- A sortable head is a button inside the th, which carries aria-sort ascending, descending or none.
- Selected rows set aria-selected and the open row aria-current; the wash is never the only sign, the checkbox is ticked.
- Heads are 12/500 text-2 (5.5:1), cells text-1; the row rule is decoration and needs no contrast.
- Set aria-busy on the Table while TableLoading shows; the placeholder rows are aria-hidden.
Keyboard
3 keys
- Tab
- Moves to the scroll box while it overflows, then to each sort button, checkbox and row action.
- Enter
- On a sort head, sorts; Space on a checkbox ticks it.
- Arrow keys
- In the focused scroll box, scroll it.
Edge cases
6 edge cases
- Wider than its box
- The panel scrolls sideways; the shell and its footer stay put.
- Long value
- Cells never wrap; put a long value in Truncate inside the cell, which keeps the whole value.
- Missing number
- Write None in the cell; an empty cell reads as a rendering bug.
- Last row
- Drops the row rule; the panel's edge closes it.
- Touch screen
- Row actions and the unsorted sort mark always show, since nothing hovers.
- Dark theme
- The shell, panel and washes flip with their tokens; ticked checkboxes turn to light glass.
API
13 props
frame'card' | 'none'default'card'titleReactNodecountReactNodeactionsReactNodetoolbarReactNodefooterReactNodestickyHeaderbooleandefaultfalsemaxHeightnumber | stringTableHead numeric, sort, onSortboolean, 'asc' | 'desc' | false, (event) => voidTableCell numeric, monobooleandefaultfalseTableRow selected, openbooleanTableEmpty{colSpan, title, description?, action?, reason?}TableLoading{rows?, columns, hidden?}
Tokens
17 tokens
--shell- #f1f1f0
--paper- #ffffff
--line-faint- rgb(5 5 5 / 0.06)
--hover- rgb(5 5 5 / 0.06)
--pressed- rgb(5 5 5 / 0.10)
--row- 36px
--control-sm- 26px
--text-1- rgb(5 5 5 / 0.80)
--text-2- rgb(5 5 5 / 0.60)
--text-3- rgb(5 5 5 / 0.45)
--type-body-sm- 400 13px/1.5 var(--font-sans)
--type-label-sm- 500 12px/1.35 var(--font-sans)
--type-code-sm- 400 calc(13px * var(--mono-scale))/1.5 var(--font-mono)
--radius-shell- 14px
--radius-panel- 11px
--shell-inset- 3px
--z-raised- 10