Data Grid

Description

A data grid is a composite widget that presents tabular data in a two-dimensional structure of rows and cells. Users may use a keyboard to navigate the grid with Arrow, Home, and End keys, then interact with buttons, checkboxes, links, text fields or other interactive elements contained within grid cells.

Though often structured as a table, the grid role does not imply a specific visual presentation—it describes relationships among elements.

Though a data grid may contain multiple interactive elements, in occupies a single tab stop in the page’s focus sequence. Internal focus should be managed during keyboard navigation as described in the ARIA guidelines Managing Focus.

When to use

Use a data grid when:

  • Users need to navigate through tabular data cell-by-cell using arrow keys. Examples include spreadsheet-like interfaces, scheduling grids, or data management tables.
  • Individual cells contain interactive controls such as buttons, checkboxes, links, or editable inputs. With a single entry for the grid in the page tabbing sequence, keyboard users can more easily navigate the page without needing to tab through every interactive element in the data grid.
  • Selection of rows or cells or column sort controls are necessary. Such elements will need to be operated using a keyboard.

Do not use a data grid when:

  • The data is read-only and users only need to scan or look up values. Refer to the Table entry for accessibility guidelines for tables. -
  • The "grid" is purely for layout purposes. CSS grid and flexbox are the appropriate tools; Layout grids should never carry role="grid".
  • The data does not contain a logical row and column association. Do not use a data grid for a linear array of data simply because it wraps into multiple rows when it is displayed.

Examples

Constructing an accessible data grid widget requires more than adding interactive elements to a table structure. At a minimum, these two things are necessary:

Creating a data grid widget using the HTML table element

By using native table markup and adding ARIA grid roles and JavaScript required for grid semantics and interaction, the widget takes advantage of the browser’s built-in cell-spanning, header-association, and accessible-name calculation.

In this example, an accessible data grid is set up for a Tic Tac Toe game (also known as “Noughts and Crosses”) using standard HTML table elements. The example illustrates HTML structure for a data grid but does not include the JavaScript needed to handle keyboard interaction for a data grid or everything that would be needed for a workable game.

The example uses the roving tabindex technique for managing focus within the grid. In the initial state, tabindex="0" is set on the first interactive element so that the grid will be in the tabbing sequence. All other interactive elements in the grid have tabindex="-1" set.

Setting role="grid" on the <table> element sets the implicit role of its children, so often it is only necessary to set role="grid" on the table element itself.

ElementDefault RoleRole with table role="grid"
<table>tablegrid
<caption>captioncaption
<thead>rowgrouprowgroup
<tbody>rowgrouprowgroup
<tfoot>rowgrouprowgroup
<tr>rowrow
<th scope="col">columnheadercolumnheader
<th scope="row">rowheaderrowheader
<th>cellgridcell
<td>cellgridcell
Example Grid HTML structure using table elements
1
2<table role='grid'>
3 <caption>Play Tic-Tac-Toe</caption>
4 <thead>
5 <tr>
6 <th></th>
7 <th scope='col'>Column 1</th>
8 <th scope='col'>Column 2</th>
9 <th scope='col'>Column 3</th>
10 </tr>
11 </thead>
12 <tbody>
13 <tr>
14 <th scope='row'>Row 1</th>
15 <td>
16 <input name='11' tabindex="0">
17 </td>
18 <td>
19 <input name='12' tabindex="-1">
20 </td>
21 <td>
22 <input name='13' tabindex="-1">
23 </td>
24 </tr>
25 <tr>
26 <th scope='row'>Row 2</th>
27 <td>
28 <input name='21' tabindex="-1">
29 </td>
30 <td>
31 <input name='22' tabindex="-1">
32 </td>
33 <td>
34 <input name='23' tabindex="-1">
35 </td>
36 </tr>
37
38 <tr>
39 <th scope='row'>Row 3</th>
40 <td>
41 <input name='31' tabindex="-1">
42 </td>
43 <td>
44 <input name='32' tabindex="-1">
45 </td>
46 <td>
47 <input name='33' tabindex="-1">
48 </td>
49 </tr>
50 </tbody>
51</table>
52

Creating a data grid widget with ARIA

The ARIA grid role does not imply a physical presentation. When inherently two-dimensional data needs to be arranged in something other than a tabular display, an ARIA-based structure may be appropriate.

