乐闻世界logo
搜索文章和话题

How to implement accessibility in Expo apps? What are best practices?

2月21日 16:03

Accessibility (a11y) of Expo apps is an important aspect to ensure all users, including those with visual, hearing, motor, or cognitive impairments, can effectively use the app. Expo and React Native provide rich accessibility APIs and properties.

Accessibility Basics:

  1. accessibilityLabel

Provide element descriptions for screen readers.

typescript
<Image source={{ uri: 'https://example.com/image.jpg' }} accessibilityLabel="User avatar" style={{ width: 50, height: 50 }} /> <Button title="Submit" accessibilityLabel="Submit form" onPress={handleSubmit} />
  1. accessibilityHint

Provide additional information about element behavior.

typescript
<TouchableOpacity accessibilityLabel="View details" accessibilityHint="Tap to view detailed user information" onPress={handlePress} > <Text>View</Text> </TouchableOpacity>
  1. accessibilityRole

Specify the UI role of an element.

typescript
<View accessibilityRole="button" accessibilityLabel="Confirm" onClick={handleConfirm} > <Text>Confirm</Text> </View>

Common Accessibility Roles:

  • button: Button
  • link: Link
  • header: Header
  • text: Text
  • image: Image
  • search: Search box
  • adjustable: Adjustable control
  1. accessibilityState

Describe the current state of an element.

typescript
<CheckBox accessibilityLabel="Agree to terms" accessibilityState={{ checked: isChecked, disabled: false, }} value={isChecked} onValueChange={setIsChecked} />

Accessibility States:

  • disabled: Disabled state
  • selected: Selected state
  • checked: Checked state
  • busy: Busy state
  • expanded: Expanded state
  1. accessibilityValue

Describe the value of an element.

typescript
<Slider accessibilityLabel="Volume" accessibilityValue={{ min: 0, max: 100, now: volume }} value={volume} onValueChange={setVolume} /> <ProgressBar accessibilityLabel="Download progress" accessibilityValue={{ min: 0, max: 100, now: progress }} progress={progress / 100} />

Accessibility Actions:

  1. accessibilityActions

Define accessibility actions supported by an element.

typescript
<View accessibilityLabel="Playback controls" accessibilityActions={[ { name: 'increment', label: 'Increase volume' }, { name: 'decrement', label: 'Decrease volume' }, { name: 'magicTap', label: 'Double tap to play/pause' }, ]} onAccessibilityAction={(event) => { switch (event.nativeEvent.actionName) { case 'increment': setVolume((v) => Math.min(v + 10, 100)); break; case 'decrement': setVolume((v) => Math.max(v - 10, 0)); break; case 'magicTap': togglePlayPause(); break; } }} > <Text>Volume: {volume}</Text> </View>
  1. onAccessibilityEscape

Define escape action.

typescript
<Modal visible={isVisible} onAccessibilityEscape={() => setIsVisible(false)} accessibilityViewIsModal={true} > <View> <Text>Modal content</Text> <Button title="Close" onPress={() => setIsVisible(false)} /> </View> </Modal>

Accessibility Properties:

  1. accessible

Mark an element as accessible.

typescript
<View accessible={true}> <Text>This view can be accessed by screen readers</Text> </View>
  1. accessibilityElementsHidden

Hide accessibility of child elements.

typescript
<View accessible={true} accessibilityLabel="Container" accessibilityElementsHidden={isHidden} > <Text>Child element 1</Text> <Text>Child element 2</Text> </View>
  1. accessibilityIgnoresInvertColors

Ignore color inversion settings.

typescript
<Image source={{ uri: 'https://example.com/chart.jpg' }} accessibilityIgnoresInvertColors={true} style={{ width: 200, height: 200 }} />

Focus Management:

  1. focusable

Make an element focusable.

typescript
<TextInput focusable={true} accessibilityLabel="Username input" placeholder="Enter username" />
  1. accessibilityLiveRegion

Mark dynamic content regions.

typescript
<Text accessibilityLiveRegion="polite" accessibilityLabel="Status message" > {statusMessage} </Text>

Accessibility Events:

typescript
function useAccessibilityFocus() { const [isFocused, setIsFocused] = useState(false); const handleFocus = () => { setIsFocused(true); console.log('Element focused'); }; const handleBlur = () => { setIsFocused(false); console.log('Element blurred'); }; return { isFocused, handleFocus, handleBlur }; }

Semantic Components:

  1. Use Semantic HTML Tags (Web)
typescript
// Use semantic tags on web platform if (Platform.OS === 'web') { return ( <nav accessibilityRole="navigation"> <ul> <li><a href="/home">Home</a></li> <li><a href="/about">About</a></li> </ul> </nav> ); }
  1. Use Correct Accessibility Roles
typescript
// Use button role for buttons <TouchableOpacity accessibilityRole="button" accessibilityLabel="Submit" onPress={handleSubmit} > <Text>Submit</Text> </TouchableOpacity> // Use link role for links <TouchableOpacity accessibilityRole="link" accessibilityLabel="View details" onPress={handlePress} > <Text>View details</Text> </TouchableOpacity>

Best Practices:

  1. Provide Clear Labels
typescript
// Good practice <Button title="Submit form" accessibilityLabel="Submit user registration form" onPress={handleSubmit} /> // Avoid duplication <Button title="Submit" accessibilityLabel="Submit" // Duplicates title onPress={handleSubmit} />
  1. Use Meaningful Hints
typescript
<TouchableOpacity accessibilityLabel="Delete item" accessibilityHint="This action cannot be undone, proceed with caution" onPress={handleDelete} > <Text>Delete</Text> </TouchableOpacity>
  1. Support Keyboard Navigation
typescript
function KeyboardNavigation() { const [focusedIndex, setFocusedIndex] = useState(0); const handleKeyDown = (event) => { if (event.key === 'ArrowDown') { setFocusedIndex((i) => Math.min(i + 1, items.length - 1)); } else if (event.key === 'ArrowUp') { setFocusedIndex((i) => Math.max(i - 1, 0)); } else if (event.key === 'Enter') { items[focusedIndex].onPress(); } }; return ( <View onKeyDown={handleKeyDown}> {items.map((item, index) => ( <TouchableOpacity key={index} accessibilityLabel={item.label} focusable={true} style={[ styles.item, focusedIndex === index && styles.focused, ]} onPress={item.onPress} > <Text>{item.label}</Text> </TouchableOpacity> ))} </View> ); }
  1. Support Screen Readers
typescript
function ScreenReaderSupport() { const isScreenReaderEnabled = useAccessibilityInfo(); return ( <View> {isScreenReaderEnabled ? ( <Text>Screen reader enabled</Text> ) : ( <Text>Screen reader not enabled</Text> )} </View> ); }
  1. Test Accessibility
typescript
import { AccessibilityInfo } from 'react-native'; async function testAccessibility() { // Check if screen reader is enabled const isScreenReaderEnabled = await AccessibilityInfo.isScreenReaderEnabled(); console.log('Screen reader enabled:', isScreenReaderEnabled); // Check reduce motion setting const isReduceMotionEnabled = await AccessibilityInfo.isReduceMotionEnabled(); console.log('Reduce motion enabled:', isReduceMotionEnabled); // Listen for accessibility changes AccessibilityInfo.addEventListener( 'screenReaderChanged', (isEnabled) => { console.log('Screen reader changed:', isEnabled); } ); }

Accessibility Tools:

  1. AccessibilityInfo API
typescript
import { AccessibilityInfo } from 'react-native'; // Get accessibility info const isScreenReaderEnabled = await AccessibilityInfo.isScreenReaderEnabled(); const isReduceMotionEnabled = await AccessibilityInfo.isReduceMotionEnabled(); // Listen for changes AccessibilityInfo.addEventListener('screenReaderChanged', (isEnabled) => { console.log('Screen reader:', isEnabled); }); AccessibilityInfo.addEventListener('reduceMotionChanged', (isEnabled) => { console.log('Reduce motion:', isEnabled); });
  1. Accessibility Testing Tools
  • iOS: VoiceOver
  • Android: TalkBack
  • Web: Screen readers (NVDA, JAWS, etc.)

Common Accessibility Issues:

  1. Missing Accessibility Labels

    • Add accessibilityLabel to all interactive elements
    • Provide descriptive labels for images
  2. Poor Focus Management

    • Ensure keyboard navigation order is logical
    • Provide clear focus indicators
  3. Insufficient Color Contrast

    • Ensure text and background have sufficient contrast
    • Support high contrast mode
  4. Dynamic Content Not Announced

    • Use accessibilityLiveRegion to mark dynamic content
    • Promptly notify screen readers of content changes

By implementing these accessibility practices, you can ensure Expo apps are friendly and usable for all users.

标签:Expo