Data table
Rows of records people scan, sort and select: hairline rows, small uppercase headers, and numbers right-aligned so they line up.
- CSS
css/components/table.css- JS
js/components/table.js (optional)
01 — Variants
Sortable and selectable
Click a header to sort. data-select adds checkboxes and a select-all. On phones, .table--stack turns each row into label and value pairs.
Compact, no script
.table--compact has 32px rows. A plain table needs no script.
| Token | Value | px |
|---|---|---|
--control-h | 2.5rem | 40 |
--control-h-touch | 2.75rem | 44 |
--control-h-lg | 3.5rem | 56 |
Empty
No rows? Put a compact empty state in one full-width cell.
| Title | Edited |
|---|---|
No draftsPosts you start and don’t publish are kept here. | |
02 — Behavior
Script
| Name | Detail |
|---|---|
| Sorting | th[data-sort] sorts as text, number or date. A cell’s data-value or <time datetime> sorts in place of its text. Event: jewel:sort. |
| Selection | data-select on the wrap. Selected rows get aria-selected="true". Event: jewel:selectionchange; API wrap.jewelTable.selected(). |
| Size | Wide tables scroll sideways inside .table-wrap. .table-wrap--tall fixes the height with a sticky header. |
| Rows as links | .table__link on a row’s main link makes the whole row clickable. |
03 — Usage
Use it for
- Records people compare across several columns.
.is-numon number columns, so digits line up.
Avoid
- Layout. Use the grid.
- Two columns of facts. Use a meta list.
- A chart’s data. Charts keep their own table view.
04 — Accessibility
Notes
- A caption names the table. Headers use
scope. - Sortable headers are buttons that announce the sort direction (
aria-sort).
05 — Without Jewel’s JS
What the script does
A plain table needs no script. The script sorts rows, adds selection checkboxes and phone labels, and announces changes. The CSS reads only the markup below, so a framework can sort and select its own rows and leave out table.js and data-component.
| State | Set this | Notes |
|---|---|---|
| Sortable header | <button class="table__sort" type="button"> inside the th, holding its text | Sort the rows yourself. |
| Sorted | aria-sort="ascending" or "descending" on that th | Shows the arrow. Remove it from the other headers. |
| Selection column | A first th.table__select and td.table__select, each holding <label class="choice"><input type="checkbox"> and a visually hidden name | “Select all rows” in the header, “Select Night bus” in a row. |
| Selected row | aria-selected="true" on the tr, "false" otherwise | Selected fill. Set the select-all box’s indeterminate property when only some rows are selected. |
| Phone labels | data-label="Posted" on each cell of a .table--stack | The column’s header text, shown beside the value on phones. |
| Scrolling | tabindex="0", role="region" and aria-labelledby (the caption’s id) on .table-wrap | Only when it scrolls, so keyboard users can reach it. |
Events and keyboard
- The script announces “Sorted by Title, ascending” and “All rows selected” in a visually hidden polite live region.
jewel:sortandjewel:selectionchangedon’t fire without the script.