Table
NewRows of records on hairlines: orders, products, customers. Quiet headings that stay put while the rows scroll, a soft hover, and a line of teal on the rows you have chosen.
Basics
Plain table elements dressed by the kit, so bring any data engine (Orbit Commerce's store dashboard uses TanStack Table with virtual rows). Links in cells read as ink until pointed at: the row is the target.
| Order | Date | Customer | Payment | Fulfilment | Total |
|---|---|---|---|---|---|
| #1043 | 29 Sep, 14:02 | Sarah Whitfield | Paid | Unfulfilled | £240.00 |
| #1042 | 29 Sep, 11:47 | James Okafor | Payment pending | Unfulfilled | £89.50 |
| #1041 | 28 Sep, 19:20 | Priya Shah | Paid | Fulfilled | £1,120.00 |
| #1040 | 28 Sep, 09:05 | Tom Hughes | Refunded | Fulfilled | £34.99 |
| #1039 | 27 Sep, 16:33 | Eleanor Price | Paid | Fulfilled | £412.80 |
Selection
A Checkbox column; the head box goes part-ticked while some rows are chosen. Chosen rows take a teal line on their leading edge and a tint. While anything is selected, the head row can make way for the actions that apply to the selection.
Sorting
Give a heading sort and onSort and it becomes a button with an arrow; the header cell carries aria-sort. Press Date or Total.
Frame, scrolling and pages
TableFrame is the hairline box with its own scroll area: set height and the headings stay put while rows scroll. footer pins Pagination under it. Its ref goes to the scroll area, for virtual rows.
| Order | Customer | Payment | Total |
|---|---|---|---|
| #1043 | Sarah Whitfield | Paid | £240.00 |
| #1042 | James Okafor | Payment pending | £89.50 |
| #1041 | Priya Shah | Paid | £1,120.00 |
| #1040 | Tom Hughes | Refunded | £34.99 |
| #1039 | Eleanor Price | Paid | £412.80 |
| #1038 | Sarah Whitfield | Paid | £240.00 |
| #1037 | James Okafor | Payment pending | £89.50 |
| #1036 | Priya Shah | Paid | £1,120.00 |
| #1035 | Tom Hughes | Refunded | £34.99 |
| #1034 | Eleanor Price | Paid | £412.80 |
| #1033 | Sarah Whitfield | Paid | £240.00 |
| #1032 | James Okafor | Payment pending | £89.50 |
Density
Rows are 56px (comfortable, the default) or 44px (compact) for long lists and side panels.
| Order | Date | Customer | Payment | Fulfilment | Total |
|---|---|---|---|---|---|
| #1043 | 29 Sep, 14:02 | Sarah Whitfield | Paid | Unfulfilled | £240.00 |
| #1042 | 29 Sep, 11:47 | James Okafor | Payment pending | Unfulfilled | £89.50 |
| #1041 | 28 Sep, 19:20 | Priya Shah | Paid | Fulfilled | £1,120.00 |
| #1040 | 28 Sep, 09:05 | Tom Hughes | Refunded | Fulfilled | £34.99 |
| #1039 | 27 Sep, 16:33 | Eleanor Price | Paid | Fulfilled | £412.80 |
In both themes
Guidelines
- Lead with the thing people scan for (the order number, the product name) and keep it one line where you can.
- Status in words on a badge, never colour alone. Brand teal is not a status colour: it marks selection.
- One primary action per page, outside the table. Row actions go in a menu at the end of the row.
- Keep headings short and in sentence case; the heading names the column, the cells carry the detail.
Accessibility
- Real table elements: headers are th with scope="col", so screen readers announce the column for each cell.
- Sortable headings are buttons inside the th, and the th carries aria-sort.
- A selected row carries aria-selected; every row checkbox names its row ("Select order #1043").
- A clickable row also has a real link in its first cell, so keyboard users can open it with Tab and Enter.
Props
Every part also accepts the native attributes of its element.
TableFrame
| Prop | Type | Default | Description |
|---|---|---|---|
height | number | string | None | Height of the scroll area. The headings stick while rows scroll. |
footer | ReactNode | None | Pinned under the scroll area, e.g. Pagination. |
ref | Ref<HTMLDivElement> | None | The scroll area, for virtual rows. |
Table
| Prop | Type | Default | Description |
|---|---|---|---|
density | 'comfortable' | 'compact' | 'comfortable' | 56 or 44px rows. |
layout | 'auto' | 'fixed' | 'auto' | fixed pins column widths to a colgroup: steady columns under virtual rows. |
TableRow
| Prop | Type | Default | Description |
|---|---|---|---|
selected | boolean | false | Teal line and tint; aria-selected. |
interactive | boolean | false | Pointer cursor: the row opens something. |
TableHeaderCell and TableCell
| Prop | Type | Default | Description |
|---|---|---|---|
align | 'start' | 'center' | 'end' | 'start' | Text alignment. |
sort | 'ascending' | 'descending' | 'none' | None | Header cell: current sort; sets aria-sort. |
onSort | () => void | None | Header cell: makes the heading a sort button. |
numeric | boolean | false | Cell: tabular figures, aligned to the end. |
truncate | boolean | false | Cell: one line with an ellipsis (set a column width). |
| Part | Element |
|---|---|
TableFrame | div (frame) › div (scroll area) |
Table | table |
TableHead | thead |
TableBody | tbody |
TableRow | tr |
TableHeaderCell | th |
TableCell | td |