Accessibility
What tablekit does for keyboard, screen reader and low-vision users, and what you still need to do.
Target: WCAG 2.2 AA. Every build runs axe-core in CI against the table, its states and the composable API.
What you get
- A semantic
<table>with<th scope="col">,aria-sorton sorted headers, andaria-rowcount/aria-rowindexso screen readers know the full size across pages. In the stacked layout, rows become a<ul>of cards with<dl>field pairs instead of CSS-reflowed table cells, which lose their semantics in several browsers. - Keyboard:
- Tab reaches every control.
- Arrow keys, Home and End move between cells. When a cell has a link or button, focus goes to it.
- Enter on a cell runs
onRowClick. - Shift+click or Shift+Enter on a header adds a secondary sort.
- Resize handles are focusable separators you move with ←/→ (hold Shift for bigger steps). Double-click resets the width.
- Announcements through a polite live region: sort changes (“Sorted by Total, descending”), result counts while searching, page changes (“Page 2 of 5”), and selection counts.
- Menus and popovers: focus moves in when they open and returns to the trigger on close. Escape closes them. Row menus support arrow keys, Home and End.
- Labels: every checkbox has a meaningful name (“Select Ava Patel”). The select-all checkbox shows a mixed state. Icon-only buttons have
aria-labels. - Visual:
- Tokens meet 4.5:1 text contrast in light and dark.
- Focus rings are always visible (
:focus-visible). - Badges pair a dot with text, so status doesn’t rely on color alone.
forced-colors(Windows High Contrast) is supported.prefers-reduced-motionturns off animation.
What you still own
- An accessible name. Pass
titlein the schema, oraria-labelonTable.Root. - Custom cells. If you render buttons or links yourself, give them names.
- Contrast after theming. If you override tokens, check them. The Playground is a quick way to preview.
- Localization. Pass a
labelsobject to translate every string, including the announcements:
<DataTable
labels={{
search: "Suchen",
selected: (n) => `${n} ausgewählt`,
sortedAsc: (c) => `Sortiert nach ${c}, aufsteigend`,
}}
…
/>