Data table
A Table that works: a filter, a page size, the columns a person chose, an export; the range and the pager under it; a sorted column and a selected row.
Beta · Content blocks · packages/css/src/components/table.css
.pui-data-table · __toolbar __tools __footer __range around a product Table.
Sizes across, states down.
What it is for
Section titled “What it is for”- One component, no variants — its states are its parts’: the Table’s rows hover and select, the Input focuses, the Buttons press
- Toolbar: Search (Input, a search glyph, ‘Filter publishers…’), Page size (Select — 10 / 50 / 100 / 200 / 500), Columns (a secondary Button with a chevron — the choice is kept per user) and Export (secondary, a download glyph); Has toolbar
- The Table is the Table at Size product — every row and cell exposed; sort the column that is sorted (Has sort on its header cell) and select rows with State selected
- Footer: Range (text — ‘1–5 of 118 publishers · 1 selected’) and Pagination, exposed; Has footer
- 1200 wide for the specimen; FILL in a Card or the page; gap space/16 between the three bands
Use when more rows than fit — a report, a search result, an admin list — and the person needs to filter, page, sort, pick columns or select.
Do not use when the rows fit on one screen with nothing to do to them (Table); the data is a chart’s (Chart recipes); each row is a card of its own (Card grid).
Relates to Table, Table row, Table cell, Pagination, Input, Select, Button - secondary, Checkbox, Badge.
Anti-patterns a page size without the range text · a toolbar with a primary button (the primary belongs to the page) · sort arrows on every header · a selected row with no count in the footer · numbered pills.
Keywords: data table, report table, admin list, filter table, paginated table, sortable, column chooser, export
A composition (Table + TanStack Table), not a registry item
Figma — page Data table: Data table (1 variants)
The markup — copy this
Section titled “The markup — copy this”The class, the parts, and nothing that is not in the package. The same block opens table.css.
<div class="pui-table"><table class="pui-table__table"> <thead><tr class="pui-table__row"><th class="pui-table__cell pui-table__cell--header">Publisher<span class="pui-icon pui-table__sort">…</span></th> <th class="pui-table__cell pui-table__cell--header pui-table__cell--right">Revenue</th></tr></thead> <tbody><tr class="pui-table__row"><td class="pui-table__cell pui-table__cell--row-header"><span class="pui-table__pill">Q1</span></td> <td class="pui-table__cell pui-table__cell--right">$ 8 969 916</td></tr> <tr class="pui-table__row" aria-selected="true"> … </tr></tbody></table></div><div class="pui-data-table"><div class="pui-data-table__toolbar">…</div><div class="pui-table">…</div> <div class="pui-data-table__footer"><span class="pui-data-table__range">1–5 of 118 publishers</span><nav class="pui-pagination">…</nav></div></div>The modifiers
Section titled “The modifiers”| What it is | Note | |
|---|---|---|
| Size | (none) product · --slide (on .pui-table) | — |
| Cell | __cell (body) · __cell--header · __cell--row-header (holds a __pill) · __cell--right | — |
| Row | :hover · selected = [aria-selected=“true”] or --selected | — |
| Accent | data-accent on the table or a parent drives the bar and the pills — table/header/bg and table/header/pill-tint re-resolve per mode (blue navy · red · yellow · multicolor = a neutral-800 bar on neutral-200 pills, the black one); a product dashboard’s table is the neutral one (data-accent=“multicolor” on .pui-table) | — |
Accessibility — measured, not assumed
Section titled “Accessibility — measured, not assumed”Every number here is measured on the rendered component: contrast (npm run contrast:render), the focus ring (npm run focus) and the keyboard (npm run keyboard). The floors are principle 12’s.
Contrast
Section titled “Contrast”| ink on ground | reads | floor | the matrix calls it | |
|---|---|---|---|---|
#544d4d on #ffffff |
8.24 | 4.5 | text.muted on surface.page (promised) | ok |
#0b0202 on #e9e9fb |
17.1 | 4.5 | KPI value, blue tile (promised) | ok |
#0b0202 on #f2f2f7 |
18.38 | 4.5 | text.default on surface.card (promised) | ok |
#ffffff on #0c0b41 |
18.42 | 4.5 | Table header label (promised) | ok |
#0b0202 on #ffffff |
20.51 | 4.5 | text.default on surface.page (promised) | ok |
5 distinct pair(s) rendered, 0 that axe calls a violation.
The focus ring
Section titled “The focus ring”9 of 9 focus stop(s) in this block meet WCAG 2.4.13 on screen — the same crop screenshotted focused and unfocused, and the difference measured.
Keyboard
Section titled “Keyboard”Nothing in this component claims an ARIA role with a keyboard contract; its controls are ordinary ones and the browser already gives them their keys.
Tab order across the whole library is document order over 344 stops, and Shift+Tab is its exact reverse — no keyboard trap anywhere (WCAG 2.1.2).
Pepper UI v1.5.0
The Mobupps design system. 386 tokens, 88 components, 65 pages in the workshop — every page of this site generated from the repository, so nothing here is typed twice.