Build a case operations workspace with a 62-row queue, an assignee search field, priority reordering, and background status updates. The read-only queue starts as a native table with labeled headers and one clear case action per row. If the team adds cell editing, define a separate grid interaction mode, including arrow movement, entry into an editor, Escape behavior, and an exit path. The assignee combobox keeps DOM focus on its input, references a mounted active option, and ignores stale search responses. Reordering works with pointer drag, keyboard controls, and a single-pointer path that does not require dragging; each sends the same stable-ID mutation with a version check. A successful save announces one concise status without stealing focus. A blocking conflict offers a focused error summary and a route back to the affected control.
Project: accessible operations workspace
Build contract
- Compare a native read-only table with an interactive grid prototype; count Tab stops and test header context, edit mode, sorting, and virtualization.
- Exercise combobox Down Arrow, Up Arrow, Enter, Escape, Tab, text editing, result shrinkage, pointer selection, and out-of-order responses.
- Move case 47 with keyboard and taps without dragging; preserve focus, announce its new position, and reject a concurrent stale order.
- Stream 62 progress updates while a note field has focus; keep announcements bounded and distinguish routine success from a blocking conflict.
Implementation checkpoint
function movedCasePosition(order, caseId) {
const position = order.indexOf(caseId);
return position < 0 ? null : { position: position + 1, total: order.length };
}
console.log(JSON.stringify(movedCasePosition([62, 47, 93], 47)));
// Output: {"position":2,"total":3}Cost and boundaries
A read-only table with n rows and c columns mounts O(n × c) cells; virtualization lowers mounted DOM work but can remove the focused row and hide total context unless carefully implemented. Filtering n local combobox options is O(n) per query, while server search adds network races. Reordering a copied n-item ID list is O(n) time and memory; a rank-based server model may be cheaper at large scale but needs conflict handling. Short status messages cost little compute, yet unbounded live-region announcements impose high human cost. Measure keyboard task steps and focus stability with actual users and assistive technology alongside render performance.
Failure drill
Replace the table role with grid and observe that arrow keys still scroll the page. Either implement the grid keyboard contract or remove the role. Return an old assignee result after a newer query and inspect whether aria-activedescendant points to a removed option. Filter the priority list during a drag and see whether a DOM index moves the wrong case; switch to stable IDs and a version. Then make every streamed progress row live and listen for repeated announcements while typing. A routine success toast must not move the caret from the note field, while a failed submission needs a discoverable error route.
Acceptance checks
- Read-only rows expose useful header context without hundreds of Tab stops.
- Combobox focus, active option, and current query remain in agreement under delayed results.
- Every reorder action works by keyboard and a non-drag pointer method with correct IDs.
- Background success preserves focus, and conflicts provide a clear recovery path.
Common Mistakes
- Adding grid semantics without its keyboard model.
- Leaving an active-descendant reference after its option unmounts.
- Offering keyboard reordering as the only alternative to pointer dragging.
- Making an entire changing panel a live region.
Related lessons
Data Table Versus Interactive Grid; Combobox Suggestions and Active Option; Reorder Controls Without Dragging; Async Status Announcements and Focus; Accessible Names and Native Controls.
