aria-selected on Cells Versus Rows: Choosing a Selection Model
Permalink to "aria-selected on Cells Versus Rows: Choosing a Selection Model"aria-selected indicates the selection state of an item inside a selectable composite widget. In a grid, that item can be a whole row or an individual gridcell, and the choice decides what users hear, what keys they press, and what “select all” means. The failure this page prevents is the hybrid: a grid that marks rows as selected, cells as selected, and also renders checkboxes, so that the same record is announced as “selected” in three different ways — or, worse, in two ways that disagree.
It is part of cell selection & clipboard in data grids.
Spec reference
Permalink to "Spec reference"ARIA 1.2 supports aria-selected on row when the row is inside a grid or treegrid, and on gridcell, columnheader and rowheader. It is not supported on rows or cells of role="table" — a static <table> — so selection there must be expressed with checkboxes or another control.
aria-multiselectable="true" on the grid says more than one item can be selected. Without it, aria-selected implies single selection, and some readers announce “selected” only for the chosen item and nothing for others. The value of aria-selected should be true or false on every selectable item; omitting it means “not selectable”, which is different from “not selected”.
WCAG: SC 4.1.2 Name, Role, Value — the state must be programmatic; SC 1.4.1 Use of Color — the visual selection must not be colour alone; SC 1.3.1 for the relationship between the selection and its effect.
When to use each model
Permalink to "When to use each model"Row selection fits record lists where the unit of action is a record: an email list, a file manager, an order queue. Rows are focusable (or the grid uses row focus), Space toggles the focused row, Shift+Arrow extends. The whole row is read with “selected”.
Cell selection fits spreadsheet-style grids where the unit of action is a value: budget sheets, pivot views, schedule editors. Cells are focusable, Shift+Arrow builds rectangles, and copy/paste operates on the rectangle.
Checkboxes fit static tables that need selection but not a grid’s keyboard model. They are the most robust option — native, announced everywhere, operable in browse mode — and the right default when in doubt.
The misapplication to name is adding aria-selected="true" to <tr> elements in a normal <table> to “make selection accessible”. Browsers do not expose it for table rows, so nothing is announced, and axe-core flags it as an unsupported attribute.
Annotated code example
Permalink to "Annotated code example"<!-- ROW SELECTION: records are the unit -->
<div role="grid" aria-label="Inbox" aria-multiselectable="true">
<div role="row" aria-selected="true" tabindex="0"> <!-- SC 4.1.2 -->
<div role="gridcell">Northwind</div>
<div role="gridcell">Invoice overdue</div>
</div>
<div role="row" aria-selected="false" tabindex="-1"> <!-- explicit false -->
<div role="gridcell">Contoso</div>
<div role="gridcell">Weekly report</div>
</div>
</div>
<!-- CELL SELECTION: values are the unit -->
<div role="grid" aria-label="Budget 2026" aria-multiselectable="true">
<div role="row">
<div role="rowheader">Travel</div>
<div role="gridcell" aria-selected="true" tabindex="0">4,000</div>
<div role="gridcell" aria-selected="false" tabindex="-1">4,200</div>
</div>
</div>
<!-- STATIC TABLE: checkboxes, no aria-selected anywhere -->
<table>
<caption>Invoices</caption>
<tr>
<td><input type="checkbox" aria-label="Select INV-1042" checked></td>
<th scope="row">INV-1042</th>
<td>1,280.00</td>
</tr>
</table>
/* SC 1.4.1: selection is not colour alone — add a shape cue */
[role="row"][aria-selected="true"],
[role="gridcell"][aria-selected="true"] {
background: var(--color-selected-bg);
box-shadow: inset 4px 0 0 var(--color-selected-edge); /* left bar */
}
Styling from the ARIA attribute, as above, keeps the visual and programmatic states from drifting apart: if the attribute is wrong, the highlight is wrong too, and someone notices.
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Model / event | NVDA + Chrome | JAWS + Chrome | VoiceOver + Safari |
|---|---|---|---|
| Row: focus a selected row | Row content, “selected” | Row content, “selected” | Row content, “selected” |
| Row: focus an unselected row | Row content only | “not selected” (verbosity) | Row content only |
| Cell: focus a selected cell | “4,000, selected” | “4,000, selected” | “4,000, selected” |
| Cell: range extended | Focused cell only | Focused cell only | Focused cell only |
Checkbox: Space |
“checked” | “checked” | “checked” |
Integration context
Permalink to "Integration context"A grid of selectable rows with only one or two columns is often better as a listbox, where each option is a record and selection is native to the role — the trade-offs are in listbox versus grid for selectable record lists.
Checkbox selection in a React table is worked through in accessible row selection in TanStack Table; cell-range messaging is in announcing rectangular cell range selections.
Gotchas
Permalink to "Gotchas"Selection following focus. In some grids, moving focus also selects (like a single-select listbox). That is acceptable for single selection but disastrous for multi-select, where arrowing through a list would clear the user’s selection at every step. Keep focus and selection separate when aria-multiselectable is true.
Omitted versus false. Removing aria-selected from unselected rows tells assistive technology those rows cannot be selected. Set false explicitly.
Checkbox plus aria-selected. If you render checkboxes in a row-selectable grid, the checkbox state and the row state must be the same value at all times, or readers announce contradictory states. Prefer one or the other.
Design system notes
Permalink to "Design system notes"A grid component should expose the selection model as a single prop — selection: 'none' | 'row' | 'cell' | 'checkbox' — and refuse combinations. That one enum prevents the hybrid states this page warns about, because no product can turn on row aria-selected and a checkbox column at the same time. Tie the selected styles to the ARIA attribute in the component’s stylesheet so the visual state is always derived from the programmatic one.
Selected colours need design tokens for both themes, and the token set should include an edge or outline colour, not just a background, so the non-colour cue required by SC 1.4.1 is part of the system rather than an afterthought.
Testing checklist
Permalink to "Testing checklist"FAQ
Permalink to "FAQ"How is a single-select grid different from a multi-select one?
In a single-select grid, aria-multiselectable is false or absent and selection may follow focus, like a listbox. In a multi-select grid, aria-multiselectable is true and focus and selection are independent, so moving never clears what the user has chosen.
Can I use aria-selected on a tr in a regular HTML table?
No. aria-selected is supported on rows and cells of grid and treegrid, not of a static table. Browsers do not expose it there, so screen readers announce nothing. Use a checkbox in each row instead.
Should selection follow focus in a data grid?
Only for single selection. When aria-multiselectable is true, keep focus and selection independent: arrows move focus, Space toggles selection, and Shift with arrows extends it.
Why set aria-selected="false" instead of removing the attribute?
Because a missing aria-selected means the item is not selectable at all, while false means selectable but not selected. Setting false explicitly keeps the grid’s selection model clear to assistive technology.
Related
Permalink to "Related"- Announcing cell ranges — messages for cell selection
- Row selection in TanStack Table — the checkbox model in React
- Listbox versus grid — when rows should not be a grid at all