# Epic DREAM-874 - Full keyboard and screenreader accessibility for work packages tables/list screen

Project: Stream Design System
Status: in development
URL: https://community.openproject.org/work_packages/DREAM-874

## Detailed specification

### Motivation and background information

The Work Packages table cannot be used without a pointer: every row and read-only cell is its own Tab stop, there is no arrow-key navigation, selection is conveyed only visually, and manual sorting requires drag-and-drop. This excludes keyboard-only and screen-reader users from the most common bulk workflows (move, copy, change status, delete, reorder). Backlogs already provides accessible selection on the shared `BatchSelection` model; the table should offer the same model so users learn one set of rules. WCAG 2.1 AA (2.1.1 Keyboard, 4.1.2 Name/Role/Value) requires it.

### Acceptance criteria

  - The table is exposed as a grid or treegrid with an accessible name, column headers, `aria-multiselectable`, and `aria-selected` on every selectable row (<mention class="mention" data-id="DREAM-846" data-type="work_package" data-text="#DREAM-846">#DREAM-846</mention>).
  - The table has one entry in the page Tab sequence; arrow keys move between cells and rows, hidden rows are skipped, and focus is recovered after re-renders (<mention class="mention" data-id="DREAM-846" data-type="work_package" data-text="#DREAM-846">#DREAM-846</mention>).
  - Rows can be toggled and range-selected from the keyboard with the same anchor/range rules as Backlogs; pointer and keyboard gestures can be mixed (<mention class="mention" data-id="DREAM-846" data-type="work_package" data-text="#DREAM-846">#DREAM-846</mention>).
  - **Shift+F10** and the **Menu** key open the context menu for the focused row and focus returns to the originating cell when it closes (<mention class="mention" data-id="DREAM-846" data-type="work_package" data-text="#DREAM-846">#DREAM-846</mention>).
  - Manual sort order can be changed via context menu move actions without drag-and-drop (<mention class="mention" data-id="DREAM-876" data-type="work_package" data-text="#DREAM-876">#DREAM-876</mention>).
  - **Escape** clears the selection in every table and card view when no overlay, menu or editor owns the key (<mention class="mention" data-id="OP-20331" data-type="work_package" data-text="#OP-20331">#OP-20331</mention>).
  - Selection state and changes are perceivable with NVDA (Firefox, Chrome) and VoiceOver (Safari); the required screen-reader mode is documented.
  - Everything ships together in OpenProject 18.0.0 without feature flags; no release contains the grid role with unreachable cells or keyboard selection without exposed state.

### Out of scope

  - Card view and Boards keyboard accessibility.
  - Timeline (Gantt) keyboard interaction.
  - Column and cell-range selection; selection stays row-based.
  - Batch drag-and-drop and keyboard grab-and-drop of selected rows.
  - Selecting beyond the rendered page (whole-query select all).
  - A persistent visible selection count (separate work coordinated with Backlogs).
  - Changes to Backlogs behaviour; **Ctrl/Cmd+Shift+A** compatibility (<mention class="mention" data-id="DREAM-936" data-type="work_package" data-text="#DREAM-936">#DREAM-936</mention>).
  - Separately implemented tables not using the shared work package table component.
  - Third-party table libraries (e.g. TanStack Table).

| Attribute | Value |
| --- | --- |
| Mockups |  |

## Planning

| Attribute | Value |
| --- | --- |
| Roadmap | 2026 Q4 |
| Date |  |
| Votes |  |
| List | a11y report 2026 |
| Target versions | 18.0.0, 18.1.0 |
| Sprint |  |
| Backlog bucket |  |
| Relevance |  |

## Subitems

| ID | Type | Subject | Status | Assignee | Target versions | Project | Priority | Spent time |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| DREAM-846 | Feature | Make selection and existing context actions in the Work Packages table fully accessible to keyboard and assistive-technology users | merged |  | 18.0.0 | Stream Design System | High |  |
| DREAM-876 | Feature | Provide move context menu actions in the Work Packages table | merged |  | 18.0.0 | Stream Design System | Medium |  |
| DREAM-978 | Feature | Use Primer Blankslate for Angular no-results states | closed |  | 18.0.0 | Stream Design System | Medium |  |
| OP-20331 | Feature | Clear work package selection with Escape instead of Ctrl/Cmd+D | in test | Ivana Šugić | 18.0.0 | OpenProject | Medium |  |
| DREAM-996 | Feature | Accessible row positions for expanded relation columns and after manual-sort drops | in specification |  | 18.1.0 | Stream Design System | Medium |  |
| DREAM-940 | Open Point | Revisit Escape clearing scope for work package table selection | needs clarification |  |  | Stream Design System | Medium |  |
| DREAM-965 | Open Point | Decide the rendering and row-model approach for the work package table | needs clarification |  |  | Stream Design System | Medium |  |
| DREAM-999 | Open Point | Allow paging in manually sorted work package tables? | new |  |  | Stream Design System | Medium |  |

## Bugs and other work packages

| ID | Type | Subject | Status | Assignee | Target versions | Module |
| --- | --- | --- | --- | --- | --- | --- |
| OP-20196 | Code maintenance | Add a Vitest harness for the work package table | closed |  | 18.0.0 | Modules and project features / Work packages, Frontend components and Primer design system |
| OP-20384 | Code maintenance | Simplify Angular selection anchor and focus ownership | tested |  | 18.0.0 | Frontend components and Primer design system, Modules and project features / Work packages |
| OP-20383 | Code maintenance | Align Angular table and card range selection with Sortable Lists | tested |  | 18.0.0 | Frontend components and Primer design system, Modules and project features / Work packages |
| OP-20382 | Code maintenance | Share selection gestures between table and card view | tested |  | 18.0.0 | Frontend components and Primer design system, Modules and project features / Work packages, Accessibility |
| DREAM-1021 | Bug | Dropdown menus on work package pages cannot be operated with arrow keys | confirmed |  | 18.0.0 | Modules and project features / Work packages, Accessibility, Frontend components and Primer design system |
| DREAM-939 | Code maintenance | Modernize the work package table Angular shell (signal inputs, effects, takeUntilDestroyed) | needs review | Henriette Darge | 18.0.0, 18.1.0 | Frontend components and Primer design system, Modules and project features / Work packages |
| DREAM-967 | Code maintenance | Cover the work package table code fully | review approved | Alexander Coles | 18.1.0 |  |
| DREAM-946 | Code maintenance | Evaluate focusZone from @primer/behaviors for the table's roving tabindex | rejected |  |  |  |
| DREAM-965 | Open Point | Decide the rendering and row-model approach for the work package table | needs clarification |  |  |  |
| DREAM-940 | Open Point | Revisit Escape clearing scope for work package table selection | needs clarification |  |  |  |
| DREAM-999 | Open Point | Allow paging in manually sorted work package tables? | new |  |  |  |

## Details

| Attribute | Value |
| --- | --- |
| Module | Modules and project features / Work packages, Frontend components and Primer design system |
| Enterprise plan | Community (COM) |
| Labels |  |

## People

| Attribute | Value |
| --- | --- |
| Assignee | Alexander Coles |
| Accountable |  |
| Product manager |  |
| Designer |  |
| Developers | OP Dev Dream Team |
| Requested by |  |
| Interest by |  |

## Estimates and progress

| Attribute | Value |
| --- | --- |
| Work |  |

## Description

**As** a keyboard or screen-reader user
**I want to** operate the Work Packages table without a pointer
**so that** I can navigate, select, and act on work packages the same way pointer users can.

### Background

The Work Packages table is OpenProject's central view, and today it is not operable without a pointer. Rows and read-only cells are plain Tab stops with no arrow-key navigation, selection exists only as a CSS class invisible to assistive technology, keyboard selection is limited to select-all/deselect-all, and manual sorting is drag-and-drop only. Backlogs (<mention class="mention" data-id="AGILE-181" data-type="work_package" data-text="#AGILE-181">#AGILE-181</mention>) already established shared pointer and keyboard selection rules on the `BatchSelection` model; the foundations for reusing them in the table landed in <mention class="mention" data-id="OP-20196" data-type="work_package" data-text="#OP-20196">#OP-20196</mention>, <mention class="mention" data-id="OP-20382" data-type="work_package" data-text="#OP-20382">#OP-20382</mention>, <mention class="mention" data-id="OP-20383" data-type="work_package" data-text="#OP-20383">#OP-20383</mention> and <mention class="mention" data-id="OP-20384" data-type="work_package" data-text="#OP-20384">#OP-20384</mention>.

This epic groups the work that brings the table to parity, targeting OpenProject 18.0.0:

  - <mention class="mention" data-id="DREAM-846" data-type="work_package" data-text="#DREAM-846">#DREAM-846</mention> — grid semantics, single-Tab-stop focus model, keyboard row selection, keyboard access to the existing context menu, selection pruning when rows change, and screen-reader feedback.
  - <mention class="mention" data-id="DREAM-876" data-type="work_package" data-text="#DREAM-876">#DREAM-876</mention> — move actions in the context menu as the keyboard alternative to drag-and-drop manual sorting.
  - <mention class="mention" data-id="OP-20331" data-type="work_package" data-text="#OP-20331">#OP-20331</mention> — **Escape** clears the selection, replacing **Ctrl/Cmd+D**, matching Sortable Lists.

Backlogs still limits selection to cards the user may move, so its fixed cards cannot follow the table's <mention class="mention" data-id="OP-20333" data-type="work_package" data-text="#OP-20333">#OP-20333</mention> context-menu rule; the open point and its likely resolution (selection independent of movement permissions) are recorded in <mention class="mention" data-id="AGILE-181" data-type="work_package" data-text="#AGILE-181">#AGILE-181</mention>. Keep the two aligned.

The role decision (`grid` vs `treegrid`) for flat, grouped and hierarchy tables is owned by <mention class="mention" data-id="DREAM-860" data-type="work_package" data-text="#DREAM-860">#DREAM-860</mention>. Accessible feedback wording is coordinated with <mention class="mention" data-id="AGILE-424" data-type="work_package" data-text="#AGILE-424">#AGILE-424</mention> so Backlogs and the table speak the same way.

### Research

Survey of keyboard models in accessible grids and tree grids (WAI-ARIA APG, React Aria/Spectrum, AG Grid, MUI X, Fluent, Salesforce, Carbon, Primer, plus Jira, Linear, Asana, Notion, Airtable, GitHub Projects) and screen-reader guidance, 2026-10-22: attached `2026-10-22-dream-886-grid-keyboard-research.md`. Findings that shape this epic: every accessibility-grade grid is a single Tab stop with arrow keys inside and Tab leaving (no source moves Tab row to row); Right/Left on a focused row expand and collapse; Enter or F2 enters a cell's controls and Escape leaves; Shift+Space or Space toggles a row and Shift+Up/Down extends; row focus with Right/Left switching to cells is first-class in React Aria, Fluent and Salesforce tree grid. <mention class="mention" data-id="DREAM-860" data-type="work_package" data-text="#DREAM-860">#DREAM-860</mention> records the decisions drawn from it.
