From 23809a9f40ff18013e51f6069756ad8419a68f64 Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj Date: Wed, 6 May 2026 15:16:29 +0200 Subject: [PATCH 01/17] fix: make sure buttons are properly accessible --- .claude/skills/accessibility/SKILL.md | 14 +++--- ai-docs/accessibility.md | 8 ++++ package/src/a11y/hooks/useA11yLabel.ts | 11 +++-- .../components/AttachmentTypePickerButton.tsx | 9 ++++ .../__tests__/ImageGalleryFooter.test.tsx | 12 +++-- .../__tests__/ImageGalleryHeader.test.tsx | 5 ++- .../components/ImageGalleryFooter.tsx | 4 +- .../components/ImageGalleryHeader.tsx | 2 +- .../components/ImageGalleryVideoControl.tsx | 2 +- .../AudioRecorder/AudioRecorder.tsx | 3 ++ .../components/InputButtons/AttachButton.tsx | 1 + .../components/OutputButtons/EditButton.tsx | 1 + .../components/OutputButtons/SendButton.tsx | 1 + .../MessageList/ScrollToBottomButton.tsx | 12 ++--- .../UnreadMessagesNotification.tsx | 1 + .../MessageMenu/MessageReactionPicker.tsx | 1 + .../components/MessageMenu/ReactionButton.tsx | 16 +++---- .../__tests__/MessageReactionPicker.test.tsx | 28 ++++++------ .../__tests__/MessageUserReactions.test.tsx | 37 ++++++++-------- .../__tests__/ReactionButton.test.tsx | 23 +++++++--- .../Poll/components/CreatePollHeader.tsx | 2 + .../Poll/components/MultipleVotesSettings.tsx | 2 + .../Poll/components/PollModalHeader.tsx | 1 + package/src/components/ui/Avatar/Avatar.tsx | 3 +- package/src/components/ui/Button/Button.tsx | 19 ++++++++ .../ui/Button/__tests__/Button.test.tsx | 44 +++++++++++++++++++ package/src/i18n/en.json | 25 ++++++++++- package/src/i18n/es.json | 25 ++++++++++- package/src/i18n/fr.json | 25 ++++++++++- package/src/i18n/he.json | 25 ++++++++++- package/src/i18n/hi.json | 25 ++++++++++- package/src/i18n/it.json | 25 ++++++++++- package/src/i18n/ja.json | 25 ++++++++++- package/src/i18n/ko.json | 25 ++++++++++- package/src/i18n/nl.json | 25 ++++++++++- package/src/i18n/pt-br.json | 25 ++++++++++- package/src/i18n/ru.json | 25 ++++++++++- package/src/i18n/tr.json | 25 ++++++++++- 38 files changed, 480 insertions(+), 82 deletions(-) create mode 100644 package/src/components/ui/Button/__tests__/Button.test.tsx diff --git a/.claude/skills/accessibility/SKILL.md b/.claude/skills/accessibility/SKILL.md index 785ab09e2c..ab32176cce 100644 --- a/.claude/skills/accessibility/SKILL.md +++ b/.claude/skills/accessibility/SKILL.md @@ -10,7 +10,7 @@ Use this skill whenever code changes can affect screen-reader users (VoiceOver o ## Non-negotiable rules 1. **Native semantics first.** Use `Pressable`, `TextInput`, `Switch`, `Image` directly. Use `accessibilityRole` only when native semantics cannot represent the widget (`menu`, `menuitem`, `progressbar`, `radio`, `checkbox`, `article`, `alert`, `tablist`, `tab`). -2. **Never hardcode English** in `accessibilityLabel`/`accessibilityHint`/announcement strings. Use `useA11yLabel('a11y/...', params)` (or `t('a11y/...')` directly when you don't need the disabled-state short-circuit). Add the key to all 12 locale files in `package/src/i18n/`. +2. **Never hardcode English** in `accessibilityLabel`/`accessibilityHint`/announcement strings. For SDK `Button`, pass `accessibilityLabelKey='a11y/...'` (and `accessibilityLabelParams` when needed). For non-Button components, use `useA11yLabel('a11y/...', params)` or `t('a11y/...')` directly when you don't need the disabled-state short-circuit. Add the key to all 12 locale files in `package/src/i18n/`. 3. **Gate behavior on `useAccessibilityContext().enabled`.** A11y is opt-in. New listeners, subscriptions, and announcer mounts must be no-ops when `enabled` is false. New `accessibilityRole`/`accessibilityState` props are fine to render unconditionally — they cost ~zero. 4. **One focusable target per action.** Don't nest `Pressable` inside `Pressable`. Mark inner decorative views with `accessibilityElementsHidden` (iOS) + `importantForAccessibility='no-hide-descendants'` (Android) so the parent carries the label. 5. **Decorative visuals stay hidden from AT.** Icon-only buttons must carry an `accessibilityLabel` on the wrapper, and the SVG icon should be hidden. @@ -31,13 +31,17 @@ Use this skill whenever code changes can affect screen-reader users (VoiceOver o ### 1) Composing accessible names ```tsx -import { useA11yLabel } from 'stream-chat-react-native'; +import { Button, useA11yLabel } from 'stream-chat-react-native'; -const label = useA11yLabel('a11y/Reaction {{emoji}} by {{count}} users', { emoji, count }); +const labelParams = useMemo(() => ({ count, emoji }), [count, emoji]); +const label = useA11yLabel('a11y/Reaction {{emoji}} by {{count}} users', labelParams); + +