ネストされたフォーカス可能な要素
フォーカス可能な要素は、クリック可能な親要素内にネストされるべきではありません。なぜなら、その役割がTalkBackやVoice Accessといった支援技術に正しく伝達されないためです。
このルールについて
ネストされたフォーカス可能な要素ルールは、インタラクティブでないすべてのアクセシビリティフォーカス可能なコントロールをチェックします。このようなコントロールがクリック可能な親に含まれている場合、それ自体はインタラクティブであるが役割を伝達しません。したがって、この要素はアクセシビリティ違反としてフラグが付けられます。
ユーザーへの影響
支援技術が役割を伝達しない場合、TalkBackのような支援技術に依存する視覚障害者や低視力ユーザーは、アクションを起こせることを知ることができません。例えば、TalkBackは内部の要素にフォーカスし、そのテキスト「トーストを起動する」とだけを伝え、ボタンであることやダブルタップしてアクティベートする必要があることを伝えません。
確認
- TalkBack / スクリーンリーダーをオンにします。
- ビューおよびそのすべての子孫をフォーカスします。
- 次のいずれかが発生します。
- アクセシブルでない: TalkBackはテキストを読みますが、役割やインタラクションの能力を伝えません。
- アクセシブル: TalkBackはすべてのテキストを読みます。また、役割を伝えたり、要素とのインタラクション方法を示唆したりします。
修正方法
フォーカス可能で非インタラクティブなコントロールをクリック可能な親要素内にネストしないようにしてください。ネストされたコントロールに、フォーカス可能にするプロパティが設定されていないことを確認します。このようにして、インタラクティブな親要素がフォーカスを取得し、その役割を伝えることができます。以下で具体的な例を参照してください。
誤ったアクセシビリティの発見を防ぐために、タップしても何もしないコンテナビューがクリック可能なままにされていないことを確認します。私たちのツールでは、クリック可能なビューに関連したプログラムされたアクションがあるかどうかを判断できないため、潜在的にアクセスできない動作を指摘するために、それがあると仮定しなければなりません。
Frame Layouts、Card Views、またはDrawersなどの周囲のコンテナビューを調査して、アクションを関連付けるべきでないビューがクリック可能に設定されていないことを確認してください。
XML
失敗例
この例では、 MaterialCardView はクリック可能ですが、子要素 LinearLayout が代わりにフォーカスを取得します。支援技術のユーザーが LinearLayoutとインタラクションしたときにカードのクリックアクションは起動されますが、それがクリック可能であることを伝えません。要素の役割や役割説明がアクションが存在するか、またはインタラクションが何をするのかを示していないため、支援技術ではアクセスできない体験になります。
<com.google.android.material.card.MaterialCardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="true"
android:layout_margin="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/launch_a_toast"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>修正ガイダンス: LinearLayout:focusable をfalseにする
がtrueに設定されているフォーカス可能プロパティを持たないことを確認します。デフォルトの状態ではfalseです。 LinearLayout
<com.google.android.material.card.MaterialCardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/launch_a_toast"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>Compose
失敗例
この例では、 Card はクリック可能ですが、子要素 Row が代わりにフォーカスされます。支援技術のユーザーが Rowとインタラクションしたときにカードのクリックアクションは起動されますが、それがクリック可能であることを伝えません。要素の役割や役割説明がアクションが存在するか、またはインタラクションが何をするのかを示していないため、支援技術ではアクセスできない体験になります。
Card(
modifier = Modifier
.clickable {
launchToast(context)
}
) {
Row(
modifier = Modifier
.focusable()
.padding(10.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterHorizontally)
) {
Text(stringResource(R.string.launch_a_toast))
}
}修正ガイダンス: 子孫要素にフォーカスする能力を除去する
ここでは、 Row 要素からフォーカスする能力が除去されました。 Card は、 Textから情報を得るため、話すテキストを持つようになります。 Card は支援技術でフォーカスを取得でき、テキストコンテンツを伝えるようになります。カードは「ダブルタップしてアクティブ化する」のような役割説明を通じてユーザーにクリック可能であることを伝え、ユーザーにとってアクセス可能な体験をもたらします。
Card(
modifier = Modifier
.clickable {
launchToast(context)
}
) {
Row(
modifier = Modifier
.padding(10.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterHorizontally)
) {
Text(stringResource(R.string.launch_a_toast))
}
}.NET MAUI
失敗例
この例では、レイアウトがクリック可能ですが、アクセシビリティツリーに隠されています。このビューを削除することで、このビューがボタンとして機能しているというコンテキストを削除しました。これにより、支援技術のユーザーはこのビューとどのようにインタラクションできるかを認識できません。
<Grid
HorizontalOptions="FillAndExpand"
RowDefinitions="Auto,Auto"
RowSpacing="10"
VerticalOptions="FillAndExpand"
AutomationProperties.IsInAccessibleTree="false">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Function}"/>
</Grid.GestureRecognizers>
<Label
Grid.Row="1"
FontAttributes="Bold"
FontSize="16"
HorizontalOptions="CenterAndExpand"
VerticalOptions="FillAndExpand">
<Label.FormattedText>
<FormattedString>
<Span Text="Text with a clickable parent"/>
</FormattedString>
</Label.FormattedText>
</Label>
</Grid>修正ガイダンス: SemanticProperties.Hint プロパティを使用
アクセシビリティのエラーは2通りの方法で対応できます。最も直接的な方法は、 AutomationProperties.IsInAccessibleTree="false" 要素をグリッドビューから削除することです。それがアプリの他の機能と矛盾する場合、ユーザーにクリックアクションを提供するもう一つの方法は、 SemanticProperties.Hint プロパティに「ボタン」や「スイッチ」など、ユーザーがどのような種類のビューとインタラクションしているかに関する有用なコンテキストを設定することです。これにより、支援技術が「ダブルタップしてアクティベート」のヒントを読み上げに追加し、ユーザーにビューとインタラクションするためのフィードバックを提供します。
<Grid
HorizontalOptions="FillAndExpand"
RowDefinitions="Auto,Auto"
RowSpacing="10"
VerticalOptions="FillAndExpand">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Function}"/>
</Grid.GestureRecognizers>
<Label
Grid.Row="1"
FontAttributes="Bold"
FontSize="16"
HorizontalOptions="CenterAndExpand"
VerticalOptions="FillAndExpand">
<Label.FormattedText>
<FormattedString>
<Span Text="Text with a clickable parent"/>
</FormattedString>
</Label.FormattedText>
</Label>
</Grid>React Native
失敗例
この例では、 TouchableOpacity はクリック可能ですが、ネストされた View は、 accessible={true}、親がアクセシビリティにおいてフォーカスを得るのを防ぎます。 View は、クリック可能な TouchableOpacityの代わりにフォーカスを受けますが、インタラクティブであることを告知することはありません。支援技術のユーザーは製品情報を聞くことができますが、詳細を見るためにタップできることを知ることはできません。
<TouchableOpacity
accessibilityRole="button"
style={styles.productCard}
onPress={() => navigateTo("product")}
>
<View accessible={true}>
<Text>Product Price</Text>
<Text>$10.00</Text>
</View>
</TouchableOpacity>修正ガイダンス: アクセシビリティプロパティを追加する
この修正済みの例では、 TouchableOpacity に適切なアクセシビリティプロパティが直接設定されています。これらのプロパティは、 accessible={true} と accessibilityRole="button" 、それがインタラクティブな要素として支援技術によって告知されることを保証し、ユーザーが内容とそれが行動可能であることを理解できるようにします。
<TouchableOpacity
accessible={true}
accessibilityRole="button"
style={styles.productCard}
onPress={() => navigateTo("product")}
>
<Text>Product Price</Text>
<Text>$10.00</Text>
</TouchableOpacity>Flutter
失敗例
以下の例では、 GestureDetector は、 Card のクリック可能な親ラッパーとして機能しますが、自らフォーカスを得ることはできません。 Card は子ノードのコンテナとして機能するため、フォーカスを得て子ノードからすべてのコンテンツを告知します。支援技術のユーザーが Cardにフォーカスすると、"ダブルタップしてアクティブにする" とは告知されませんが、 Card 自体はクリック可能ではないためです。しかし親の GestureDetector はクリック可能です。
GestureDetector(
onTap: () => ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Card clicked"),
behavior: SnackBarBehavior.floating,
)
),
child: const Card(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Text(
"Card with clickable parent"
),
),
),
);修正ガイダンス: GestureDetector を使用しない
ここでは、 InkWell は、子としてのクリック可能な要素として機能し、 Card と同じサイズを占めます。 Cardその結果、 InkWell は、支援技術のユーザーにテキストコンテンツをすべて提供するコンテナとして機能します。その結果としての1つのクリック可能な要素が、インタラクティブであることを示すために"ダブルタップしてアクティブにする"と告知します。
Card(
child: InkWell(
onTap: () => ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Card clicked"),
behavior: SnackBarBehavior.floating,
)
),
child: const Padding(
padding: EdgeInsets.all(16.0),
child: Text(
"Text with clickable parent"
),
),
),
)