Example Grid HTML structure using ARIA

The following example illustrates the use of ARIA with the generic elements used rather than table elements to construct the Tic Tac Toe game.

1<h2 id="gameLabel">Play Tic-Tac-Toe</h2>
2
3<div role='grid' aria-labelledby="gameLabel">
4 <div role="rowgroup">
5 <div role="row">
6 <div></div>
7 <div role="columnheader">Column 1</div>
8 <div role="columnheader">Column 2</div>
9 <div role="columnheader">Column 3</div>
10 </div>
11 </div>
12 <tbody>
13 <div role="row">
14 <div role="rowheader">Row 1</div>
15 <div role="gridcell">
16 <input name='11' tabindex="0">
17 </div>
18 <div role="gridcell">
19 <input name='12' tabindex="-1">
20 </div>
21 <div role="gridcell">
22 <input name='13' tabindex="-1">
23 </div>
24 </div>
25 <div role="row">
26 <div role="rowheader">Row 2</div>
27 <div role="gridcell">
28 <input name='21' tabindex="-1">
29 </div>
30 <div role="gridcell">
31 <input name='22' tabindex="-1">
32 </div>
33 <div role="gridcell">
34 <input name='23' tabindex="-1">
35 </div>
36 </div>
37
38 <div role="row">
39 <div role="rowheader">Row 3</div>
40 <div role="gridcell">
41 <input name='31' tabindex="-1">
42 </div>
43 <div role="gridcell">
44 <input name='32' tabindex="-1">
45 </div>
46 <div role="gridcell">
47 <input name='33' tabindex="-1">
48 </div>
49 </div>
50 </tbody>
51</div>
52

Relevant ARIA attributes

  • role="grid" on the container element identifies it as an interactive grid widget. The grid must have an accessible name, typically via aria-label or aria-labelledby.

  • role="row" on each row element. Rows are owned by the grid container, or by an optional role="rowgroup" element (equivalent to <thead>, <tbody>, or <tfoot>).

  • role="gridcell" on each data cell. For header cells, use role="columnheader" or role="rowheader" instead.

  • aria-rowcount / aria-colcount on the grid element when the DOM contains only a subset of rows or columns (virtual scrolling). These communicate the true total size of the grid to assistive technologies.

  • aria-rowindex / aria-colindex on each row or cell to communicate its position within the full dataset when virtual scrolling is in use.

  • aria-multiselectable on the grid element to indicate that multiple rows or cells can be selected simultaneously.

  • aria-selected on a row or cell to indicate its selection state. When aria-multiselectable is set, all selectable rows or cells must have an explicit aria-selected value.

  • aria-readonly on the grid, a row, or an individual cell to indicate that the content cannot be edited. See the Avoid aria-readonly attribute test entry for important caveats.

  • aria-sort on column or row header cells to communicate the current sort direction (ascending, descending, other, or none). See Grid cell aria-sort accessibility tests.

  • aria-expanded on a row that can be expanded to reveal detail rows.

  • Focus management: Use a roving tabindex (set tabindex="0" on the currently active cell, tabindex="-1" on all others, and update on arrow-key navigation) or aria-activedescendant on the grid container (keeping focus on the grid while updating the attribute to point to the focused cell). Either approach ensures the grid has exactly one tab stop in the page's tab sequence.

Table component accessibility tests

In addition to the data grid-specific tests documented below, Evinced also runs the full Table component accessibility analysis on data grid components. Refer to the Table knowledge base entry for details on all table-level tests — including accessible name, header structure, cell relationships, and column/row span correctness — that also apply to your data grid.

Structure and semantics

The grid container and its cells must carry the correct ARIA roles and attributes so that assistive technologies can identify the widget, communicate its structure, and expose the state of individual cells.

Role

Element has incorrect role
StandardCriteria
WCAG4.1.2 Name, Role, Value
EN 301 5499.4.1.2 Name, Role, Value
Section 5081194.21(d)

Impact: Critical

The root element of a data grid widget must have role="grid". When an incorrect role is applied — for example, role="table", role="list", or no role at all — assistive technologies identify the widget as a different type of component. Screen reader users are presented with the wrong interaction model: they will not be told to use arrow keys to navigate, and key commands for grid-specific behavior (such as navigating to the first or last cell in a row) will not be announced.

Ensure:

  • The outermost container element has role="grid".
  • Each row element has role="row".
  • Each data cell has role="gridcell", role="columnheader", or role="rowheader" as appropriate.
Incorrect root element

The Evinced Unit Tester may report this issue when it detects role="grid" on a descendant element rather than on the element passed to the test locator. Verify that the locator used in the test targets the outermost container of the grid widget, and that role="grid" is applied directly to that element rather than to a child or wrapper inside it.

Grid Has Cells

No group members with required role
StandardCriteria
WCAG1.3.1 Info and Relationships
EN 301 5499.1.3.1 Info and Relationships
Section 508N/A

Impact: Serious

A grid container must own child elements with the required cell roles. Specifically, the grid must contain role="row" elements, and each row must contain at least one element with role="gridcell", role="columnheader", or role="rowheader". If the grid container has no descendants carrying these required roles, assistive technologies cannot convey the grid's structure to users: screen readers will not announce rows, column counts, or individual cell content in a meaningful way.

Ensure that:

  • Every row in the grid is marked with role="row".
  • Every data cell is marked with role="gridcell".
  • Header cells use role="columnheader" or role="rowheader" as appropriate.
Accessible name

Like table components, data grid elements must have accessible names — refer to the Table accessible name tests for full guidance on naming the grid container and its structural elements.

Aria Readonly

Avoid aria-readonly attribute
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

The aria-readonly attribute is being used on one or more cells or rows in the data grid. While the ARIA specification explicitly supports this — aria-readonly set on the grid element propagates automatically to all owned gridcell elements, and individual cells may override the propagated value — support for announcing the read-only state is inconsistent across browsers and assistive technologies. Some screen readers do not announce aria-readonly at all, leaving users unaware that certain cells cannot be edited.

A few important points from the spec to keep in mind:

  • aria-readonly communicates only whether cell content is editable. It has no effect on a user's ability to navigate or otherwise manipulate the grid itself — cells with aria-readonly="true" must remain focusable and reachable via arrow key navigation.
  • If a grid presents content that is inherently non-editable (for example, a date picker showing a grid of link elements), you do not need to set aria-readonly at all. The absence of aria-readonly does not imply that content is editable.

Where possible, prefer the native HTML readonly attribute on form controls within cells, as it has broader and more consistent screen reader support. If you use aria-readonly, test with multiple screen reader and browser combinations to verify that the read-only state is announced correctly.

Disabled

Element type does not support disabled state
StandardCriteria
WCAG4.1.2 Name, Role, Value
EN 301 5499.4.1.2 Name, Role, Value
Section 5081194.21(d)

Impact: Serious

An element with a role of grid, row, or gridcell has the native HTML disabled attribute applied to it. While the ARIA specification does allow these roles to carry a disabled state, the correct way to communicate it is through aria-disabled="true" — not the native disabled attribute. The native disabled attribute is a feature of HTML form controls; applying it to generic or ARIA-role elements has no semantic effect and will not be announced by screen readers.

Replace the disabled attribute with aria-disabled="true" on the affected element. Note that unlike native disabled, aria-disabled does not suppress keyboard interaction automatically — you must also add event handlers that prevent activation when the element is in a disabled state.

Conflicting disabled values
StandardCriteria
WCAG4.1.2 Name, Role, Value
EN 301 5499.4.1.2 Name, Role, Value
Section 5081194.21(d)

Impact: Serious

An element within the grid has both disabled and aria-disabled attributes set to conflicting values — for example, disabled present (which implies disabled) alongside aria-disabled="false" (which explicitly signals enabled). This inconsistency causes assistive technologies to report the element's state incorrectly, leading to a discrepancy between what sighted users see and what screen reader users hear.

Resolve the conflict by using a single attribute:

  • For native HTML form controls inside grid cells, use only the disabled attribute.
  • For custom elements, use only aria-disabled.

Aria-sort

aria-sort invalid value
StandardCriteria
WCAG4.1.2 Name, Role, Value
EN 301 5499.4.1.2 Name, Role, Value
Section 5081194.21(d)

Impact: Minor

A table or grid header cell has an aria-sort attribute with an invalid value. The only valid values are ascending, descending, other, and none. An invalid value (such as a misspelling or a custom string) is ignored by assistive technologies, which then cannot communicate the column's sort state to screen reader users.

Correct the aria-sort value to one of the four valid options. Update the value dynamically as users interact with the sort control — toggle between ascending and descending, and reset to none (or omit the attribute) when the column is unsorted.

aria-sort on a table cell
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

The aria-sort attribute is present on a standard data cell (<td> or role="gridcell") rather than on a header cell (<th>, role="columnheader", or role="rowheader"). Assistive technologies expect sort indicators on header cells; placing aria-sort on a data cell may not be announced correctly and is inconsistent with the ARIA specification.

Move the aria-sort attribute to the appropriate column or row header cell that controls the sort order. Ensure the header cell is also keyboard operable if it triggers a sort action.

Table sort not accessible to keyboard
StandardCriteria
WCAG2.1.1 Keyboard
EN 301 5499.2.1.1 Keyboard
Section 508N/A

Impact: Needs Review

A header cell with aria-sort is present, indicating that the column supports sorting, but the cell cannot be reached or activated by keyboard. Keyboard-only users are unable to change the sort order, which means a core function of the data grid is inaccessible to them.

Take the following actions:

  • Make sortable header cells focusable by adding tabindex="-1" (or tabindex="0" if they are the initially active cell) so that keyboard users can reach them via arrow navigation within the grid.
  • Add a keydown event listener to handle Enter or Space to trigger the sort, consistent with button behavior.

Relationships

Aria-owns

Invalid aria-owns values
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Needs Review

Elements referenced by the aria-owns attribute on the grid container have roles that do not conform to the expected child roles for a grid. A grid should only own elements with role="row" or role="rowgroup". Referencing elements with incompatible roles causes assistive technologies to misrepresent the grid's structure, potentially confusing users about which cells belong to the grid.

Avoid using aria-owns if possible. If you must use it:

  • Preserve DOM order: DOM children of the grid should come first, followed by the elements referenced in aria-owns, unless you explicitly list the DOM children in aria-owns in the desired reading order.
  • Match expected roles: All elements specified in aria-owns must have roles that are valid owned children of gridrole="row" or role="rowgroup".
  • Avoid circular references: An element's id must not appear in more than one aria-owns attribute at any time.
Avoid aria-owns
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Needs Review

The aria-owns attribute is being used on the grid container or on one of its gridcell elements to override the default DOM parent–child relationship. Support for aria-owns is inconsistent across assistive technologies, and its use can produce an unpredictable or incorrect reading order for screen reader users.

Avoid aria-owns at every level of the grid. Instead:

  • Ensure row and cell elements are direct DOM children of the grid (or nested inside a rowgroup child of the grid).
  • Place any content that belongs to a cell as a direct DOM child of that gridcell element.

If content appears out of order in the DOM for layout reasons, restructure the markup or CSS to achieve the required visual presentation without modifying the accessibility tree via aria-owns.

Operation

Pointer operation

A user with a mouse, touch screen, or other pointing device should be able to:

Click a cell

Move focus to the clicked cell, making it the active cell. Subsequent arrow-key navigation starts from that cell.

Click an interactive control in a cell

Activate the control (checkbox, button, link, etc.) directly without a separate step to enter the cell.

Click a sortable column header

Change the column's sort order. The header's aria-sort value should update to reflect the new sort direction.

Keyboard operation

Data grids implement a two-mode keyboard interaction model. In navigation mode , arrow keys move focus between cells. In interaction (or edit) mode, focus is inside a cell's content so the user can interact with the element within it.

Entering the grid
Tab

Moves focus into the grid, landing on the previously focused cell or, if none, the first cell. Subsequent Tab presses move focus out of the grid to the next element in the page tab sequence.

Shift+Tab

Moves focus into the grid from the other direction (if the grid was the last-visited focusable element), or moves focus to the previous page element when pressed from within the grid.

Navigating between cells (navigation mode)
ArrowRight

Moves focus one cell to the right. Focus does not wrap at the end of a row.

ArrowLeft

Moves focus one cell to the left. Focus does not wrap at the start of a row.

ArrowDown

Moves focus one cell down. Focus does not wrap from the last row to the first.

ArrowUp

Moves focus one cell up. Focus does not wrap from the first row to the last.

Home

Moves focus to the first cell in the current row.

End

Moves focus to the last cell in the current row.

Ctrl+Home

Moves focus to the first cell in the grid (first row, first column).

Ctrl+End

Moves focus to the last cell in the grid (last row, last column).

Page Down

Optionally moves focus down by the number of rows visible in the viewport.

Page Up

Optionally moves focus up by the number of rows visible in the viewport.

Entering and exiting interaction mode
Enter or F2

When focus is on a cell, switches to interaction mode: moves focus to the editable element or first interactive element within the cell.

Escape

When in interaction mode, exits the cell content and returns focus to the cell itself (navigation mode). Focus must not leave the grid entirely.

Operating controls within a cell (interaction mode)
Space or Enter

Activates the focused interactive element (for example, checks a checkbox, follows a link, or triggers a button) within the cell.

Arrow keys

Navigate within a composite element inside the cell (for example, within a select or radio group). Arrow keys operate the inner widget rather than moving between grid cells while interaction mode is active.

Tab / Shift+Tab

Move focus between multiple interactive elements within the same cell while in interaction mode, if the cell contains more than one control.

Focus sequence

Data grid not focusable
StandardCriteria
WCAG2.1.1 Keyboard
EN 301 5499.2.1.1 Keyboard
Section 508N/A

Impact: Critical

Neither the grid container nor any of its descendant cells are included in the page's tab sequence. Keyboard users have no way to move focus into the grid at all: they cannot navigate its cells, read cell content, or operate any interactive controls within it.

Take one of the following actions:

  • Set tabindex="0" on the first cell (or the most recently active cell) so that the grid is reachable via Tab.
  • Set tabindex="0" on the grid container element if you are using the aria-activedescendant focus management pattern.

Only one element within the grid should have tabindex="0" at any time; all other cells should have tabindex="-1".

Composite unpredictable focus
StandardCriteria
WCAG2.1.1 Keyboard
EN 301 5499.2.1.1 Keyboard
Section 508N/A

Impact: Serious

When focus enters the grid, it does not land on the expected cell. The ARIA authoring practices require that focus lands on the previously active cell, or on the first cell if no cell has been active before. When focus arrives unpredictably — for example, on a non-cell element, or on a cell that is not the logical starting point — keyboard and screen reader users lose their orientation within the grid.

Ensure that:

  • When the grid gains focus for the first time, focus lands on the first cell.
  • On subsequent visits, focus returns to the previously active cell.
  • No cell other than the intended one has tabindex="0" at the time focus enters the grid.
Redundant focusable children
StandardCriteria
WCAG1.3.1 Info and Relationships
EN 301 5499.1.3.1 Info and Relationships
Section 508N/A

Impact: Moderate

Multiple child elements of the grid are included in the page's tab sequence simultaneously (i.e., more than one element has tabindex="0"). This disrupts the expected single-tab-stop pattern for composite widgets. Users navigating with Tab must stop at each extra focusable element within the grid rather than passing through it in one step.

Audit all elements in the grid and ensure that at any given moment only one element has tabindex="0". All other cells and interactive elements within cells should have tabindex="-1".

Skipped shift-tab evaluation
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Needs Review

The tester was unable to evaluate Shift+Tab behavior for this data grid. This typically occurs when earlier focus sequence tests have failed, leaving the grid in a state where reverse-tab navigation cannot be reliably tested.

Manually verify that:

  • Pressing Tab from within the grid moves focus to the next element in the page tab sequence, not to another cell within the grid.
  • Pressing Shift+Tab moves focus to the previous element in the page tab sequence.
  • Focus is never lost or sent to <body> after exiting the grid in either direction.

Keyboard navigation

Skipped grid keyboard navigation tests
StandardCriteria
WCAG2.1.1 Keyboard
EN 301 5499.2.1.1 Keyboard
Section 508N/A

Impact: Serious

The grid's keyboard navigation does not conform to the ARIA spec, causing the tester to lose context and skip further evaluation. This issue is triggered when one or more of the following fundamental navigation behaviors are broken:

  • Arrow keys (ArrowLeft, ArrowRight, ArrowUp, ArrowDown) do not move focus between cells as expected.
  • Edge behavior — focus loops from the last cell in a row or column back to the first, rather than stopping at the boundary.
  • Edge keys (Home, End, Ctrl+Home, Ctrl+End) do not move focus to the first or last cell in the current row or in the entire grid.

Keyboard-only users will have significant difficulty navigating the data grid. Because this test is a prerequisite for cell-level evaluation, the tester will also skip all cell focus and interaction tests when this failure is present — meaning the full extent of keyboard accessibility issues in the grid may be underreported until this is fixed.

Address the failures above, then re-run the tests. Refer to Keyboard operation for the full expected interaction spec.

Focus left data grid unexpectedly
StandardCriteria
WCAG1.3.2 Meaningful Sequence
EN 301 5499.1.3.2 Meaningful Sequence
Section 508N/A

Impact: Serious

Focus unexpectedly leaves the grid boundary when navigating with arrow keys or Home/End/Ctrl+Home/Ctrl+End. The expected behavior is that all directional navigation keys keep focus within the grid cells. When focus escapes to an element outside the grid, keyboard users lose their position and the reading sequence is disrupted.

To fix this:

  • Ensure role="grid" is on the container and role="row" / role="gridcell" are on the appropriate descendants so the browser can compute the correct grid structure.
  • Add keydown event listeners on the grid container that call event.preventDefault() for all navigation keys (arrow keys, Home, End, Ctrl+Home, Ctrl+End) and then programmatically move focus to the intended cell.
Grid arrow navigation failed
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

Users cannot navigate to neighboring cells using arrow keys. This is the most fundamental keyboard interaction in a data grid; without it, keyboard-only users have no way to move through the grid's data.

Implement keydown event listeners on the grid container for ArrowUp, ArrowDown, ArrowLeft, and ArrowRight. Each handler should:

  1. Calculate the target cell based on the current cell's row and column indices.
  2. Update tabindex (roving pattern) or aria-activedescendant to the target cell.
  3. Set focus on the target cell.
  4. Call event.preventDefault() to prevent the browser's default scroll behavior.
Failed next cell keyboard navigation
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

The grid does not support navigating to the next cell using ArrowRight or ArrowDown. Users relying on keyboard navigation are unable to move forward through the grid's data, making the component effectively unusable for keyboard-only users.

Add keydown event listeners for ArrowRight and ArrowDown that move focus to the next cell in the respective direction. Ensure the handler prevents default browser scrolling and that focus lands directly on the target gridcell element (or updates aria-activedescendant).

Failed previous cell keyboard navigation
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

The grid does not correctly handle ArrowLeft or ArrowUp to move focus to the previous cell. Keyboard users can navigate forward into the grid but cannot navigate backward, severely limiting their ability to review or interact with data they have already passed.

Add keydown event listeners for ArrowLeft and ArrowUp. Check that aria-activedescendant is updated correctly, or that tabindex is transferred to the target cell, when these keys are pressed.

Grid arrow navigation should not loop
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

Pressing an arrow key at the edge of a row or column causes focus to loop to the opposite edge — for example, pressing ArrowRight on the last cell in a row moves focus to the first cell of the same row. Looping behavior is not part of the ARIA grid pattern and is disorienting: users expecting to reach the edge of the grid instead find themselves unexpectedly repositioned.

Update the navigation logic to treat grid edges as hard boundaries. When focus is on:

  • The last cell in a row: ArrowRight should have no effect.
  • The first cell in a row: ArrowLeft should have no effect.
  • The last cell in a column: ArrowDown should have no effect.
  • The first cell in a column: ArrowUp should have no effect.
Grid row context lost on Home/End key press
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

Pressing Home or End moves focus to a different row rather than to the first or last focusable cell in the current row. The ARIA authoring practices specify that Home should move focus to the first cell in the current row and End to the last cell in the current row. Jumping to a different row causes users to lose their row context, making the grid harder to navigate.

Implement custom keydown handlers for Home and End that calculate the first and last cells in the currently focused row and move focus there, without changing the active row.

Grid does not support Home/End keys
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

The grid does not respond to Home, End, Ctrl+Home, or Ctrl+End key presses. These keys are part of the standard grid keyboard interaction and allow efficient navigation to row and grid boundaries without pressing arrow keys repeatedly.

Implement the following behaviors:

  • Home → first focusable cell in the current row.
  • End → last focusable cell in the current row.
  • Ctrl+Home → first cell in the grid.
  • Ctrl+End → last cell in the grid.
RTL navigation not supported
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Needs Review

The grid's keyboard navigation appears to use a right-to-left (RTL) convention, which is not currently supported by the tester. Users may experience unexpected behavior when navigating with arrow keys if the directionality of the navigation does not match the document or component's text direction setting.

