Skip to content

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.

Data table — every size and every stateOpen in the workshop

Sizes across, states down.

  • 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 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>
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) —

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.

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.

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.

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.