Skip to content

Improve poll vote row TalkBack accessibility#6461

Merged
andremion merged 3 commits into
developfrom
fix/compose-poll-message-item-a11y
May 22, 2026
Merged

Improve poll vote row TalkBack accessibility#6461
andremion merged 3 commits into
developfrom
fix/compose-poll-message-item-a11y

Conversation

@andremion
Copy link
Copy Markdown
Contributor

@andremion andremion commented May 21, 2026

AND-1180

Goal

A11y audit of the poll message item (the poll bubble in the message list) surfaced three gaps:

  1. Option rows weren't individually focusable for TalkBack — the whole poll bubble was a single focus stop, so users had to listen to one long merged announcement (title + every option + every button) instead of navigating option by option.
  2. LinearProgressIndicator announced redundant "X percent" on every option, duplicating the vote-count Text. Two options at 0 votes both announced "0 percent" — flagged repeatedly by Accessibility Scanner.
  3. Vote count Text read as a bare number ("5") rather than "5 votes", which is ambiguous in the merged row announcement.

This PR addresses all three.

Implementation

Row-level focusable + merged content.

  • PollOptionItem Row gets Modifier.semantics(mergeDescendants = true) {} in all cases. The row becomes a single TalkBack focus stop with the option text, vote count, and voter avatars merged into one announcement.
  • For open polls (!poll.closed), the row also gets Modifier.toggleable(value = checked, role = toggleRole, onValueChange = onToggle). The role is Role.RadioButton when poll.maxVotesAllowed == 1 (mutual-exclusion semantics), Role.Checkbox otherwise (multi-vote or unlimited). The previous onCheckedChange lambda is extracted into onToggle and shared between the row's toggleable and the inner RadioCheck's onCheckedChange, so sighted taps on either the row or the radio circle fire the same logic.
  • RadioCheck inside the row gets Modifier.semantics { hideFromAccessibility() } since the row now owns the toggle action. The visual RadioCheck still responds to taps for sighted users.
  • For closed polls, the row stays non-toggleable (no vote action) but is still a focus stop via mergeDescendants = true, so TalkBack users can navigate option by option to read the final tallies.

Hide redundant progress percentage.

  • LinearProgressIndicator modifier gains .clearAndSetSemantics {}. Compose Material3 announces the indicator as "X percent" by default; the vote-count Text on the same row already conveys the count more precisely.

Announce vote count with units.

  • The vote-count Text gets a contentDescription from the existing stream_compose_poll_vote_counts plural (e.g. "5 votes" / "1 vote" / "0 votes"). Visible text stays compact ("5") so the visual layout is unchanged.

🎨 UI Changes

No UI changes. All changes are accessibility-tree only.

Testing

Enable TalkBack on a physical device. Run the Compose sample, create a poll in a 1:1 or group channel.

  1. Open poll. Open the channel and swipe through the poll bubble with TalkBack:
    • The bubble announces the poll title and subtitle (e.g. "Vote ends 5 Mar").
    • Each option row is its own focus stop. Announces as "<option text>. N votes. Not selected. Double tap to activate." (or "Selected" when you've voted for it).
    • "View Results" and "View Comments" buttons are separate focus stops.
  2. Double-tap an option row to cast or remove a vote. TalkBack should announce the new selected/not-selected state.
  3. Single-vote vs multi-vote polls. Create a poll with maxVotesAllowed = 1 and one with multi-vote (or unlimited). The TalkBack role should be "radio button" for the single-vote poll, "checkbox" for the multi-vote one.
  4. Closed poll. End the poll. Re-test: each option row is still a separate focus stop (read-only), announces option text + vote count + "Not selected" — no double-tap-to-activate hint.

Summary by CodeRabbit

  • Bug Fixes

    • Improved poll message accessibility with better semantic descriptions and support for screen readers.
    • Enhanced poll option interaction handling for more reliable voting behavior.
  • Accessibility

    • Added support for different poll types (single and multiple choice selection modes).
    • Optimized vote count descriptions for assistive technologies.

Review Change Stack

@andremion andremion requested a review from a team as a code owner May 21, 2026 14:23
@andremion andremion added the pr:improvement Improvement label May 21, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 21, 2026

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled, or the PR is bot-authored.
  • An issue is linked (Linear ticket or GitHub issue), or the PR is bot-authored.

🎉 Great job! This PR is ready for review.

@andremion
Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@andremion andremion enabled auto-merge (squash) May 21, 2026 14:25
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 725bf149-e07f-4025-80a0-9a455b3f3661

📥 Commits

Reviewing files that changed from the base of the PR and between ad0378d and 299090e.

📒 Files selected for processing (1)
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/PollMessageContent.kt

Walkthrough

This PR refactors poll option interactions in PollMessageContent.kt to improve accessibility and user interaction handling. The changes introduce toggleable semantics for poll options, unify vote cast/remove logic, add pluralized vote count descriptions for screen readers, and clarify progress indicator semantics.

Changes

Poll Accessibility and Interaction Improvements

Layer / File(s) Summary
Accessibility and interaction imports
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/PollMessageContent.kt
Added imports for toggleable, semantics helpers (Role, setSemantics, clearAndSetSemantics), and applyIf conditional modifier to support the interaction and accessibility refactoring.
PollOptionItem interaction refactoring
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/PollMessageContent.kt
Refactored PollOptionItem to compute toggleRole (radio vs checkbox) from poll.maxVotesAllowed, unified vote cast and remove logic into a single onToggle handler, and updated the option row modifier to conditionally apply toggleable when the poll is open while merging semantics.
Accessibility semantics for vote display and progress
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/PollMessageContent.kt
Enhanced vote count Text with a pluralized contentDescription for accessibility, and modified LinearProgressIndicator to clear semantics to improve what accessibility services expose.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • gpunto

Poem

🐰 A poll so fine, now votes align,
With toggles true and semantics divine,
Each option sings its role so clear,
Accessibility draws near! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change—improving TalkBack accessibility for poll vote rows—and directly aligns with the primary objective of the pull request.
Description check ✅ Passed The description is comprehensive and well-structured, covering Goal, Implementation, UI Changes, and Testing sections with detailed explanations and step-by-step instructions. However, the Contributor and Reviewer checklists are not checked, and no before/after screenshots, videos, patch code, or GIF are provided.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/compose-poll-message-item-a11y

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 21, 2026

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.82 MB 5.82 MB 0.00 MB 🟢
stream-chat-android-ui-components 11.02 MB 11.02 MB 0.00 MB 🟢
stream-chat-android-compose 12.41 MB 12.42 MB 0.00 MB 🟢

@sonarqubecloud
Copy link
Copy Markdown

@andremion andremion disabled auto-merge May 22, 2026 09:38
@andremion andremion enabled auto-merge (squash) May 22, 2026 09:39
@andremion andremion merged commit 1ab8c4b into develop May 22, 2026
16 checks passed
@andremion andremion deleted the fix/compose-poll-message-item-a11y branch May 22, 2026 10:18
@stream-public-bot stream-public-bot added the released Included in a release label May 22, 2026
@stream-public-bot
Copy link
Copy Markdown
Contributor

🚀 Available in v7.2.0

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

Labels

pr:improvement Improvement released Included in a release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants