Skip to content

fix: a11y follow up fixes#3634

Merged
isekovanic merged 14 commits into
developfrom
fix/a11y-follow-up-fixes
Jun 9, 2026
Merged

fix: a11y follow up fixes#3634
isekovanic merged 14 commits into
developfrom
fix/a11y-follow-up-fixes

Conversation

@isekovanic

Copy link
Copy Markdown
Contributor

🎯 Goal

Follow up a11y pass on top of #3632. Covers the message row, the attachment thumbnail gallery, the full screen image/video gallery overlay, the reactions UI, the message cell annotations and date separators, the channel preview date, and the poll option row. VoiceOver and TalkBack now read each surface with proper labels, units and focus behavior and the gallery traps focus while open.

🛠 Implementation details

  • Message row sender label - the bubble's Pressable now announces "Message from {sender}" / "Message from you". Messages with interactive children (poll, quoted message, attachments, shared location) opt out of being a single focus stop so VO/TalkBack can drill into the children; an iOS-only absolute-fill
    View carries the sender label so the speaker is still announced when focus lands inside the inner content.
  • hasInteractiveAccessibilityContent override - new field on AccessibilityConfig that receives the SDK's baseline boolean and lets integrators extend the "is this message interactive?" decision without losing the defaults. Resolved upstream in useCreateMessageContext and exposed as a stable boolean on
    MessageContext.
  • Android compose gate on text bodies - MessageTextContainer and the Poll header now set accessible accessibilityRole='text' so TalkBack auto-composes the descendant <Text> content. Conditional on hasInteractiveAccessibilityContent for the message text to avoid double-announce on plain messages.
  • Gallery thumbnails - the thumbnail Pressable now carries an a11y/Gallery Image or a11y/Gallery Video label (added across all 13 locales), explicit role='button', and the existing a11y/Double tap to open hint. Announces as "Gallery video, button. Double tap to open."
  • Full-screen gallery modal trap - accessibilityViewIsModal on the ImageGallery root and the MessageOverlayHostLayer host on iOS. Android gets a new OverlayA11yShield sibling wrapper that flips importantForAccessibility='no-hide-descendants' when either the gallery or the message context menu is active.
  • Full-screen gallery adjustable cycling - the gallery backdrop is now the focus target with role='adjustable', value={ text: 'N of M' }, increment/decrement actions, and an accessibilityLabel of "Image Gallery". Swipe up/down cycles through assets.
  • Gallery focus return on dismiss - the image gallery state store gained a requesterNode field. Gallery.tsx captures the thumbnail's node handle via findNodeHandle and passes it through openImageGallery; on unmount the gallery restores focus via AccessibilityInfo.setAccessibilityFocus inside an rAF.
  • Reaction announcements - ReactionListItem gains role='button' (trips the Android compose gate so TalkBack reads the emoji's CLDR name instead of the reaction type string) and state.selected. ReactionButton drops a broken accessibilityLabelKey that was literally untranslated across every locale.
  • Thread reply indicator - the MessageReplies Pressable now has role='button', an accessibilityLabel reading the reply count ("1 Reply" / "N Replies"), and an accessibilityHint of "Double tap to view thread" (new i18n key across all 13 locales). Participant avatars in the indicator are hidden from the
    a11y tree so they no longer announce one-by-one.
  • Decorative dot separators - the middle dots in SentToChannelHeader ("Replied to a thread · View") and MessageReminderHeader ("Reminder set · 5 minutes left") are now marked decorative so focus moves directly from the label to the action.
  • Date announcements (iOS) - VoiceOver was reading numeric dates literally ("zero four slash zero eight..."). Added a getDateStringForA11y helper that substitutes LL for the calendar sameElse slot, preserving locale relative words like "Today" / "Yesterday". Wired into InlineDateSeparator (announces
    "April 8, 2026") and ChannelPreviewStatus (announces "10:30 AM" for same-day messages via a sameDay: 'LT' override). TalkBack honors the override too, no platform branching needed.
  • Poll option vote count - the bare count Text on each poll option row now carries an accessibilityLabel using the existing {{count}} votes plural key. Announces "0 votes" / "1 vote" / "N votes" instead of just the bare number.

New shared components and exports

  • OverlayA11yShield (package/src/components/Accessibility/) - Android-only focus-trap shield. Subscribes to the overlay context and the message-overlay store to compute isAnyOverlayActive, then wraps {children} in a View that flips importantForAccessibility='no-hide-descendants' and
    accessibilityElementsHidden while an overlay is active. On iOS it's a pure passthrough selected at module load.
  • getDateStringForA11y (package/src/utils/i18n/getDateString.ts) - sibling helper to getDateString. Produces a TTS-friendly calendar string by substituting LL for the sameElse slot in the locale's calendar formats, with an optional calendarFormatOverrides parameter for callers whose visible format
    diverges from the locale defaults.
  • AccessibilityConfig.hasInteractiveAccessibilityContent - new override hook for integrators (see above).
  • MessageContext.hasInteractiveAccessibilityContent - new stable boolean field consumed by MessageContent and MessageTextContainer.
  • ImageGalleryState.requesterNode and the matching openImageGallery({ requesterNode? }) argument.

🎨 UI Changes

iOS
Before After
Android
Before After

🧪 Testing

☑️ Checklist

  • I have signed the Stream CLA (required)
  • PR targets the develop branch
  • Documentation is updated
  • New code is tested in main example apps, including all possible scenarios
    • SampleApp iOS and Android
    • Expo iOS and Android

@Stream-SDK-Bot

Stream-SDK-Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

SDK Size

title develop branch diff status
js_bundle_size 1727 KB 1733 KB +5328 B 🔴

@isekovanic isekovanic merged commit 5a7b45c into develop Jun 9, 2026
9 of 10 checks passed
@isekovanic isekovanic deleted the fix/a11y-follow-up-fixes branch June 9, 2026 22:02
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.

3 participants