An editable grid is not a table with extra buttons. It is a composite widget, which means only one of its focusable elements sits in the page's tab sequence and the author has to write every bit of movement inside it by hand. W3C's pattern says that in as many words, and it is the reason a grid cannot be audited the way a table is.
The consequence people miss is what happens to content that cannot take focus. In a grid, every cell either contains a focusable element or is focusable itself, and W3C gives one exception, for header cells that provide no function like sort or filter. That rule sounds fussy until you read the reason attached to it.
The sentence that explains most grid findings
Screen readers are typically in their application reading mode, rather than document reading mode, while somebody is working inside a grid. In application mode a reader hears only focusable elements and the content that labels them. So anything in a cell that cannot take focus and does not label something that can is simply not heard, and W3C's own wording is that users may unknowingly overlook it. Not hidden, not broken, not flagged by any scanner. Just absent.
Which is why the unit of testing is the task rather than the grid. Filter to a row, open it, change a value, deal with the validation error, save it, and confirm the save was announced. If somebody can do that, the grid works. If they can see every cell in a code review and cannot finish that sequence, it does not.
Two Modes, and the Keys That Move Between Them
A grid runs in two states and half of all grid bugs live in the transition. While arrow keys are moving focus from cell to cell, those keys are not available to move an editing caret or operate a control inside a cell. Something has to switch modes.
W3C's conventions are worth knowing, because a grid that follows them will feel familiar and one that invents its own will not. Enter leaves navigation and puts focus in the editor or on the first widget in the cell. F2 does the same, and pressing F2 again restores grid navigation. Typing an alphanumeric key starts editing directly. Escape restores navigation and, if content was being edited, may also undo the edit.
That last clause is worth reading twice, because Escape doing two jobs is exactly the kind of thing that gets a bug report filed against the wrong component. Somebody presses Escape to get out of a cell and loses the value they typed. That is documented behaviour rather than a defect, and knowing it is documented changes the conversation.
APG is guidance, not a specification, and none of these keystrokes is a WCAG requirement. What they give an audit is a reference point, so a finding can say the grid departs from the published pattern and explain what that cost a user, rather than asserting a rule that does not exist.
Virtualization Is the Setting That Decides What Exists
A grid showing ten thousand rows is not rendering ten thousand rows. It renders the visible window and swaps the contents as you scroll, which is excellent for performance and consequential for anyone reading the accessibility tree, because the rows that are not rendered are not there.
W3C's pattern flags one specific symptom. Where navigation can add rows or columns to the DOM as you move, a key that jumps to the end of the grid, such as Control+End, may land on the last row in the DOM rather than the last row in the data. The user asked for the bottom of the table and got the bottom of the rendered window, with nothing to tell them apart.
The pattern also names the properties that exist for this case. Where rows or columns are hidden or absent from the DOM, aria-rowcount and aria-colcount carry the real totals and aria-rowindex and aria-colindex carry each cell's true position. A virtualized grid without them is telling a screen reader that fifty rows is the whole story.
What a Component Vendor's Report Looks Like When It Is Honest
Grid components usually ship with a published conformance report, and reading one properly is a skill worth having. Handsontable's is a good specimen, because it is current, detailed and unusually candid about its own limits.
The report is dated March 2026 at version 1.1, uses the VPAT 2.5 format, and rests on an external audit carried out from December 2025 to January 2026 against WCAG 2.1, with the extra WCAG 2.2 criteria assessed internally. Its stated scope is eight interaction patterns, named individually. And its summary across the 55 Level A and AA criteria reads 8 Supports, 8 Partially Supports, 7 Does Not Support, 17 Not Applicable and 15 Not Evaluated.
Fifteen unevaluated criteria out of fifty-five is the number a buyer should notice, and it is printed on the vendor's own page. None of that is a criticism of the product. It is an accurate description of what one report covers, which is more than most reports offer, and it tells you precisely where your own testing has to start.
Two more lines from the same document earn their place here. The vendor states that the component is embeddable rather than standalone, that the final experience depends on developer configuration and the host application, and that integrators bear responsibility for application-level conformance. And it states that row and column virtualization is enabled by default, recommending two settings, renderAllRows and renderAllColumns, for complete assistive technology support. Both sentences are true of the category rather than of one product.
| What to look for | Why it matters | Example from one published report |
|---|---|---|
| Report date and version | A report describes a moment, not a product | March 2026, version 1.1 |
| Standard and level claimed | 2.1 and 2.2 are different lists | External audit against 2.1, the 2.2 additions assessed internally |
| Who did the evaluation | Internal preparation from an external audit is common and worth knowing | Prepared internally, based on a named external audit |
| Stated scope | This is the sentence that controls every row below it | Eight named interaction patterns |
| Not Evaluated count | The rows nobody looked at | 15 of 55 |
| Assistive technology tested | A name missing here is a gap in the evidence | VoiceOver and NVDA named, JAWS listed as supported but not in the latest cycle |
| Configuration notes | Defaults are rarely the accessible setting | Virtualization on by default, with two settings recommended for full support |
| Ownership statement | Where the vendor's responsibility ends | Integrators bear application-level conformance |
The Grid-in-Task Matrix
This is the artifact. Every row is a task somebody performs inside the grid, and the states column is what turns a one-line test into a real one.
| Task | States to reach | What is in play |
|---|---|---|
| Find the grid and enter it | Empty, loading, loaded | One tab stop, and whether the grid has an accessible name |
| Move around it | Top, middle, last rendered row, last row in the data | Arrow keys, Home and End, and the DOM-versus-data problem |
| Read a cell that is not interactive | Text, number, formatted value, blank | Application mode, and whether the content can take focus |
| Edit a value | Text, number, date, dropdown, checkbox | Entering and leaving edit mode, and what Escape does |
| Fail validation | Inline error, row-level error, blocked save | Error identification and whether the error is announced |
| Sort a column | Ascending, descending, unsorted | aria-sort on the header, and whether the change is announced |
| Filter the data | Results, no results, filter cleared | The result count, which is a status message |
| Select rows | One, several, all, none | aria-selected, and whether the count is announced |
| Resize or reorder a column | Both, by pointer and by keyboard | Dragging Movements, covered on our whiteboard page too |
| Expand a nested or grouped row | Collapsed, expanded | Whether the expander is reachable at all in the default configuration |
| Load more rows | Scrolled, paged, infinite | Infinite scroll applies where the grid loads as you go |
| Save and confirm | Saved, failed, conflicted | Whether the outcome is announced without moving focus |
Three Things to Settle Before the Work Starts
- Which grid, at which version, in which configuration. A grid's behaviour is configurable enough that the version and the settings are test conditions rather than trivia. Record them the way you record the browser.
- Seeded data that reaches the states. An empty grid, a grid with one row, a grid with more rows than render at once, a row that fails validation, and a cell type of each kind. Without seeded data half the matrix is unreachable and the report says so.
- Which findings are the component's and which are yours. A vendor's defect and your integration are different fixes with different owners, and the report is more useful when it keeps them apart.
One honest limit
We audit and never repair, so this is not an offer to reconfigure your grid or patch a component. And a vendor's published report is their statement rather than our finding, so nothing here says how any product behaves today. What an evaluation produces is the task matrix above, worked on your build with your data, with the unreachable states named. Scope it at contact or read what the web app audit covers.