Table
StableA semantic data table for comparing records: sortable columns, selectable rows, density-aware rows, and a focusable scroll region on narrow screens.
| Token | Uses |
|---|---|
| color.text.default | 212 |
| color.border.strong | 48 |
| Token | Uses |
|---|---|
| color.text.default | 212 |
| color.border.strong | 48 |
When to use
Use it for
- Records with the same attributes that users compare, sort or act on: tokens, components, pull requests.
- Sortable headers (sortDirection + onSort, or useTableSort) on columns users reorder by.
- Selectable rows: a Checkbox in the first cell, selected on the row, bulk actions above the table.
- Compact density for expert, data-heavy screens.
Not for
- Layout: never use a table to position content.
- Fewer than three attributes per item: use a list.
- Editing many cells inline like a spreadsheet: that needs a grid pattern, not in the system yet.
Do and don't
Every example is rendered with the real component, so it always matches what ships.
| Button | 1,204 |
| Badge | 87 |
Right-align numbers
numeric aligns digits on the right with tabular figures, so values compare at a glance.
| No pull requests yet. Changes you sync from Figma appear here. |
Say what to do when it is empty
TableEmpty keeps the header in place and explains how rows will appear.
| color.signal.default |
Color as the only row state
A tinted row alone does not say selected. Pair it with a checked Checkbox.
Accessibility
- Native table, thead, th scope col and td: screen readers announce headers with each cell.
- Sortable headers contain a button; the th gets aria-sort ascending or descending on the sorted column only.
- Selected rows set aria-selected and use the selected surface plus a checked Checkbox (never color alone).
- The scroll container is a labelled region with tabIndex 0, so keyboard users can scroll wide tables (named by the caption or aria-label).
- Row height and cell padding follow density: 48/36px rows, both above the 24px target minimum for the controls inside.
Anatomy and tokens
Read from packages/react/src/components/Table/Table.module.css at build time. Every value is a token: open one to see its
alias chain in both themes.
- Scroll region (focusable, labelled)
.scroller - Scroll region (focusable, labelled), focus
.scroller:focus-visible - Table
.table - Caption
.caption - Header cell
.head - Cell
.cell - gridLines
.gridLines .cell + .cell, .gridLines .head + .head - Selection cell
.select - Row
.row - tbody, hover
@media (hover: hover) .tbody .row:hover - Row
.row[data-selected] - Sort button
.sortButton - Sort button, hover
@media (hover: hover) .sortButton:hover - Sort button, focus
.sortButton:focus-visible - Sort button
.sortButton[data-sorted] - Sort button
.sortButton[data-sorted] .sortIcon - Empty row
.empty
Props
Parsed from the TypeScript types in packages/react/src/components/Table/Table.tsx, so this table cannot drift from the
code.
It also accepts every native <HTMLTableElement> attribute.
| Prop | Type | Default | Description |
|---|---|---|---|
caption | ReactNode | none | Visible caption above the table. Also names the scroll region. |
stickyHeader | boolean | false | Accessible name when there is no visible caption, for example "Tokens changed in this pull request". */ 'aria-label'?: string; /** Keep the header row visible while the table scrolls inside its container. |
maxHeight | string | none | Max height of the scroll container (CSS length from a token, for example var(--size-120)). Enables vertical scroll. |
gridLines | boolean | false | Hairlines between columns as well as rows. Default `false`. |
Comes with useTableSort, each accepting native <div> attributes.
Playground
Change the props and see the real Table update. Controls are generated from the TypeScript props; nothing here changes the system.
<Table caption="Tokens in this release" />Workbench
For contributors: every state with the accessibility panel, in the component workbench (Storybook). The stories double as visual test fixtures; the guidance on this page is the user documentation (open Table in Storybook) .
Embed Storybook stories here (4)
Loads from https://bridge-storybook.floriangouloubi.com (set STORYBOOK_URL at build time). If Storybook is not running, the frames stay
empty.
SortableAndSelectable
StickyHeader
Empty
AllStates
Changelog
- New
Data display: Card restyled as a flat plate with an `interactive` option and padding sizes; Badge gains `appearance` (subtle, now the default, or solid), a dot, icons and sizes (existing solid badges must pass `appearance="solid"` to keep their look); new Avatar and AvatarGroup, Table (sortable headers with aria-sort, selectable rows, sticky header, focusable scroll region, useTableSort), Accordion, Tag (removable, toggleable), Divider and Kbd. Adds avatar, tag, kbd, divider and accordion component tokens.
Unreleased (current version 0.1.0, no release tagged yet) , Oct 17, 2026
- New
Bridge token system v1. Adds a brand mapping layer (`bridge` default, `ocean` alternate) between primitives and semantic tokens, a component tier (button, input, checkbox, switch, slider, card, popup, dialog, tooltip, toast, table, tabs, progress, nav), comfortable and compact density, motion durations and easings with a reduced-motion override, layers (z-index), icon sizes, breakpoints, a 1.2 type scale from 14px with tracking tokens, and self-hosted IBM Plex Sans and Mono (`@bridge-ds/tokens/fonts.css`). Primitives are regenerated in OKLCH on one lightness ladder. The build now writes `dist/resolved.json` and fails when any pair in `contrast.checks.json` drops below its WCAG minimum in any brand and theme. Fixes `color.border.strong` against `color.surface.default` (now at least 3:1 in both themes).
Unreleased (current version 0.1.0, no release tagged yet) , Oct 17, 2026