Accessibility
A grid that's WCAG 2.1 AA out of the box: full ARIA, a live region that speaks sort/filter/load/edit, and complete keyboard reach including header sorting — all on by default, nothing to configure, and gated by axe-core in CI.
Accessible by default — nothing to switch on
The grid ships as a correct WCAG 2.1 AA component out of the box: full ARIA structure, a live
region that speaks state changes, and complete keyboard reach. There is no
accessibility: true flag and no per-column wiring — turn on sorting, selection,
filtering, paging and editing (all shown above, all free) and the accessible behaviour comes
with them.
Screen readers hear what happens
A single polite live region per grid announces the events that matter, in your app's language
(every string is a GridMessages key, English and Arabic built in). Turn on a
screen reader and try the grid above:
| When you… | …the grid announces |
|---|---|
| Sort a column | "Name: sorted ascending" / "descending" / "Sort cleared" |
| Type in a filter | the result count — "8 results" |
| Load or page the data | "120 rows loaded" · "Page 2 of 6" |
| Commit an edit | "Salary updated" |
| Select all / clear | "20 rows selected" · "Selection cleared" |
Announcements fire on state events only — scrolling thousands of virtual rows never makes a sound, and a burst of rapid changes collapses into one message. The real row count comes from your data source, so it's truthful even when only a page is loaded.
Naming the grid
A screen reader announces the grid by name the moment focus enters it. By default that name is
Data Grid, which is enough on a page with one grid and useless on a page with two —
a listener hears the same three words twice and has no way to tell which one they landed in.
Give it a real name with config.messages.gridLabel. The grid on this page is called
Employee directory; that string becomes the grid's accessible name, and it is
the first thing a screen-reader user hears here.
config: GridConfig = {
messages: { gridLabel: 'Employee directory' }
};
messages holds every other string the grid speaks or shows too — the empty and error
text, the pager labels, the names on the selection checkboxes. Setting one leaves the rest at
their defaults.
Everything works from the keyboard
Focus enters on the first cell; arrows move it and the grid scrolls to follow. The header row is part of the path — arrow up from the top row onto a column header and press Enter or Space to sort it. Arrow onto the filter row and Enter steps into the input, Escape steps back out.
| Keys | What they do |
|---|---|
| ↑ ↓ ← → | Move one cell — across the body, the filter row, and the header row. |
| Tab / Shift+Tab | Next / previous cell. At the last cell focus leaves the grid — never a trap. |
| Home / End · Ctrl+Home / Ctrl+End | Row edges · grid corners. |
| Enter / Space on a header | Sort the column. |
| Enter on a cell | Edit it — or, when the cell holds a link or button, step into that content (Escape returns). |
| Space on a row | Select / deselect it. |
Proven, not promised
The grid's accessibility is enforced by an automated axe-core gate in CI:
the demo grids are scanned against WCAG 2.1 AA on every change and must stay at
zero violations. Correct row/column counts and indices, aria-sort,
group and tree semantics, labelled controls, and a clean role subtree are all verified — not
left to a manual audit.
Many grids make accessibility your job — you hand-tune DOM order (which then breaks arrow navigation), force off column virtualisation to satisfy a reader, and write per-column keyboard handlers to reach a link inside a cell. Here there is nothing to configure: the structure, the announcements and the keyboard model are the default, and the row count is always the real one.
Accessibility API
| Property | Type | Default | Description |
|---|---|---|---|
| config.messages.gridLabel | string | 'Data Grid' | The grid's accessible name — the first thing a screen reader announces on entry. Name it whenever a page holds more than one grid. |
| config.messages | Partial<GridMessages> | en / ar built in | All ARIA labels and screen-reader announcements. Override any key to translate or reword; set a key to '' to silence that announcement. |
| aria-live announcements | automatic | polite | Sort, filter/result count, load, page, edit commit and select-all speak through one polite region. Live-update streams stay silent by design. |
| role / aria-rowcount / aria-colcount | automatic | — | grid (or treegrid with grouping/tree) on a clean role subtree; true counts and indices in display space, correct across pinned regions and virtual scroll. |
| config.keyboardNav | boolean | true | The keyboard model incl. header-row sorting and the interactive-cell pattern. Set false to opt out. |
| --gc-cell-focus-outline / -width / -offset | CSS vars | theme accent, 2px, -2px | The keyboard focus ring (shared by body, header and filter cells). :focus-visible only — a mouse click never paints it. |