# Feature DREAM-996 - Accessible row positions for expanded relation columns and after manual-sort drops

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

## Planning

| Attribute | Value |
| --- | --- |
| Date |  |
| Priority | Medium |
| Target versions | 18.1.0 |
| Sprint |  |
| Backlog bucket |  |
| Votes |  |
| List | a11y report 2026 |
| Roadmap |  |
| Relevance |  |

## OPEN POINTS / FEEDBACK

| ID | Subject | Status | Decision |
| --- | --- | --- | --- |
| DREAM-944 | Decide the counting model for row positions in expanded relation and children columns | needs clarification |  |

## Features and Chunks

| ID | Subject | Type | Status | Assignee | Target versions | Spent time |
| --- | --- | --- | --- | --- | --- | --- |
| DREAM-942 | Row positions for expanded relation and children columns | Implementation | specified |  | 18.1.0 |  |
| DREAM-943 | Row positions after a drop in manually sorted queries | Implementation | specified | Alexander Coles | 18.1.0 |  |

## Bugs and other work packages

_No work packages_

## Technical Details

| Attribute | Value |
| --- | --- |
| Module | Modules and project features / Work packages, Accessibility, Frontend components and Primer design system |
| Feature Flag |  |
| Requires doc change | No |
| Enterprise plan |  |

## Details

| Attribute | Value |
| --- | --- |
| Pull preview |  |
| Mockups |  |
| Accessibility | Yes |

## People

| Attribute | Value |
| --- | --- |
| Assignee |  |
| Accountable |  |
| Product manager |  |
| Developers |  |
| Designer |  |
| Ordered by |  |
| Signed off by |  |
| Interest by |  |

## Estimates and progress

| Attribute | Value |
| --- | --- |
| PD (min) |  |
| PD (max) |  |
| Work |  |
| Labor costs |  |

## Description

**As** a screen-reader user working in the work package table  
**I want to** hear my row position and the row count while a relation or children column is expanded, and after I reorder rows by drag and drop  
**so that** I keep my bearings in the two states where OpenProject 18.0.0 withdraws positions.

### Background

<mention class="mention" data-id="DREAM-846" data-type="work_package" data-text="#DREAM-846">#DREAM-846</mention> (18.0.0) publishes `aria-rowindex`/`aria-rowcount` through <mention class="mention" data-id="DREAM-938" data-type="work_package" data-text="#DREAM-938">#DREAM-938</mention> and withdraws them rather than guessing in two states: while any relation or children column is expanded, and after a drop in a manually sorted query until the next load. <mention class="mention" data-id="DREAM-846" data-type="work_package" data-text="#DREAM-846">#DREAM-846</mention> lists both as proposed accepted gaps for 18.0.0, pending #DREAM-897's screen-reader evidence and feature-owner acceptance. This feature closes them in 18.1.0. Server-side row metadata (<mention class="mention" data-id="DREAM-941" data-type="work_package" data-text="#DREAM-941">#DREAM-941</mention>) was rejected, so the work stays in the frontend unless <mention class="mention" data-id="DREAM-944" data-type="work_package" data-text="#DREAM-944">#DREAM-944</mention> chooses a server-counted model.

### Acceptance criteria

  - While a relation or children column is expanded, every exposed row carries a position under the counting model decided in <mention class="mention" data-id="DREAM-944" data-type="work_package" data-text="#DREAM-944">#DREAM-944</mention>. Repeated occurrences of one work package each get their own position. Unknown totals are not fabricated. Collapsing every owner restores query-wide positions with a total in flat and grouped tables.
  - Each expanded relation or children expander references the rows it shows (`aria-controls`).
  - After a drop in a manually sorted query (saved ungrouped, grouped including cross-group drops, and unsaved), positions return once the drop settles, without a reload and without losing focus or selection.
  - In any state the model does not cover, positions stay withdrawn rather than wrong.
  - Verified with NVDA (Firefox and Chrome) and VoiceOver (Safari), reusing \#DREAM-897's practice; results recorded here.

### Technical notes

  - Implementation chunks, target version 18.1.0:
    1.  Counting model: <mention class="mention" data-id="DREAM-944" data-type="work_package" data-text="#DREAM-944">#DREAM-944</mention> (Open Point, blocks <mention class="mention" data-id="DREAM-942" data-type="work_package" data-text="#DREAM-942">#DREAM-942</mention>).
    2.  Expanded relation and children columns: <mention class="mention" data-id="DREAM-942" data-type="work_package" data-text="#DREAM-942">#DREAM-942</mention>.
    3.  Positions after a drop in manually sorted queries: <mention class="mention" data-id="DREAM-943" data-type="work_package" data-text="#DREAM-943">#DREAM-943</mention>.
  - Both implementation chunks build on the <mention class="mention" data-id="DREAM-959" data-type="work_package" data-text="#DREAM-959">#DREAM-959</mention> refactorings (row-state painter, row-mutation protocol).

### Open decisions

  - **Counting model:** <mention class="mention" data-id="DREAM-944" data-type="work_package" data-text="#DREAM-944">#DREAM-944</mention> chooses between server counting of every occurrence (option 1) and counting only expanded occurrences (option 2).

### Not yet specified

  - Screen-reader verification for 18.1.0: no work package yet. Shape it once <mention class="mention" data-id="DREAM-897" data-type="work_package" data-text="#DREAM-897">#DREAM-897</mention> shows how the 18.0.0 withdrawal reads.

### Out of scope

  - Sibling attributes `aria-posinset`/`aria-setsize` (<mention class="mention" data-id="DREAM-860" data-type="work_package" data-text="#DREAM-860">#DREAM-860</mention>).
  - Embedded tables and Gantt.
  - Every other keyboard and semantics change: <mention class="mention" data-id="DREAM-846" data-type="work_package" data-text="#DREAM-846">#DREAM-846</mention>.
