Table
AlphaAccessible data table with automatic ARIA wiring for sorting and row selection. Renders native semantic HTML — no ARIA props required from consumers.
Import
import { Table } from "@compa11y/react";Usage
Example.tsx
import { Table } from "@compa11y/react";
// Basic read-only table
function BasicExample() {
return (
<Table caption="Team members">
<Table.Head>
<Table.Row>
<Table.Header>Name</Table.Header>
<Table.Header>Role</Table.Header>
</Table.Row>
</Table.Head>
<Table.Body>
<Table.Row>
<Table.Cell>Alice</Table.Cell>
<Table.Cell>Engineer</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
);
}
// Sortable table
function SortableExample() {
const [sortKey, setSortKey] = useState<string | null>(null);
const [sortDir, setSortDir] = useState<"none" | "ascending" | "descending">("none");
return (
<Table
caption="Products"
sortKey={sortKey}
sortDirection={sortDir}
onSortChange={(key, dir) => { setSortKey(key); setSortDir(dir); }}
>
<Table.Head>
<Table.Row>
<Table.Header sortKey="name">Name</Table.Header>
<Table.Header sortKey="price">Price</Table.Header>
</Table.Row>
</Table.Head>
<Table.Body>
<Table.Row>
<Table.Cell>Widget</Table.Cell>
<Table.Cell>$9.99</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
);
}
// Selectable table
function SelectableExample() {
const [selected, setSelected] = useState<string[]>([]);
const users = [{ id: "1", name: "Alice" }, { id: "2", name: "Bob" }];
return (
<Table caption="Users" selectedRows={selected} onSelectionChange={setSelected}>
<Table.Head>
<Table.Row>
<Table.SelectAllCell rowIds={users.map(u => u.id)} />
<Table.Header>Name</Table.Header>
</Table.Row>
</Table.Head>
<Table.Body>
{users.map(u => (
<Table.Row key={u.id} rowId={u.id}>
<Table.SelectCell label={`Select ${u.name}`} />
<Table.Cell>{u.name}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
);
}Features
- Automatic <caption> rendering from caption prop
- scope='col' and scope='row' applied automatically to header cells
- aria-sort on sortable columns; cycles none → ascending → descending → none
- Sort button uses native <button> — no div click handlers
- aria-selected on selectable rows
- Select-all checkbox with indeterminate state
- aria-busy applied when isLoading is true
- Focus retained on sort button after re-render
- Screen reader announcements for sort and selection changes
- Dev warnings for missing caption/label and missing SelectCell label
Props
| Prop | Type | Default | Description |
|---|---|---|---|
caption | string | - | Renders <caption>; required for accessible name unless aria-label or aria-labelledby is used |
captionHidden | boolean | false | Visually hides the caption while keeping it accessible |
sortKey | string | null | - | Controlled sort column key |
sortDirection | 'none' | 'ascending' | 'descending' | 'none' | Controlled sort direction |
onSortChange | (key: string, direction: SortDirection) => void | - | Called when sort column or direction changes |
selectedRows | string[] | - | Controlled selected row IDs |
defaultSelectedRows | string[] | - | Uncontrolled default selected rows |
onSelectionChange | (rows: string[]) => void | - | Called when row selection changes |
isLoading | boolean | false | Sets aria-busy on the table and shows loading state |
Sub-components
Table.Head
| Prop | Type | Default | Description |
|---|---|---|---|
children* | ReactNode | - | Header rows |
Table.Body
| Prop | Type | Default | Description |
|---|---|---|---|
children* | ReactNode | - | Body rows |
Table.Row
| Prop | Type | Default | Description |
|---|---|---|---|
rowId | string | - | Required for selection — sets aria-selected on the row |
children* | ReactNode | - | Cells |
Table.Header
| Prop | Type | Default | Description |
|---|---|---|---|
sortKey | string | - | Enables a sort button on this column |
scope | string | - | Override the auto-detected scope attribute |
children* | ReactNode | - | Header cell content |
Table.Cell
| Prop | Type | Default | Description |
|---|---|---|---|
children* | ReactNode | - | Cell content |
Table.SelectAllCell
| Prop | Type | Default | Description |
|---|---|---|---|
rowIds* | string[] | - | All selectable row IDs — drives the indeterminate state |
Table.SelectCell
| Prop | Type | Default | Description |
|---|---|---|---|
label* | string | - | Accessible label for the row checkbox e.g. 'Select Alice' |
Table.EmptyState
| Prop | Type | Default | Description |
|---|---|---|---|
colSpan* | number | - | Spans the full table width |
children* | ReactNode | - | Empty state message |
Table.LoadingState
| Prop | Type | Default | Description |
|---|---|---|---|
colSpan* | number | - | Spans the full table width |
children* | ReactNode | - | Loading message |
Keyboard Interactions
Accessibility
All keyboard interactions follow WAI-ARIA best practices and work without any additional configuration.
| Key | Action |
|---|---|
| Tab | Moves between sort buttons and row checkboxes |
| Enter / Space | Cycles sort direction on a sort button |
| Space | Toggles row selection on a row checkbox |