Elemento Focável Aninhado

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

Elementos focáveis não devem ser aninhados dentro de elementos pais clicáveis, pois eles não anunciarão corretamente seu papel para tecnologias assistivas, como TalkBack e Voice Access.

Sobre esta Regra

A regra do Elemento Focável Aninhado verifica todos os controles focáveis em termos de acessibilidade que não são interativos. Se tal controle estiver contido dentro de um elemento pai clicável, ele se torna inerentemente interativo, mas não anunciará um papel. Portanto, este elemento será sinalizado como uma violação de acessibilidade.

Impacto para os Usuários

Quando tecnologias assistivas não anunciam uma função, usuários cegos ou com baixa visão que dependem de tecnologia assistiva, como o TalkBack, não saberão que podem acionar uma ação. Por exemplo, o TalkBack foca em um elemento interno e anuncia seu texto "Lançar um Toast", e nada mais. Ele não anuncia que é um botão ou que o usuário deve tocar duas vezes no elemento para ativar.

Confirmação

  1. Ative o TalkBack / leitor de tela.
  2. Foque na visualização e em cada um de seus descendentes.
  3. Uma das seguintes coisas acontecerá:
    • Inacessível: o TalkBack lê o texto, mas não anuncia um papel ou habilidade para interagir.
    • Acessível: o TalkBack lerá todo o texto. Ele também anunciará um papel e/ou indicará como interagir com o elemento.

Como Consertar

Não aninhe um controle focável e não interativo dentro de um elemento pai clicável. Assegure-se de que o controle aninhado não tenha propriedades definidas que o tornem focável. Desta forma, o elemento pai interativo pode obter o foco e anunciar sua função. Veja exemplos específicos abaixo.

tip

Para evitar um falso positivo em acessibilidade, certifique-se de que as visualizações de contêiner que não fazem nada quando tocadas não estejam marcadas como clicáveis. Nossos ferramentas não conseguem determinar se uma visualização clicável tem uma ação programada associada, então devemos assumir que sim para sinalizar comportamentos potencialmente inacessíveis.

Examine as visualizações de contêiner ao redor - como Layouts de Moldura, Visualizações de Cartão ou Gavetas - para garantir que qualquer visualização que não deva ter uma ação associada não esteja definida como clicável.

XML

Exemplo com Falha

Neste exemplo, o MaterialCardView é clicável, mas o filho LinearLayout recebe o foco. Quando os usuários de tecnologia assistiva interagem com o LinearLayout, ele ativa a ação de clique do cartão, mas não anuncia que é clicável. Isso resulta em uma experiência que não é acessível com tecnologias assistivas, pois o papel ou a descrição do papel do elemento não indicam que uma ação está presente ou o que a interação fará.

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>

Orientação de Remediação: Faça LinearLayout:focusable Falso

Certifique-se de que LinearLayout não tenha uma propriedade focável definida como verdadeira. Em seu estado padrão, é falso.

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

Exemplo com Falha

Neste exemplo, o Card é clicável, mas o filho Row é focado. Quando os usuários de tecnologia assistiva interagem com o Row, isso ativa a ação de clique do cartão, mas não anuncia que é clicável. Isso resulta em uma experiência que não é acessível com tecnologias assistivas, pois o papel ou a descrição do papel do elemento não indicam que uma ação está presente ou o que a interação fará.

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))
    }
}

Orientação de Remediação: Remova a Capacidade de Foco no Elemento Descendente

Aqui, a capacidade de foco foi removida do Row elemento. O Card agora terá texto para falar, pois obterá informações do Text. O Card agora será capaz de ganhar foco com tecnologia assistiva e anunciará o conteúdo do texto. O cartão informará ao usuário que é clicável através de uma descrição de função como "Toque duas vezes para ativar", resultando em uma experiência acessível para o usuário.

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

Exemplo com Falha

Neste exemplo, o layout é clicável; no entanto, ele foi escondido na árvore de acessibilidade. Ao remover esta visualização, retiramos o contexto de que esta visualização está funcionando como um botão, e usuários de tecnologia assistiva não saberão como podem interagir com esta visualização.

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>

Orientação de Remediação: Use SemanticProperties.Hint Propriedade

Erros de acessibilidade podem ser resolvidos de uma das duas maneiras. A maneira mais direta é remover o AutomationProperties.IsInAccessibleTree="false" elemento da visualização da grade. Se isso conflitar com outras funcionalidades no aplicativo, outra maneira de fornecer aos usuários as ações de clique é definir a SemanticProperties.Hint propriedade com contexto útil sobre o tipo de visualização com que o usuário está interagindo - como "Botão" ou "Interruptor". Isso permitirá que a tecnologia assistiva adicione corretamente a dica "Toque duas vezes para ativar" à sua leitura e forneça aos usuários o feedback necessário para interagir com a visualização.

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

Exemplo com Falha

Neste exemplo, TouchableOpacity é clicável, mas o aninhado View tem accessible={true}, o que impede que o elemento pai obtenha foco de acessibilidade. O View receberá o foco em vez do elemento TouchableOpacityclicável, mas não anunciará que é interativo. Os usuários de tecnologias assistivas ouvirão as informações do produto, mas não saberão que podem tocar para ver os detalhes.

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>

Orientação para Remediação: Adicionar Propriedades de Acessibilidade

Neste exemplo remediado, TouchableOpacity tem as propriedades de acessibilidade adequadas definidas diretamente nele. As propriedades accessible={true} e accessibilityRole="button" garantem que as tecnologias assistivas o anunciem como um elemento interativo, para que os usuários compreendam o conteúdo e saibam que é acionável.

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

Exemplo de Falha

No exemplo abaixo, o GestureDetector atua como um wrapper pai clicável para o Card , mas não consegue obter foco por conta própria. O Card atua como o contêiner para seus filhos, então ele ganhará foco e anunciará todo o conteúdo dos nós filhos. Quando usuários de tecnologia assistiva focam no Card, ele não anunciará "Toque duas vezes para ativar" ao usuário, já que o Card em si não é clicável, embora o elemento pai GestureDetector seja.

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"
      ),
    ),
  ),
);

Orientação para Remediação: Não use um GestureDetector wrapper

Aqui, o InkWell atua como um filho clicável do Card e ocupará a mesma dimensão que o Card. O InkWell agora serve como contêiner que apresenta todo o conteúdo de texto para usuários de tecnologias assistivas. O resultado é um elemento clicável que anuncia "Toque duas vezes para ativar" para indicar que é interativo.

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"
        ),
      ),
    ),
  )

Recursos

Páginas de Curso da Deque University (Assinatura Necessária)

Outros Recursos