ネストされたフォーカス可能な要素

This page is not available in the language you requested. You have been redirected to the English version of the page.
Link to this page copied to clipboard
Not for use with personal data

WCAG 2.0 - 4.1.2 A Impact - Critical

フォーカス可能な要素は、クリック可能な親要素内にネストされるべきではありません。なぜなら、その役割がTalkBackやVoice Accessといった支援技術に正しく伝達されないためです。

このルールについて

ネストされたフォーカス可能な要素ルールは、インタラクティブでないすべてのアクセシビリティフォーカス可能なコントロールをチェックします。このようなコントロールがクリック可能な親に含まれている場合、それ自体はインタラクティブであるが役割を伝達しません。したがって、この要素はアクセシビリティ違反としてフラグが付けられます。

ユーザーへの影響

支援技術が役割を伝達しない場合、TalkBackのような支援技術に依存する視覚障害者や低視力ユーザーは、アクションを起こせることを知ることができません。例えば、TalkBackは内部の要素にフォーカスし、そのテキスト「トーストを起動する」とだけを伝え、ボタンであることやダブルタップしてアクティベートする必要があることを伝えません。

確認

  1. TalkBack / スクリーンリーダーをオンにします。
  2. ビューおよびそのすべての子孫をフォーカスします。
  3. 次のいずれかが発生します。
    • アクセシブルでない: TalkBackはテキストを読みますが、役割やインタラクションの能力を伝えません。
    • アクセシブル: TalkBackはすべてのテキストを読みます。また、役割を伝えたり、要素とのインタラクション方法を示唆したりします。

修正方法

フォーカス可能で非インタラクティブなコントロールをクリック可能な親要素内にネストしないようにしてください。ネストされたコントロールに、フォーカス可能にするプロパティが設定されていないことを確認します。このようにして、インタラクティブな親要素がフォーカスを取得し、その役割を伝えることができます。以下で具体的な例を参照してください。

tip

誤ったアクセシビリティの発見を防ぐために、タップしても何もしないコンテナビューがクリック可能なままにされていないことを確認します。私たちのツールでは、クリック可能なビューに関連したプログラムされたアクションがあるかどうかを判断できないため、潜在的にアクセスできない動作を指摘するために、それがあると仮定しなければなりません。

Frame Layouts、Card Views、またはDrawersなどの周囲のコンテナビューを調査して、アクションを関連付けるべきでないビューがクリック可能に設定されていないことを確認してください。

XML

失敗例

この例では、 MaterialCardView はクリック可能ですが、子要素 LinearLayout が代わりにフォーカスを取得します。支援技術のユーザーが LinearLayoutとインタラクションしたときにカードのクリックアクションは起動されますが、それがクリック可能であることを伝えません。要素の役割や役割説明がアクションが存在するか、またはインタラクションが何をするのかを示していないため、支援技術ではアクセスできない体験になります。

Launch a Toast card with the focus outline inside
<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

Launch a Toast card with the focus outline outside
<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とインタラクションしたときにカードのクリックアクションは起動されますが、それがクリック可能であることを伝えません。要素の役割や役割説明がアクションが存在するか、またはインタラクションが何をするのかを示していないため、支援技術ではアクセスできない体験になります。

Launch a Toast card with the focus outline outside
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 は支援技術でフォーカスを取得でき、テキストコンテンツを伝えるようになります。カードは「ダブルタップしてアクティブ化する」のような役割説明を通じてユーザーにクリック可能であることを伝え、ユーザーにとってアクセス可能な体験をもたらします。

Launch a Toast card with the focus outline outside
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

失敗例

この例では、レイアウトがクリック可能ですが、アクセシビリティツリーに隠されています。このビューを削除することで、このビューがボタンとして機能しているというコンテキストを削除しました。これにより、支援技術のユーザーはこのビューとどのようにインタラクションできるかを認識できません。

Card reading 'Text with accessible clickable ancestor' outlined in red to indicate it is not in the accessibility tree
<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 プロパティに「ボタン」や「スイッチ」など、ユーザーがどのような種類のビューとインタラクションしているかに関する有用なコンテキストを設定することです。これにより、支援技術が「ダブルタップしてアクティベート」のヒントを読み上げに追加し、ユーザーにビューとインタラクションするためのフィードバックを提供します。

Card reading 'Text with accessible clickable ancestor' outlined in green to indicate it is in the accessibility tree
<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の代わりにフォーカスを受けますが、インタラクティブであることを告知することはありません。支援技術のユーザーは製品情報を聞くことができますが、詳細を見るためにタップできることを知ることはできません。

Card reading 'Product Price $10.00' with focus outline on the inside
<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" 、それがインタラクティブな要素として支援技術によって告知されることを保証し、ユーザーが内容とそれが行動可能であることを理解できるようにします。

Card reading 'Product Price $10.00' with focus outline on the outside
<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 はクリック可能です。

Card reading 'Text with clickable parent', with focus outline on the inside
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 reading 'Text with clickable parent', with focus outline on the outside
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"
        ),
      ),
    ),
  )

リソース

Deque大学のコースページ(サブスクリプションが必要)

その他のリソース