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.

Three selection models side by side Matrix comparing row selection, cell selection and checkbox selection by where state lives, supported container, what is announced, and typical use. Three selection models side by sideModelState lives onContainerAnnounced asRow selectionrole="row"grid or treegrid"selected" with the rowCell selectionrole="gridcell"grid"selected" with the cellCheckbox columninput checkboxtable or grid"checked" on the checkboxaria-selected on tr of atabletr in a tablenot supportednothing, usually
Pick one model per table. The checkbox model is the only one that works in a static table.

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”
Which selection model? Decision tree choosing among checkbox, row and cell selection based on whether the table is static and whether users act on records or values. Which selection model?What does a user act on after selecting?Records, static tableCheckbox columnnative, browse-mode friendlyRecords, interactive gridaria-selected on rowsSpace toggles, Shift extendsValues in cellsaria-selected on gridcellsranges, copy and paste
Start from what users act on, then check whether the table is a grid at all.

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.

Selection following focus, and not Comparison of selection that follows focus against selection independent of focus, showing where each is appropriate. Selection following focus, and not✓ Follows focus (single select)Arrow keys move and select togetherLike a listbox of one choicearia-multiselectable absent or falseFine for pick-one record lists✓ Independent (multi select)Arrows move focus onlySpace toggles; Shift+Arrow extendsaria-multiselectable="true"Never clears a selection by moving
Follow-focus is a single-select convenience; in a multi-select grid it silently discards the user's work.

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.

Permalink to "Related"

← Back to Cell Selection & Clipboard in Data Grids