Components

Table

A data table with styled header, body, footer, and row hover states. Supports striped rows for easier scanning.

Table is a static primitive: it ships its own horizontally scrolling, rounded, bordered container, so you never have to wrap it yourself. Sorting, row selection wiring and pinned columns live one tier up, in the DataTable composite.

Basic usage

InvoiceCustomerAmountStatus
INV-001Alice Johnson$250.00Paid
INV-002Bob Smith$150.00Pending
INV-003Carol White$350.00Paid
INV-004David Brown$450.00Overdue

Striped variant

Use variant="striped" — or the boolean shorthand striped — for alternating row backgrounds.

InvoiceCustomerAmountStatus
INV-001Alice Johnson$250.00Paid
INV-002Bob Smith$150.00Pending
INV-003Carol White$350.00Paid
INV-004David Brown$450.00Overdue

With footer

InvoiceCustomerAmount
INV-001Alice Johnson$250.00
INV-002Bob Smith$150.00
INV-003Carol White$350.00
INV-004David Brown$450.00
Total$1,200.00

Selected rows

Set data-state="selected" on a TableRow to mark it as chosen. The row picks up a muted tint and an accent rule along its bottom edge.

InvoiceCustomerAmount
INV-001Alice Johnson$250.00
INV-002Bob Smith$150.00
INV-003Carol White$350.00
INV-004David Brown$450.00

Two signals, on purpose. The bg-muted tint is deliberately faint — 1.07:1 against --card in light, 1.10:1 in dark — so text in the row stays comfortable to read. On its own it would fall short of the 3:1 that WCAG 1.4.11 asks of a meaningful non-text cue, and no tint is both faint and 3:1. The rule that does carry the information is border-b-app-accent-ink: 6.51:1 against the page or card fill, 6.09:1 at worst when the row sits on --muted. Note the -ink step — the bare accent (gold-300) is a background token and manages only 1.86:1 as a mark on white.

The rule sits on the bottom edge rather than the left because a <tr> scrolls sideways with the table: the left edge drifts out of the scroll box (and gets covered by DataTable's sticky pinned cells), while the bottom edge spans the full width and stays visible. Border width does not change with state, so nothing shifts.

Density

Row height is not hard-coded. TableHead takes its height from --table-head-h and TableCell its vertical padding from --row-py, so any table below a data-density="compact" subtree tightens from 40px/12px to 32px/6px without a single prop change. Wire the attribute with DensityProvider from @comitor/ui/shell; the scale is vertical only, so columns keep their padding and text never crowds the cell edge.

InvoiceCustomerAmount
INV-001Alice Johnson$250.00
INV-002Bob Smith$150.00
INV-003Carol White$350.00
INV-004David Brown$450.00

Caption

TableCaption renders a real <caption>, placed below the table, and names it for screen readers.

Invoices issued in the last 30 days.
InvoiceCustomerAmount
INV-001Alice Johnson$250.00
INV-002Bob Smith$150.00

Props

PropTypeDefaultDescription
variant'default' | 'striped''default'Visual style. Striped tints every even body row with the table-row-stripe token.
stripedbooleanfalseConvenience alias for variant="striped". Either one turns striping on.
...propsReact.ComponentProps<'table'>All native table attributes are forwarded to the inner <table>; className lands there too, not on the scroll container.

Sub-components

Every part is a thin wrapper over its native element and forwards all of that element's props.

PropTypeDefaultDescription
TableHeaderReact.ComponentProps<'thead'>The <thead>. Carries the muted header fill and the bottom border.
TableBodyReact.ComponentProps<'tbody'>The <tbody>. Drops the border on the last row so it does not double up with the container edge.
TableFooterReact.ComponentProps<'tfoot'>The <tfoot>. Muted fill, top border, medium weight — for totals and summary rows.
TableRowReact.ComponentProps<'tr'>A <tr> with hover tint. Set data-state="selected" to mark it as chosen.
TableHeadReact.ComponentProps<'th'>A header cell: uppercase, tracked, muted; its height follows the --table-head-h density token.
TableCellReact.ComponentProps<'td'>A body cell; its vertical padding follows the --row-py density token.
TableCaptionReact.ComponentProps<'caption'>A caption rendered below the table, in muted text.

Accessibility

  • Uses semantic <table>, <thead>, <tbody>, <tfoot>.
  • Column headers use <th> for proper screen reader announcement.
  • The horizontal scroll container is built in — Table wraps itself in a data-slot="table-container" div with overflow-auto, so wide tables scroll on small screens without an extra wrapper.
  • Give the table a name with TableCaption, or an aria-label on the <table>, whenever a page shows more than one.
  • Selection is never colour-only: a selected row is marked by an accent rule at 6.51:1 as well as its tint, and the tint alone would not clear the 3:1 non-text threshold. Pair it with a checkbox or a text summary of the selection all the same.