Data table
Many records in one view, with search, filters, sorting, column choice, selection and paging.
Left out of Brand expressive Brand expressive avoids tools people use for hours. Data table is for acting on many records in bulk; here people browse and explore the story. Use instead: Card
See it live
npm run storybookWhen to use it
- Use it when: People find, compare and act on many records, often in bulk.
- Not when: A handful of rows (a plain table), or items people browse rather than compare (cards).
Instead:
How Brand expressive uses it
Words
- Headlines are short and bold. Three to six words.
- Verbs that move: Join, Try, Go.
- Playful in headlines, plain in errors.
- Use: Join, Drop, Go, Love. Avoid: Synergy, Leverage, Kindly.
The labels components use, from the voice tokens:
content.action.commit: Gocontent.action.cancel: Not nowcontent.action.destroy: Removecontent.action.retry: Try againcontent.action.empty-action: Explore the drop
See Voice and tone for the whole voice.
Accessibility and keyboard
| Keyboard | What happens | Not |
|---|---|---|
| undefined | Every tool and row action is reachable by keyboard. | undefined |
Test before you ship:
- It stays a real table with headers; sorting and selection are announced. undefined
Use it in code
npx open-ds add data-table
The component reads only component keys and semantic tokens, so it follows the brand and mode on <html>.
Tokens it reads
Data table reads 12 tokens of its own, each an alias of a semantic token. Each row is one part: its own token, the semantic it aliases, and the value it ends at. Change a semantic and every component that reads it follows; override one of Data table's tokens to change Data table alone.
Brand expressive decides what changes when you switch theme. Here a switch changes colors only, so the marked rows differ between light and dark; everything else holds steady.
| Part | Its token and the semantic it aliases | Value |
|---|---|---|
| Shared | ||
| Card radius | component.card.radiusradius.containervar(--card-radius) | radius.24:1.5rem |
| Table row height | component.table.row-heightsize.rowvar(--table-row-height) | size.52:3.25rem |
| Table row border Changes with theme | component.table.row-bordercolor.border.defaultvar(--table-row-border) | Lighttaupe.light.200:#F2DCCFDarktaupe.dark.100:#33241F |
| Data table | ||
| Gap | component.data-table.gapspace.gap.tightvar(--data-table-gap) | space.8:0.5rem |
| Label hover Changes with theme on hover | component.data-table.label-hovercolor.text.primaryvar(--data-table-label-hover) | Lighttaupe.light.900:#1F1411Darktaupe.dark.900:#FFF8F3 |
| Padding | component.data-table.paddingcomponent-spacing-map.x2var(--data-table-padding) | space.8:0.5rem |
| Gap 2 | component.data-table.gap-2space.gap.defaultvar(--data-table-gap-2) | space.16:1rem |
| Corner | component.data-table.radiusradius.badgevar(--data-table-radius) | radius.999:9999px |
| Padding, left and right | component.data-table.padding-xcomponent-spacing-map.x2var(--data-table-padding-x) | space.8:0.5rem |
| Padding, top and bottom | component.data-table.padding-ycomponent-spacing-map.x1var(--data-table-padding-y) | space.4:0.25rem |
| Background hover Changes with theme on hover | component.data-table.background-hovercolor.surface.sunkenvar(--data-table-background-hover) | Lighttaupe.light.100:#FCEFE6Darktaupe.dark.100:#33241F |
| Text Changes with theme | component.data-table.labelcolor.text.mutedvar(--data-table-label) | Lighttaupe.light.600:#6B544ADarktaupe.dark.400:#B5988A |
| From the system | ||
| Edge Changes with theme | color.border.default straight from the system, through --border | Lighttaupe.light.200 · #F2DCCFDarktaupe.dark.100 · #33241F |