Elemento Focável Aninhado
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
- Ative o TalkBack / leitor de tela.
- Foque na visualização e em cada um de seus descendentes.
- 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.
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á.
<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.
<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á.
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.
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.
<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.
<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.
<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.
<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.
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(
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"
),
),
),
)