Skip to content

Make topic tabs accessible to screen readers - #31110

Open
rezabakhshilaktasaraei wants to merge 7 commits into
telegramdesktop:devfrom
rezabakhshilaktasaraei:subsection-tabs-accessibility
Open

Make topic tabs accessible to screen readers#31110
rezabakhshilaktasaraei wants to merge 7 commits into
telegramdesktop:devfrom
rezabakhshilaktasaraei:subsection-tabs-accessibility

Conversation

@rezabakhshilaktasaraei

@rezabakhshilaktasaraei rezabakhshilaktasaraei commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

The topic tabs inside a forum chat (and the sender tabs in a channel's Direct Messages inbox) were invisible to screen readers: the tab buttons had no names, no list semantics and no keyboard access, so a blind user could not tell which topics exist, which one is active, or switch between them.

The tab strip is now a tab control - it is named tabs everywhere in the UI, so the screen reader calls it that too. Each tab is a named item (title with unread count, mention state and the pinned mark, updated as the badges change), the active one is reported as selected, and the strip is named "Topics" or "Direct Messages" with the right orientation for each position. In screen reader mode the strip gets one roving Tab stop on the active tab; arrows move focus between tabs (Up/Down when vertical, Left/Right when horizontal, Home/End for the edges) without switching - the browse-then-commit pattern for tabs whose panes load their content - Enter or Space activates, the context menu key opens the topic menu anchored on the tab (a mouse-invoked menu keeps opening at the cursor), and the focused tab is scrolled into view. Keyboard focus survives the frequent rebuilds of the strip on unread updates. The folders sidebar follows in #31176.

The position toggle button got an accessible name, with the current position reported in its description. It is also one persistent widget now, reused across mode switches instead of being recreated with the strip, so pressing it from the keyboard keeps focus on it without the screen reader re-announcing the button on every press. Sighted users are unaffected - the button is only focusable in screen reader mode.

The strip is created only once it is needed, long after the top bar, the list and the composer, so its Tab stops used to land at the end of the chain. The chat sections now keep their Tab chain in visual order (the lib_ui visual Tab order added for the chat list column), which places the strip at its reading-order position wherever it sits - left column, top or bottom - and follows it as the toggle moves it around. The one thing the container cannot see on its own is the roving stop moving between tabs deep inside the strip, so the slider tells it after each move; without that, a Tab entering the strip from outside would follow wiring to the previous stop. The composer inside a topic needed the same treatment: its buttons are created in an order unrelated to the row layout, so switching topics used to scramble their Tab order, and its send-as picker was missing its accessible name.

Needs desktop-app/lib_ui#348 for the PageTab button role.

Tested with NVDA on Windows 10 in a forum group with the tabs layout: browsing, activation, unread counts, context menus and cycling the tab bar through all three positions - after each move the next Tab finds the strip at its new place, and leaving the strip and Tabbing back in lands on the active tab - the arrows browse freely inside, but once focus leaves, the roving stop parks back on the current topic.

@rezabakhshilaktasaraei
rezabakhshilaktasaraei force-pushed the subsection-tabs-accessibility branch 2 times, most recently from 534b1e1 to 36f9dfc Compare August 5, 2026 05:00
@rezabakhshilaktasaraei
rezabakhshilaktasaraei force-pushed the subsection-tabs-accessibility branch from 36f9dfc to e3969f4 Compare August 26, 2026 03:05
The strip is called tabs everywhere in the UI, its position toggle
included, so a screen reader should call its items tabs as well. The
browsing stays as it was - arrows move between tabs and Enter switches -
matching how a tab list whose panes load their content is supposed to
behave. The folders sidebar keeps being a list: its locked folders fit
plain list items better than tabs that refuse to become current.
The buttons of ComposeControls are created in an order unrelated to
where they sit in the row (the send button first of all), so switching
to a topic scrambled the Tab chain that HistoryWidget already keeps in
layout order. Also name the send-as button the way HistoryWidget names
its own.
Browsing with the arrows used to leave the roving Tab stop on the last
browsed tab, so Tabbing back into the strip landed there instead of on
the current topic. Once focus leaves the strip, the stop parks back on
the active tab, the way the chat folder tabs behave.
The context menu key picked the right topic but popped the menu at the
mouse cursor, which may sit nowhere near the tab or outside the window
altogether. The slider now sends the position to show the menu at
along with the section index - the tab itself for a keyboard-invoked
menu, the cursor as before for a mouse-invoked one.
The chat list already reports pinned chats as pinned; the pinned tabs
of the strip conveyed it only by their ordering. The tab name carries
the same pinned mark now.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant