Skip to main content

Design system components

TableHeaderCell

A table header cell (<th>). scope=col (default) carries an optional three-state DS-Icon sort control and aria-sort; scope=row marks the identifying cell of a body row so screen readers announce the row context with every other cell.

Status: stable. Group: data-display. Markdown version: /design-system/components/table-header-cell.md

When to use

TableHeaderCell names a column or row with correct scope semantics, and hosts sortable column headers driven by useTableSortable; sort state is announced via aria-sort, never by caret colour alone.

When not to use

  • Do not render it outside a table.
  • Do not make a scope=row header sortable; row headers are not sort controls.
  • Do not convey sort state with the caret colour alone.

Look-alikes

  • TableCell: Do not use it for a header; use TableHeaderCell so column/row context is announced.

Props

TableHeaderCell props
PropTypeDefaultDescriptionRequired
align"center" | "end" | "start"startCell content alignment.No
scope"row" | "col"colHeader scope. col (default) labels a column; row makes the cell the accessible name for its row; use it on the identifying cell of each body row so screen readers announce the row context with every other cell.No
sortablebooleannoneRenders the sort button, which calls onSort on click; the consumer owns sortDirection. Column headers only: with scope="row" it is silently ignored and no button renders. Without onSort the button does nothing.No
sortDirection"none" | "ascending" | "descending"noneCurrent sort state; drives aria-sort and the caret icon. Column headers only; ignored with scope="row".No
onSort() => voidnoneCalled when a sortable header is activated.No

Variants

  • Allowed align values: start, center, end (default start).
  • Allowed scope values: col, row (default col).
  • Allowed sortDirection values: none, ascending, descending (default none).

Invalid combinations

The contract records no invalid combinations.

Keyboard

TableHeaderCell keyboard behaviour
KeyBehaviour
TabMoves focus to the control and on to the next focusable element. (standard)
EnterActivates the focused control. (standard)
SpaceActivates or toggles the focused control. (standard)

Rows marked (standard) name a key the contract lists without a description; the behaviour shown is the usual one for that key.

Rules

Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.

TableHeaderCell rules
LevelRuleHow it is checked
MUSTARIA: Native <th> with an explicit scope (col or row)Not yet verified: no evidence record. WCAG 4.1.2.
MUSTARIA: Sortable column headers expose aria-sort and toggle via an inner button.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTARIA: The sort caret is decorative (aria-hidden); state is carried by aria-sort, never the icon alone.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTRequired stories produce no axe violations at the configured severity.Automated by npm run test:stories:matrix:ci. WCAG 2.5.8.
MUSTThe accessibility tree (role, name, state) matches the committed snapshot for every required story.Automated by npm run test:stories:matrix:ci. WCAG 1.3.1, 4.1.2.
MUSTThe component remains legible and operable under a real browser forced-colors: active pass.Automated by npm run test:stories:hc.
MUSTEvery documented keyboard interaction works as specified.Not yet verified: no evidence record. WCAG 2.1.1, 2.1.2.
MUSTA human completed an end-to-end accessibility review of this component.Manual review, last on 2026-11-04.

Install and import

  • Install: npm install @digitaltableteur/react (version 0.9.1, restricted access on npm).
  • Import: import { TableHeaderCell } from "@digitaltableteur/react";
  • Entry point: @digitaltableteur/react/content exports it too.
  • Styles: import @digitaltableteur/react/style.css once at the app boundary.