---
name: accessibility-mobile
description: React Native accessibility patterns for iOS and Android. Use when implementing a11y features.
---
# Accessibility Mobile Skill
This skill covers accessibility (a11y) best practices for React Native apps.
## When to Use
Use this skill when:
- Building accessible components
- Implementing screen reader support
- Adding accessibility labels
- Testing accessibility
## Core Principle
**INCLUSIVE BY DEFAULT** - Accessibility is not optional. Build for all users.
## Basic Accessibility Props
```typescript
import { TouchableOpacity, Text, View } from 'react-native';
// Accessible button
Submit
// Accessible image
// Decorative image (hidden from screen readers)
```
## Accessibility Roles
```typescript
// Common roles
```
## Accessible Forms
```typescript
function AccessibleForm(): React.ReactElement {
const [email, setEmail] = useState('');
const [emailError, setEmailError] = useState('');
return (
{/* Label association */}
Email Address
{emailError && (
{emailError}
)}
Submit
);
}
```
## Accessibility State
```typescript
// Toggle state
setIsChecked(!isChecked)}
>
{isChecked ? '☑' : '☐'} Accept terms
// Expanded state
setIsExpanded(!isExpanded)}
>
Show details
// Selected state
Tab 1
// Busy state
```
## Accessibility Value
```typescript
// Progress bar
// Slider
```
## Live Regions
```typescript
// Announce changes to screen readers
{statusMessage}
// Toast/notification
function Toast({ message, visible }: ToastProps): React.ReactElement | null {
if (!visible) return null;
return (
{message}
);
}
```
## Grouping Elements
```typescript
// Group related elements
iPhone 15 Pro
$999
// Prevent grouping
```
## Focus Management
```typescript
import { useRef } from 'react';
import { AccessibilityInfo, findNodeHandle } from 'react-native';
function FocusExample(): React.ReactElement {
const headerRef = useRef(null);
const focusOnHeader = () => {
const node = findNodeHandle(headerRef.current);
if (node) {
AccessibilityInfo.setAccessibilityFocus(node);
}
};
return (
Welcome
Focus header
);
}
```
## Screen Reader Detection
```typescript
import { useEffect, useState } from 'react';
import { AccessibilityInfo } from 'react-native';
function useScreenReader() {
const [isEnabled, setIsEnabled] = useState(false);
useEffect(() => {
AccessibilityInfo.isScreenReaderEnabled().then(setIsEnabled);
const subscription = AccessibilityInfo.addEventListener(
'screenReaderChanged',
setIsEnabled
);
return () => subscription.remove();
}, []);
return isEnabled;
}
// Usage
function MyComponent(): React.ReactElement {
const isScreenReaderEnabled = useScreenReader();
return (
{isScreenReaderEnabled ? (
Detailed description for screen reader users
) : (
)}
);
}
```
## Reduce Motion
```typescript
import { useEffect, useState } from 'react';
import { AccessibilityInfo } from 'react-native';
function useReduceMotion() {
const [reduceMotion, setReduceMotion] = useState(false);
useEffect(() => {
AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion);
const subscription = AccessibilityInfo.addEventListener(
'reduceMotionChanged',
setReduceMotion
);
return () => subscription.remove();
}, []);
return reduceMotion;
}
// Usage with animations
function AnimatedComponent(): React.ReactElement {
const reduceMotion = useReduceMotion();
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{
scale: reduceMotion
? 1
: withSpring(scale.value),
},
],
}));
return ;
}
```
## Accessible Navigation
```typescript
// Tab bar with proper accessibility
function TabBar({ tabs, activeTab, onTabPress }) {
return (
{tabs.map((tab, index) => (
onTabPress(index)}
>
{tab.label}
))}
);
}
```
## Accessible Lists
```typescript
function AccessibleList({ items }) {
return (
(
{item.title}
)}
accessibilityRole="list"
/>
);
}
```
## Testing Accessibility
```typescript
import { render, screen } from '@testing-library/react-native';
describe('Accessibility', () => {
it('has correct accessibility role', () => {
render();
expect(screen.getByRole('button')).toBeOnTheScreen();
});
it('has accessibility label', () => {
render();
expect(screen.getByLabelText('Add to favorites')).toBeOnTheScreen();
});
it('announces state changes', () => {
render();
expect(screen.getByRole('switch')).toHaveAccessibilityState({
checked: true,
});
});
});
```
## Checklist
- [ ] All interactive elements have `accessibilityRole`
- [ ] All images have `accessibilityLabel` or are hidden
- [ ] Form inputs have labels and error messages
- [ ] Touch targets are at least 44x44 points
- [ ] Color is not the only way to convey information
- [ ] Text has sufficient contrast ratio (4.5:1)
- [ ] Animations respect reduce motion setting
- [ ] Focus order is logical
- [ ] Dynamic content uses live regions
- [ ] Screen reader testing on iOS and Android
## Notes
- Test with VoiceOver (iOS) and TalkBack (Android)
- Use Accessibility Inspector in Xcode
- Enable accessibility testing in development
- Consider users with motor impairments
- Provide alternatives for gestures