Skip to content

Sidebar thread row actions are unreachable with screen readers #13959

Open

Description

@akj

Follow-up to #13491 and #13910.

What happened

In NVDA browse mode, the sidebar thread list is not announced as a list, and the Snooze and Settle buttons on each row cannot be reached. Before #13491, NVDA reached them.

Diagnosis

Each thread row is a <div role="button"> with real buttons nested inside it: Snooze, Settle, Un-settle, Wake, Unpin, Dismiss Woke, and the PR badge. Draft rows nest Discard.

Whether VoiceOver makes users interact with a list depends on their web navigation setting. Lists require interaction under "Grouping items" but not under "DOM order" (Apple).

Steps to reproduce

  1. On Windows, run NVDA and open T3 Code (desktop, or web in Chrome or Firefox).
  2. In browse mode, press L to move to the thread list.
  3. Arrow through an active thread row.

Environment

main @ de251fc, Windows 11, latest stable NVDA and Chrome.

Workaround

The row's context menu (Applications key) includes Settle, Snooze, and Pin.

Proposed fix

Give each row one native button that opens the thread, named by its title. Put the row actions in the same <li> as siblings of that button, not inside it, and keep role="list". The button fills the row behind its content, so nothing changes visually: layout, colors, and the focus ring match main in Chromium.

In Sarah Higley's secondary actions study, buttons placed next to a tab worked in JAWS, NVDA, and VoiceOver. The same buttons nested inside the tab failed.

Branch: akj:fix/sidebar-row-sibling-actions (diff). It is verified with NVDA. Before a PR, it needs VoiceOver checks on macOS in Safari and Chrome, under both DOM order and Grouping items navigation.

Activity

  1. juliusmarminge commented on Sep 27, 2026

    @juliusmarminge
    Member

    Triage

    Confirmed on main at de251fc. This is not a duplicate, and the partial fix in #13910 never merged, so the default sidebar still has both pieces of the #13491 regression.

    The thread list is a <ul role="presentation">. Card, slim, and draft rows are each a <div role="button"> whose accessible name is title, status, project. Snooze, Settle, Un-settle, Wake, Unpin, Dismiss Woke, Discard, and the PR badge are real buttons or links inside that button. That matches the NVDA browse-mode failure. Search results are a separate case: a role="option" with a label and no nested actions. Those labels should stay.

    Removing the row label is not sufficient. ARIA treats a button’s children as presentational, and WebKit omits every descendant of role="button" from the accessibility tree whether or not the button has an author-supplied name. Each row needs one native button that opens it, with the actions as siblings in the same <li>, and the list role restored.

    akj:fix/sidebar-row-sibling-actions is that change. From the source, keyboard activation of the primary button still bubbles to the row click handler, and the action buttons already stop propagation. Please open it as a PR.

    VoiceOver is the merge check, which is why #13910 was held. #13491 set role="presentation" because an exposed list was a VoiceOver interaction boundary. On the PR, please include the Safari and Chrome pass under both DOM order and Grouping items, and specifically:

    • Snooze and Settle are reachable, and the title is announced first.
    • Status and project are still announced once. The branch names the primary button with the title only; status and project are sibling text, and the visible title stays aria-hidden.
    • The primary button is actually in the tree. It fills the row from behind the content (inset-0, negative z-index). You already verified that with NVDA in Chromium; VoiceOver may treat a covered control differently.

    Until this lands, the row context menu is how to reach Settle, Snooze, and Pin.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Sep 27, 2026
  3. juliusmarminge commented on Oct 22, 2026

    @juliusmarminge
    Member

    Existing fix: #13962, already taken over by a maintainer. No competing PR opened. Its comments record passing contributor NVDA checks; the remaining gate is VoiceOver in Safari and Chrome under both DOM order and Grouping items. This triage host is Linux and has neither NVDA nor Safari, so native screen-reader reproduction was not run. Leaving this issue open for the existing fix and native verification.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions