Élément imbriqué pouvant recevoir le focus

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 – Critique

Les éléments pouvant recevoir le focus ne doivent pas être imbriqués dans des éléments parents cliquables, car ils n'annonceront pas correctement leur rôle aux technologies d'assistance telles que TalkBack et Voice Access.

À propos de cette règle

La règle des éléments focalisables imbriqués vérifie tous les contrôles d'accessibilité focalisables qui ne sont pas interactifs. Si un tel contrôle est contenu dans un élément parent cliquable, il est intrinsèquement interactif, mais n'annoncera pas de rôle. Par conséquent, cet élément sera signalé comme une violation d'accessibilité.

Impact sur les utilisateurs

Lorsque les technologies d'assistance n'annoncent pas un rôle, les utilisateurs aveugles ou malvoyants qui dépendent de technologies d'assistance telles que TalkBack ne sauront pas qu'ils peuvent déclencher une action. Par exemple, TalkBack se concentre sur un élément interne et annonce son texte « Lancer un Toast », et rien de plus. Il n'annonce pas qu'il s'agit d'un bouton ni que l'utilisateur doit effectuer un double appui sur l'élément pour l'activer.

Confirmation

  1. Veuillez activer TalkBack / le lecteur d'écran.
  2. Placez le focus sur la vue et sur chacun de ses éléments descendants.
  3. L’une des situations suivantes se produira :
    • Inaccessible : TalkBack lit le texte mais n'annonce pas de rôle ni de possibilité d'interaction.
    • Accessible : TalkBack lira tout le texte. Il annoncera également un rôle et/ou indiquera comment interagir avec l'élément.

Comment corriger

Ne placez pas un contrôle pouvant recevoir le focus et non interactif dans un élément parent cliquable. Assurez-vous que le contrôle imbriqué ne possède aucune propriété définie qui le rendrait focalisable. Ainsi, l'élément parent interactif peut obtenir le focus et annoncer son rôle. Voyez des exemples spécifiques ci-dessous.

tip

Pour éviter un faux positif concernant l'accessibilité, assurez-vous que les vues conteneurs qui ne font rien lorsqu'on appuie dessus ne soient pas cliquables. Nos outils ne peuvent pas déterminer si une vue cliquable possède une action programmée associée ; nous devons donc supposer que c’est le cas afin de signaler un comportement potentiellement inaccessible.

Examinez les vues conteneurs environnantes — telles que les Frame Layouts, Card Views ou Drawers — afin de vous assurer que toute vue qui ne doit pas avoir d'action associée n'est pas paramétrée comme cliquable.

XML

Exemple d'échec

Dans cet exemple, le MaterialCardView est cliquable, mais l'enfant LinearLayout reçoit le focus à la place. Lorsque les utilisateurs de technologies d'assistance interagissent avec le LinearLayout, celui-ci active l'action de clic de la carte mais n'annonce pas qu'elle est cliquable. Il en résulte une expérience inaccessible avec les technologies d'assistance, car le rôle de l'élément ou la description du rôle n'indiquent pas qu'une action est présente ni ce que l'interaction accomplira.

Lancer une carte Toast avec le contour de mise au point à l'intérieur
<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>

Conseil de remédiation : Définissez LinearLayout:focusable sur False

Assurez-vous que LinearLayout ne possède pas la propriété focusable définie sur true. À l’état par défaut, la propriété est à « false ».

Afficher une carte Toast avec le contour de focus à l'extérieur
<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

Exemple d'échec

Dans cet exemple, le Card est cliquable mais l’enfant Row reçoit le focus à la place. Lorsque les utilisateurs de technologies d'assistance interagissent avec le Row, cela déclenche l'action de clic de la carte sans annoncer qu'elle est cliquable. Il en résulte une expérience inaccessible avec les technologies d'assistance, car le rôle de l'élément ou la description du rôle n'indiquent pas qu'une action est présente ni ce que l'interaction accomplira.

Lancez une carte Toast avec le contour de focus à l’extérieur
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))
    }
}

Guide de remédiation : Supprimer la possibilité pour l’élément descendant de recevoir le focus

Ici, la capacité de recevoir le focus a été retirée de l’élément Row. Le Card disposera désormais de texte à prononcer, parce qu'il obtiendra des informations depuis le Text. Le Card pourra désormais recevoir le focus avec une technologie d’assistance et annoncera le contenu textuel. La carte indiquera à l'utilisateur qu'elle est cliquable via une description de rôle telle que « Double-tapez pour activer », ce qui offrira une expérience accessible à l'utilisateur.

Lancez une carte Toast avec le contour de mise au point à l'extérieur
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

Exemple d'échec

Dans cet exemple, la mise en page est cliquable, mais elle a été masquée dans l’arborescence d’accessibilité. En retirant cette vue, nous avons supprimé le contexte selon lequel cette vue agit comme un bouton, et les utilisateurs de technologies d’assistance ne sauront pas comment interagir avec cette vue.

Carte affichant « Texte avec un ancêtre cliquable accessible », entourée de rouge pour indiquer qu'elle ne figure pas dans l'arbre d'accessibilité
<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>

Conseils de remédiation : Utiliser la propriété SemanticProperties.Hint

Les erreurs d'accessibilité peuvent être traitées de deux manières. La méthode la plus simple consiste à supprimer l'élément AutomationProperties.IsInAccessibleTree="false" de la vue grille. Si cela entre en conflit avec d'autres fonctionnalités de l'application, une autre façon de fournir aux utilisateurs les actions d'activation consiste à définir la propriété SemanticProperties.Hint avec un contexte pertinent sur le type de vue avec lequel l'utilisateur interagit, comme « Bouton » ou « Interrupteur ». Cela permettra aux technologies d’assistance d’ajouter correctement l’indication « Appuyez deux fois pour activer » à leur lecture et de donner aux utilisateurs le retour nécessaire pour interagir avec la vue.

Carte affichant « Texte avec un ancêtre cliquable accessible », encadrée en vert pour indiquer qu'elle se trouve dans l'arbre d'accessibilité
<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

Exemple d'échec

Dans cet exemple, TouchableOpacity est cliquable mais le View imbriqué possède accessible={true}, ce qui empêche le parent d'obtenir le focus d'accessibilité. Le View recevra le focus à la place du TouchableOpacity cliquable, mais n'annoncera pas qu'il est interactif. Les utilisateurs de technologies d’assistance recevront les informations sur le produit, mais ne sauront pas qu’ils peuvent appuyer pour afficher les détails.

Carte affichant « Prix du produit : 10,00 $ » avec un contour de mise au point interne
<TouchableOpacity
    accessibilityRole="button"
    style={styles.productCard}
    onPress={() => navigateTo("product")}
>
    <View accessible={true}>
        <Text>Product Price</Text>
        <Text>$10.00</Text>
    </View>
</TouchableOpacity>

Conseils de correction : Ajouter des propriétés d’accessibilité

Dans cet exemple corrigé, TouchableOpacity a des propriétés d'accessibilité définies directement sur l'élément. Les propriétés accessible={true} et accessibilityRole="button" garantissent que les technologies d'assistance l'annoncent comme un élément interactif, afin que les utilisateurs comprennent le contenu et sachent qu'ils peuvent agir dessus.

Carte portant l'inscription « Product Price 10,00 $ » avec un contour de mise au point à l'extérieur
<TouchableOpacity
    accessible={true}
    accessibilityRole="button"
    style={styles.productCard}
    onPress={() => navigateTo("product")}
>
    <Text>Product Price</Text>
    <Text>$10.00</Text>
</TouchableOpacity>

Flutter

Exemple d'échec

Dans l'exemple ci-dessous, le GestureDetector sert de conteneur parent cliquable pour le Card, mais il ne peut pas recevoir le focus lui-même. Le Card sert de conteneur pour ses enfants, il recevra donc le focus et annoncera tout le contenu des nœuds enfants. Lorsque les utilisateurs de technologies d'assistance se concentrent sur le Card, il n'annoncera pas « Double-tapez pour activer » à l'utilisateur puisque le Card lui-même n'est pas cliquable, même si le parent GestureDetector l'est.

Carte indiquant « Texte avec élément parent cliquable », avec contour de mise au point à l'intérieur
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"
      ),
    ),
  ),
);

Directive de remédiation : Évitez d’utiliser un wrapper GestureDetector

Ici, le InkWell agit comme un élément enfant cliquable du Card et occupera la même taille que le Card. Le InkWell fait désormais office de conteneur qui présente tout le contenu textuel aux utilisateurs de technologies d'assistance. Le résultat est un élément cliquable annonçant « Appuyez deux fois pour activer » pour indiquer qu’il est interactif.

Carte affichant « Texte avec élément parent cliquable », avec contour de mise au point à l’extérieur
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"
        ),
      ),
    ),
  )

Ressources

Deque University Course Pages (Subscription Required)

Autres ressources