Implement, audit, and fix accessibility (a11y) in React Native and Expo mobile apps.
60
70%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
Optimize this skill with Tessl
npx tessl skill review --optimize ./skills/accessibility-react-native/SKILL.mdThis skill implements and audits accessibility (a11y) in React Native and Expo apps, following WCAG 2.1 AA standards as adapted for mobile (iOS VoiceOver + Android TalkBack).
Use this skill for:
accessibilityLabel, accessibilityHint, accessibilityRole, accessibilityState, accessibilityValueAccessibilityInfo API usage (reduce motion, screen reader detection)accessibilityLabel or meaningful text child.accessibilityRole tells screen readers what it is.accessibilityState for disabled, selected, checked, expanded, busy.importantForAccessibility (Android) and accessibilityViewIsModal to manage focus scope.AccessibilityInfo.isReduceMotionEnabled().| Prop | Platform | Purpose |
|---|---|---|
accessible | Both | Groups children into one focusable node |
accessibilityLabel | Both | Name read aloud by screen reader |
accessibilityHint | Both | Explains the result of an action |
accessibilityRole | Both | Semantic role (button, link, header, image, etc.) |
accessibilityState | Both | {disabled, selected, checked, expanded, busy} |
accessibilityValue | Both | {min, max, now, text} for sliders/progress |
accessibilityLiveRegion | Android | 'none', 'polite', 'assertive' for dynamic content |
aria-live | Both (RN ≥0.73) | ARIA-aligned live region |
accessibilityViewIsModal | iOS | Traps focus inside modal |
importantForAccessibility | Android | 'yes', 'no', 'no-hide-descendants' |
onAccessibilityAction | Both | Custom actions for screen readers |
accessibilityActions | Both | Declares custom action names |
For detailed prop usage with examples, see references/props.md.
Run the audit script to surface issues before writing any code:
node scripts/audit.js <path-to-src>This checks for:
accessibilityLabel or accessibilityRoleaccessibilityLabel or accessibilityIgnoresInvertColorsTextInput missing accessibilityLabel (separate from placeholder)Then do a manual walkthrough with:
Xcode > Open Developer Tool > Accessibility Inspector)See references/components.md for canonical patterns for:
node scripts/contrast-check.jsreferences/contrast.md for replacement palette strategies.Install and configure:
npx expo install @testing-library/react-native
npm install --save-dev eslint-plugin-jsx-a11yAdd to .eslintrc:
{
"plugins": ["jsx-a11y"],
"extends": ["plugin:jsx-a11y/recommended"],
"rules": {
"jsx-a11y/interactive-supports-focus": "error"
}
}Write accessibility-focused unit tests — see references/testing.md.
import { AccessibilityInfo } from 'react-native';
// Reduce Motion
const [reduceMotion, setReduceMotion] = useState(false);
useEffect(() => {
AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion);
const sub = AccessibilityInfo.addEventListener('reduceMotionChanged', setReduceMotion);
return () => sub.remove();
}, []);
// Dynamic Type (iOS) — use allowFontScaling (default true, do NOT set false)
<Text allowFontScaling={true} maxFontSizeMultiplier={2}>...</Text>Use this checklist per screen. Check each item; document failures with component name + line number.
accessibilityLabel or are marked decorative (accessible={false})allowFontScaling is NOT disabled on any Text or TextInputaccessibilityRoleaccessibilityLabel (or unambiguous text child)hitSlop to extend without changing layout)accessibilityHint used where action outcome isn't obviousaccessibilityLiveRegion / aria-live on dynamic content (toasts, errors, countdowns)accessibilityViewIsModal={true} on iOS)Views (reduces noise)accessibilityLabel on a View wrapping a TouchableOpacity (set it on the Touchable itself)placeholder as the only label for TextInput (VoiceOver reads placeholder as label, but it disappears on input)accessible={false} on a container that has interactive children (hides them from screen reader)AccessibilityInfo.announceForAccessibility on every render (debounce or trigger only on meaningful changes)importantForAccessibility="no-hide-descendants" on a visible interactive groupStack.Screen options={{ title }} for clarity.accessibilityLabel prop directly; use role="presentation" for decorative images.subtitleStyles and tracks props. Announce play/pause state via accessibilityState={{ busy: isLoading }}.| File | Contents |
|---|---|
references/props.md | Full prop API reference with before/after code examples |
references/components.md | Canonical accessible patterns for 15+ component types |
references/contrast.md | WCAG contrast ratios, formulas, palette fix strategies |
references/testing.md | Unit test patterns, e2e with Detox, CI integration |
| Script | Purpose |
|---|---|
scripts/audit.js | Static AST scan for missing a11y props |
scripts/contrast-check.js | Extract and validate color contrast from JS/TS files |
c0b2e4b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.