developer guide
Accessibility and keyboard
Built to WCAG 2.2 level AA: the full keyboard map, roving focus over a virtualised body, the ARIA the grid writes, and forced-colours support.
Developer guide › Accessibility and keyboard
Accessibility
The grid is built to WCAG 2.2 level AA. What follows is what it does, where the standard's own allowances apply, and the keyboard map in full: stated plainly, because a conformance claim that overstates is worth less than one that admits its edges.
Keyboard
Every operation is reachable without a pointer. Resizing and reordering a column were once drag-only; both now have key bindings and menu items, so nothing depends on dragging.
| Keys | Does |
|---|---|
| In the data | |
| ArrowUp/Down/Left/Right | Move the focused cell |
| Home or End | First / last cell of the row |
| Ctrl+Home or Ctrl+End | First cell of the first row / last cell of the last row |
| PageUp or PageDown | Move one viewport of rows |
| Tab or Shift+Tab | Next / previous cell, wrapping across rows |
| Enter | Start editing the focused cell |
| Space | Toggle selection of the focused row |
| Escape | Cancel the current edit or drag |
| Alt+ArrowRight or Alt+ArrowLeft | Expand / collapse a group or tree row |
| Ctrl+Alt+H | Move focus to the column header |
| Ctrl+Alt+P | Move focus to the tool panel |
| Shift+F10 or ContextMenu | Open the context menu for the focused cell |
| Alt+Shift+ArrowUp or Alt+Shift+ArrowDown | Move the focused row, when row reorder is enabled |
| On a column heading | |
| ArrowLeft or ArrowRight | Move between headings |
| Ctrl+ArrowLeft or Ctrl+ArrowRight | First / last heading |
| Enter or Space | Sort by the column, Shift to add to the sort |
| Alt+ArrowLeft or Alt+ArrowRight | Resize the column, Ctrl for a coarse step |
| Shift+ArrowLeft or Shift+ArrowRight | Move the column |
| Alt+ArrowDown | Open the column menu |
| ArrowDown or Escape | Return focus to the data |
| In the tool panel's column list | |
| G | Group by the column, or stop grouping by it |
| V | Add the column to values, or take it out |
| P | Pivot by the column, or stop pivoting by it |
| Shift+ArrowUp or Shift+ArrowDown | Move the column |
This table is generated from the bindings the build ships and checked on every build, so it cannot drift from what the grid actually does.
What a screen reader is told
The grid reports itself as a grid, or a treegrid when it holds a
hierarchy, and the role follows the configuration rather than being fixed when the grid is
created. Rows and cells carry their position in the dataset, not in the rendered
window: a reader on row 500,000 of a virtualised grid is told exactly that, which is the point
most grids get wrong. Rows in a hierarchy also carry their position among their siblings, since
a reader cannot count siblings that were never rendered.
Focus is real focus, moved onto the cell, rather than aria-activedescendant. It
is restored after a row is recycled or scrolled out and back, and the grid holds a single tab
stop, so tabbing in and out crosses it once.
State changes are announced: sorting, filtering, selection, grouping, expanding and collapsing, paging, undo and redo, pasting, and rows arriving or leaving on a live feed. A feed is summarised on an interval rather than narrated, because a reader queues what it is given and a fast feed would leave someone listening to counts that are no longer true. A rejected edit is announced with the value that was put back, which is the change users most need to hear about and the one a visual marker alone cannot convey.
Colour and contrast
No information is carried by hue alone. The high-contrast theme runs text at 21:1
and borders at 6.1:1. In Windows High Contrast Mode the grid translates its state into borders
and system colours rather than fighting the palette: see
theming for what that means in detail.
How this is checked
The grid carries its own accessibility rules, and they run on every build against each configuration that differs structurally: flat, grouped, tree, pinned, editing, paginated and with a tool panel: rather than against one sample grid. The same rules are available live from the devtools panel, where they can also read colour and measure targets.
Be clear about what that proves. These are our own rules covering what a data grid gets wrong, not a general-purpose engine, and automated checking of any kind catches a minority of real problems. They are a regression net (they stop a fix being undone silently) and not evidence of conformance.
Bigger targets for touch
The grid meets the minimum target size on its own. That minimum is a conformance floor, not a comfortable size for a finger: both mobile platforms recommend nearer 44 pixels.
targetSize: 'large'
This raises the hit areas and leaves the type where it is, which is the distinction that matters: a touch user wants a larger target, and a low-vision user wants larger text. Density is the control for the second, and the two combine, a compact grid with large targets is a reasonable thing to want on a tablet.
It applies by itself under a coarse pointer, since the person holding one is both who the
criterion is for and the least likely to go looking for a setting. Pass
targetSize: 'default' to opt out of that.
Density alone does not do this. It scales the header, the rows and the type, and leaves the affordances inside them exactly as they were: measured at every preset, the menu button stays 24 pixels, the filter 16 and the resize grip 10. A spacious grid has the room going spare and controls no larger than a compact one, which is the gap this fills.
Edge cases at narrow widths
Stated because a report that claims everything invites the one question it cannot answer.
- Two-dimensional scrolling. A grid scrolls horizontally at narrow widths. WCAG 1.4.10 Reflow explicitly permits this for data tables, so it is conforming rather than a gap, but it is worth knowing before you meet it.
- Pinning is honoured at every width. At around 320 pixels, two pinned columns of ordinary width can leave under 60 pixels for the scrolling middle. The grid stays operable and nothing is lost, but a layout that pins columns is worth reviewing if you expect it to be used at that size.
- The filter icon is a small target, deliberately. It is 16 × 16, below the 24-pixel minimum of WCAG 2.5.8, and conforms under that criterion's equivalent allowance: filtering is also a column-menu item, and the menu button meets the size on its own. Worth knowing if you are pointing at it on a touch screen, the menu is the larger route to the same thing.
Keyboard
Press ? in the grid to see this list in the product. The overlay is generated from
the same bindings the grid implements, so it cannot drift from them, and it shows
Cmd rather than Ctrl on a Mac, the grid reads either, so that is what
you will actually press. Escape closes it and focus returns where it was. Set
shortcuts: false if you want ? for something else.
| Keys | Does |
|---|---|
| Arrows | Move the focused cell. |
| Shift + arrows | Extend the range. |
| Ctrl/Cmd + Shift + arrows | Open a second range at the focused cell, keeping the first; further presses extend it. |
| Ctrl/Cmd + C, V, D | Copy, paste, fill down. |
| Delete / Backspace | Clear the selected cells. |
| Enter | Start editing; commit and step down. |
| Tab | Commit and step across. |
| Escape | Cancel the edit; restore a maximised grid once nothing else wants it. |
| Space | Toggle the row's selection. |
| Home / End, Page Up / Down | Jump; with Ctrl, to the ends of the grid. |
None of these fire while you are typing into an input, a filter box, an open editor, the view-name field. The grid checks where the keystroke came from before claiming it, which sounds obvious and is the sort of thing that is usually wrong.