compose: Improve message view screen-reader focus order#6536
Conversation
The title and subtitle were traversed by raw element position, so a screen reader could enter the header on the subtitle instead of following the visual order. Group the title and subtitle as one traversal unit so the header reads back button, heading, subtitle, avatar.
A Material Scaffold does not reliably sort its scrollable content between the top and bottom bars for accessibility traversal, so the reading cursor could jump from the composer straight to the header, skipping the messages. Mark the header, message list, and composer as traversal regions with explicit order (header first, composer last) so traversal is header, messages, composer.
TalkBack rebuilds its traversal tree from the traversalBefore links that Compose computes, relocating each link's source next to its target, and it silently drops links from a node to its own descendant, such as a message cell linked before its inner button. A reverseLayout list composes newest-first but reads oldest-first, so nearly every link demanded a relocation, and the dropped cell links left backward navigation skipping the message cells and jumping across messages. Give each lazy item a zIndex that mirrors its traversal index. Compose orders sibling semantics nodes by zIndex, so this makes the semantics tree order equal to the reading order, the links match the natural order, and TalkBack's rebuild becomes a no-op. Pin the reading-order slot of the reactions bubble to its tree position (last in the cell) for the same reason.
Document the leaf-first / trust-defaults principles, the semantics decision tree, and the TalkBack traversal-order model (semantics tree order must match reading order) with the diagnostics used to verify it.
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
|
@CodeRabbit review |
✅ Action performedReview finished.
|
SDK Size Comparison 📏
|
WalkthroughThis PR adds explicit Jetpack Compose semantics-based traversal ordering to align TalkBack screen-reader focus order with visual layout across ChannelScreen, ChannelHeader, MessageContainer, and Messages, and adds ACCESSIBILITY.md documenting accessibility guidance and anti-patterns. ChangesCompose accessibility traversal order
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant TalkBack
participant ChannelScreen
participant ChannelHeader
participant Messages
participant MessageContainer
TalkBack->>ChannelScreen: traverse semantics tree
ChannelScreen->>ChannelHeader: focus header region (traversalIndex -1)
ChannelHeader-->>TalkBack: announce title/subtitle group
ChannelScreen->>Messages: focus list region (traversalIndex 0)
Messages->>MessageContainer: order message items (itemTraversalOrder)
MessageContainer-->>TalkBack: announce message then reactions group
ChannelScreen->>ChannelScreen: focus composer region (traversalIndex 1)
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The message list now orders its semantics tree oldest-first so screen readers traverse it in reading order, which makes the UiAutomator enumeration order oldest-first as well. The e2e position lookups indexed message cells through the enumeration order and assumed newest-first, so they started acting on the wrong message (quote, delete, open thread). Sort the matched cells by their on-screen position (bottom-most first) so the lookups keep their newest-first meaning regardless of the accessibility enumeration order.
5ee98c3 to
552b670
Compare
|
|
🚀 Available in v7.6.0 |



Goal
Make the TalkBack focus order in the message view predictable and match the visual reading order, without changing the reversed message list layout that chat relies on. Before this change the reading cursor could enter the header on the subtitle, jump from the composer straight to the header and skip the messages, and, moving backwards from the composer, land inside a reaction or an attachment of a neighbouring message instead of on the message itself.
Resolves AND-1280
Implementation
zIndexthat mirrors its traversal index, so the semantics tree order equals the reading order and TalkBack has nothing to rearrange. The reactions bubble is pinned to its tree position for the same reason.stream-chat-android-compose/ACCESSIBILITY.md, a playbook covering the leaf-first principles, the semantics decision tree, and the traversal-order model with the diagnostics used to verify it.The interactive parts of a message (open attachment, vote, View Results, reactions) stay individually reachable. They are not collapsed into a single stop, so poll voting and attachment actions remain usable.
No public API changes.
🎨 UI Changes
Screen_recording_20260702_144920.webm
Screen_recording_20260702_131950.webm
Screen_recording_20260702_144956.webm
Screen_recording_20260702_132040.webm
Testing
Manual, with TalkBack enabled on a device or emulator:
One Compose platform limitation is out of scope and was accepted with QA: TalkBack speaks a region-name fragment when the reading cursor crosses between the header, list, and composer regions, which cannot be suppressed.
Summary by CodeRabbit