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); + +