Reputation: 13917
As per my current understanding, VoiceOver announces things from left to right, top to bottom, as it assumes that is how a visual user sees content.
(I am yet to understand how voiceover figures out the order exactly, it certainly does not depend on the rendered layout tree. would appreciate any pointers )
In cases where you have an atomic piece of content across multiple rows which you want to be announced together by voiceover, you can group it by adding accessible={true}
on the container view. The problem is, this makes any internal interactive elements unavailable for accessibility focus, and only allows a11y focus on this container view.
Consider the below design:
Two cards are shown side by side, each card has a title, subtitle and cta. On screen reader focus, we would ideally want the contents of the first card to be read first, and its CTA announced, and then move to the 2nd card and do the same. What ends up happening with voiceover by default is that it announces title1, title2, subtitle1, subtitle2, cta1, cta2 which doesn't make sense for the user.
To fix this, one way is to make the container view accesible={true}, which has a side effect that the CTA is now unavailable to the accessibility focus, and hence a screenreader user can't reach or click the CTA.
Are there any preferred patterns to solve this?
EDIT: added example code for each card:
<View style={...} key={...}>
<Text>{title}</Text>
<Text>{subtitle}</Text>
<Button flat secondary onPress={() => onPress(item)}>
{cta}
</Button>
</View>
Upvotes: 1
Views: 1737
Reputation: 46
I found a soltion and present it in react-native-a11y-order
.
import { A11y } from 'react-native-a11y-order';
// ...
export default function App() {
return (
<ScrollView
style={styles.slider}
contentContainerStyle={styles.sliderContainer}
horizontal
>
<A11y.Group style={styles.slide}>
<View>
<Text>Title: 1</Text>
</View>
<View>
<Text>Desctiption: 1</Text>
</View>
</A11y.Group>
<A11y.Group style={styles.slide}>
<View>
<Text>Title: 2</Text>
</View>
<View>
<Text>Desctiption: 2</Text>
</View>
</A11y.Group>
</ScrollView>
);
Upvotes: 0
Reputation: 46
There are a couple ways to solve this,
The best and simplest one is using ScrollView
component.
<ScrollView horizontal>
<ScrollView scrollEnabled={false}>
FirstCard
</ScrollView>
<ScrollView scrollEnabled={false}>
SecondCard
</ScrollView>
</ScrollView>
ScrollView
can help with defining direction without installing additional libs.
If you need specifc implementation, or ScrollView
doesn't work for you can try these libs:
Upvotes: 1
Reputation: 96
Whilst I can't offer an answer I've tested myself just yet as I'm investigating adding this into our project, I thought I'd drop this in for others as this Gist seems like the answer you need:
https://gist.github.com/louy/6b66c45ae47bb4e3bac5a104dd0649ff
It involves a creating a bridge to implement the commands in @slugolicious' answer from the JS/TS side.
Upvotes: 0
Reputation: 17563
Check out the UIAccessibilityContainer
and in particular the accessibilityElements
property.
Those are both Objective-C objects and not react, but hopefully react allows you access to them.
Upvotes: 0