Table

Lists records that share the same fields.

Frame

  • RunDurationStatus
    run_8f2a1.2sPassed
    run_8f2941.0sFailed
    run_8f280.8sRunning

    carddefault

  • RunDurationStatus
    run_8f2a1.2sPassed
    run_8f2941.0sFailed
    run_8f280.8sRunning

    none

States

  • RunStatus
    run_8f2aPassed
    run_8f29Failed
    run_8f28Running

    rest

  • RunStatus
    run_8f2aPassed
    run_8f29Failed
    run_8f28Running

    hover

  • RunStatus
    run_8f2aPassed
    run_8f29Failed
    run_8f28Running

    selected

  • RunStatus
    run_8f2941.0sFailed
    run_8f2a1.2sPassed
    run_8f280.8sRunning

    sorted

  • RunDurationStatus

    loading

  • RunDurationStatus

    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'
title
ReactNode
count
ReactNode
actions
ReactNode
toolbar
ReactNode
footer
ReactNode
stickyHeader
boolean default false
maxHeight
number | string
TableHead numeric, sort, onSort
boolean, 'asc' | 'desc' | false, (event) => void
TableCell numeric, mono
boolean default false
TableRow selected, open
boolean
TableEmpty
{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

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.