Ensure the grid's keyboard navigation follows the expected convention for the document's text direction (dir attribute). If RTL support is intentional, verify that ArrowLeft moves focus toward the reading end of the row and ArrowRight moves toward the reading start, consistent with RTL user expectations.

Keyboard accessible

Unreachable grid cells
StandardCriteria
WCAG1.3.1 Info and Relationships
EN 301 5499.1.3.1 Info and Relationships
Section 508N/A

Impact: Needs Review

Some cells in the grid are not reachable by keyboard arrow navigation. Keyboard users cannot access or interact with the content in those cells. This may occur when cells lack a tabindex attribute, when navigation logic contains a bug that skips certain cells, or when cells are rendered in the DOM in a way that the navigation handler does not account for.

Ensure that every cell in the grid:

  • Has tabindex="-1" (or participates in the aria-activedescendant pattern) so it can receive programmatic focus.
  • Is reachable from an adjacent cell via the arrow key corresponding to its direction.
  • Is included in the navigation handler's cell-indexing logic (particularly important for grids with spanning cells or virtualized rows).
Focus inconsistently leaves data grid
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

A specific cell or set of cells causes focus to leave the grid when navigated to with arrow keys, even though other cells in the grid keep focus contained. This inconsistency indicates a bug in the navigation handler — for example, a cell that is missing a tabindex="-1" attribute, or whose keydown listener incorrectly falls through to the browser's default behavior.

Audit each cell that triggers the escape and verify that:

  • It has tabindex="-1" so it can receive programmatic focus.
  • The grid's keydown handler calls event.preventDefault() for navigation keys when focus is on that cell.

Multiple Widgets

Cell contains a composite element
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

A composite UI element that requires arrow keys to navigate — such as a radio group, listbox, or another grid — has been placed inside a grid cell. Because the grid itself uses arrow keys for cell-to-cell navigation, nesting a composite widget that also captures arrow keys creates a conflict: keyboard users may become trapped within the inner composite and unable to continue navigating the outer grid.

Where possible, keep composite widgets outside of grid cells. If a composite must appear in a cell, implement a clear Enter/Escape mode-switching pattern so users can enter the inner widget, interact with it using arrow keys, and then exit back to grid navigation mode.

Cell contains multiple interactives
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

A single grid cell contains more than one interactive element. Keyboard and screen reader users may struggle to efficiently navigate and interact with the cell's content — particularly because the grid navigation model assumes a relatively simple relationship between a cell and its focusable content.

Where possible, simplify cells by reducing the number of interactive elements. If multiple interactions are necessary within a cell, consider separating them into distinct cells, or ensure clear mode-switching (Enter to enter the cell, Escape to return to grid navigation) so users can Tab between the multiple controls within the cell while in interaction mode.

Focus Within Cell

Redundant focus on grid cell
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

During arrow key navigation, focus lands on the gridcell element rather than directly on the single interactive element within it — for example, landing on the cell rather than on the checkbox or link it contains. When a cell contains exactly one interactive element that does not require arrow keys to operate ( such as a checkbox, button, or link), the ARIA authoring practices recommend placing focus directly on that element rather than on the cell itself, as users expect to interact with the control immediately.

Update the navigation logic to set focus on the interactive element inside the cell when that cell contains exactly one such element, rather than on the gridcell container. Reserve focus on the cell itself for cells that contain no interactives or multiple interactives.

Focus lands within grid cell with multiple elements
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

During arrow key navigation, focus lands on an element inside the grid cell rather than on the cell itself, even though the cell contains multiple interactive elements or no interactive elements at all. In these cases, focus should remain on the gridcell element; the user enters the cell's content via Enter (interaction mode) and uses Tab or arrow keys to navigate within it.

Update focus management so that:

  • No interactive elements: focus stays on the gridcell itself during arrow navigation. The cell is read-only from a keyboard perspective.
  • Exactly one interactive element that does not require arrow keys ( checkbox, button, link): focus may be placed directly on that element, bypassing the cell container.
  • Multiple interactive elements: focus stays on the gridcell itself during arrow navigation. The user presses Enter or F2 to enter interaction mode, then uses Tab / Shift+Tab to move between the controls inside the cell, and Escape to return focus to the cell and exit interaction mode.

