Data Display
Accessible Table
A sortable, read-only data table with proper headers, scope, and caption. It presents static content the user reads, not a widget they operate cell-by-cell.
Last verified against WCAG 2.2 and WAI-ARIA APG 1.2 on 2026-07-02.
Implementation
| Role | Last active | ||
|---|---|---|---|
| Amara Okafor | Engineer | Active | 2026-07-01 |
| Diego Fuentes | Product Manager | Invited | — |
| Priya Natarajan | Designer | Active | 2026-06-29 |
| Sofia Marchetti | Support | Active | 2026-07-02 |
| Wren Callahan | Engineer | Suspended | 2026-05-14 |
<!-- aria-sort lives on the <th>, not the button, that's where AT looks
for a column's sort state. The <button> inside is what makes the
control keyboard-operable. Only ONE column is sorted at a time. -->
<table>
<thead>
<tr>
<th scope="col" aria-sort="ascending"><button data-key="name">Name</button></th>
<th scope="col" aria-sort="none"><button data-key="email">Email</button></th>
<th scope="col" aria-sort="none"><button data-key="signup">Signed up</button></th>
</tr>
</thead>
<tbody>
<!-- data rows -->
</tbody>
</table>For an interactive tabular widget, editable, selectable, or arrow-key-navigable cells, see the Grid pattern (role="grid") instead.
Required roles, states & properties
| Element | Attribute | Why |
|---|---|---|
| <table> | <caption> | Gives the table an accessible name/purpose announced before a screen reader user enters it (not itself an ARIA attribute, but the required native equivalent). |
| Column header <th> | scope="col" | Associates the header with every cell in its column, so jumping to any cell also announces the relevant column label. |
| Row header <th> | scope="row" | Associates the header with every cell in its row, giving row context (e.g. person's name) alongside any cell value. |
| Sortable column <th> | aria-sort="ascending" | "descending" | "none" | Communicates the column's current sort state. Lives on the header cell, updated dynamically as sort changes, never left stale. |
| Sort control | Real <button> inside the <th>, with a composed aria-label | Keeps the control keyboard-operable and gives it an unambiguous accessible name like "Sort by Name, currently sorted ascending" instead of just the icon or a bare column label. |
Keyboard interaction model
| Key | Behavior |
|---|---|
| Tab / Shift+Tab | Moves between interactive elements (sort buttons, any row action buttons) in reading order, normal document tab order, nothing custom. |
| Enter / Space | Activates the focused sort button, re-sorting the table and updating aria-sort. |
A basic sortable table does not need arrow-key grid/cell navigation, that belongs to a spreadsheet-style ARIA grid pattern and is out of scope here. Testers should not expect arrow keys to move between cells in this pattern.
Focus management rules
- Only interactive elements (sort buttons, row action buttons) are ever in the Tab order, static data cells are never focus stops.
- Activating a sort button never moves focus away from that button, even though the row order changes underneath it.
- Column header aria-sort and the button's accessible name are updated together, synchronously with the re-sort, so they never fall out of sync.
WCAG 2.2 success criteria mapping
| SC | Name | Level | Why it applies |
|---|---|---|---|
| 1.3.1 | Info and Relationships | A | Structure and relationships conveyed visually are also programmatically determinable. |
| 2.1.1 | Keyboard | A | All functionality is operable through a keyboard interface with no specific timing. |
| 4.1.2 | Name, Role, Value | A | For all UI components, name, role, and value are programmatically determinable; states and changes are announced. |
| 4.1.3 | Status Messages | AA | Status messages can be programmatically determined via role or properties (e.g. aria-live) without receiving focus. |