Interactions within cell

Grid cell not focused after action
StandardCriteria
WCAG2.1.1 Keyboard
EN 301 5499.2.1.1 Keyboard
Section 508N/A

Impact: Critical

When a user presses a key (Typically Enter or F2) to interact with a grid cell's content, focus leaves the cell entirely rather than remaining within it. Users lose their position in the grid and may not be able to resume navigation from where they left off.

Ensure that focus management within cells is handled explicitly:

  1. If a cell contains editable content or a composite element, pressing Enter should enter interaction mode and move focus to the content within the cell.
  2. Handle all relevant key events with explicit focus() calls and event.preventDefault() where necessary to prevent the browser from moving focus outside the grid.
Focus does not enter grid cell
StandardCriteria
WCAG2.1.1 Keyboard
EN 301 5499.2.1.1 Keyboard
Section 508N/A

Impact: Critical

Pressing a key that should switch the cell to interaction mode — typically Enter or F2 — does not move focus to the interactive element within the cell. Focus stays on the gridcell element, and users are unable to interact with the cell's content (for example, type in an editable field or operate a dropdown).

Add a keydown listener on the gridcell element (or delegate from the grid container) that responds to Enter and F2 by explicitly calling .focus() on the appropriate interactive element within the cell.

Grid cell lost focus on Escape
StandardCriteria
WCAG1.3.2 Meaningful Sequence
EN 301 5499.1.3.2 Meaningful Sequence
Section 508N/A

Impact: Moderate

Pressing Escape while in interaction mode inside a cell moves focus out of the grid entirely rather than returning it to the gridcell element. The expected behavior is that Escape exits interaction mode and returns focus to the cell itself, leaving the user in navigation mode at their previous position.

To fix this:

  1. Add a keydown listener on each interactive element within the cell that listens for Escape.
  2. On Escape, call .focus() on the parent gridcell element to re-enter navigation mode.
  3. Ensure the gridcell element itself has tabindex="-1" so it can receive programmatic focus.
Cannot restore grid navigation
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Best Practice

Pressing Escape to exit a cell's interaction mode does not correctly return focus to the gridcell element. This is a less severe variant of the issue above: focus may land somewhere within the grid but not precisely on the expected cell, making it difficult for users to orient themselves after exiting interaction mode.

Verify that:

  • All interactive elements within every grid cell have keydown listeners for Escape.
  • Each listener explicitly calls gridcellElement.focus() on the containing gridcell.
  • The gridcell element has tabindex="-1" to ensure it is programmatically focusable.

Event Listener Type

Deprecated keypress listener
StandardCriteria
WCAGN/A
EN 301 549N/A
Section 508N/A

Impact: Needs Review

The grid or one of its cells is using a keypress event listener for keyboard interaction. The keypress event is deprecated: it does not fire for non-printable keys in some browsers, behaves inconsistently with IME (non-Latin input methods), and may be removed in future browser versions. This can cause keyboard handlers to silently fail, particularly for Escape, Enter, ArrowUp, ArrowDown, and other navigation keys that data grids depend on.

Replace all keypress listeners with keydown listeners. The keydown event fires for every key press, including arrow keys, modifier keys, and non-printable characters, and is fully supported across all current browsers and assistive technology combinations.

keydown event handler example for a data grid
1gridContainer.addEventListener("keydown", (event) => {
2 const currentCell = document.activeElement;
3 switch (event.key) {
4 case "ArrowRight":
5 focusCell(currentCell, 0, +1);
6 event.preventDefault();
7 break;
8 case "ArrowLeft":
9 focusCell(currentCell, 0, -1);
10 event.preventDefault();
11 break;
12 case "ArrowDown":
13 focusCell(currentCell, +1, 0);
14 event.preventDefault();
15 break;
16 case "ArrowUp":
17 focusCell(currentCell, -1, 0);
18 event.preventDefault();
19 break;
20 case "Home":
21 focusFirstCellInRow(currentCell);
22 event.preventDefault();
23 break;
24 case "End":
25 focusLastCellInRow(currentCell);
26 event.preventDefault();
27 break;
28 case "Enter":
29 case "F2":
30 enterInteractionMode(currentCell);
31 break;
32 case "Escape":
33 exitInteractionMode(currentCell);
34 break;
35 }
36});