accessibilityLabel & accessibilityRole
Semântica correta para componentes customizados. Um guia para configurar nomes , papéis , estados e hints para que VoiceOver e TalkBack entendam suas primitivas de design-system - não apenas chamadas Pressable dispersas pelas features.
Cartão de receita de referência rápida - pronto para copiar e colar.
import type { PressableProps } from "react-native" ;
import { Pressable, Text, StyleSheet } from "react-native" ;
type AppButtonProps = PressableProps & {
label : string ;
hint ?: string ;
variant ?: "primary" | "ghost" ;
};
export function AppButton ({
label ,
hint ,
variant = "primary" ,
accessibilityState ,
... rest
} : AppButtonProps ) {
return (
< Pressable
accessibilityRole = "button"
accessibilityLabel = {label}
accessibilityHint = {hint}
accessibilityState = {accessibilityState}
style = {[styles.base, variant === "primary" ? styles.primary : styles.ghost]}
{ ... rest}
>
< Text style = {styles.text} importantForAccessibility = "no" >
{label}
</ Text >
</ Pressable >
);
}
const styles = StyleSheet. create ({
base: { minHeight: 44 , paddingHorizontal: 16 , borderRadius: 8 , justifyContent: "center" },
primary: { backgroundColor: "#2563eb" },
ghost: { backgroundColor: "transparent" },
text: { color: "#fff" , fontWeight: "600" , textAlign: "center" },
});
Quando usar isso: Sempre que você encapsular Pressable, View ou manipuladores de gestos em UI reutilizável - cards, botões de ícone, chips, steppers e ações de bottom-sheet precisam de um contrato semântico explícito.
Uma linha de Configurações composta com ícone à esquerda, título, subtítulo e chevron - anunciada uma vez, ativada uma vez.
import { Pressable, Text, View, StyleSheet } from "react-native" ;
type SettingsRowProps = {
title : string ;
subtitle ?: string ;
onPress : () => void ;
};
export function SettingsRow ({ title , subtitle , onPress } : SettingsRowProps ) {
const label = subtitle ? `${ title }, ${ subtitle }` : title;
return (
< Pressable
accessibilityRole = "button"
accessibilityLabel = {label}
accessibilityHint = "Abre as configurações"
onPress = {onPress}
style = {styles.row}
>
< View
style = {styles.content}
importantForAccessibility = "no-hide-descendants"
accessibilityElementsHidden
>
< Text style = {styles.title}>{title}</ Text >
{subtitle ? < Text style = {styles.subtitle}>{subtitle}</ Text > : null }
< Text style = {styles.chevron} accessible = { false }>
›
</ Text >
</ View >
</ Pressable >
);
}
const styles = StyleSheet. create ({
row: { paddingVertical: 14 , paddingHorizontal: 16 , minHeight: 48 },
content: { flexDirection: "row" , alignItems: "center" , gap: 8 },
title: { flex: 1 , fontSize: 16 , fontWeight: "600" },
subtitle: { fontSize: 14 , color: "#64748b" },
chevron: { fontSize: 20 , color: "#94a3b8" },
});
O que isso demonstra:
O Pressable pai possui o nome acessível único
Filhos visuais ocultos da árvore - sem "Notificações, Notificações, chevron"
accessibilityHint adiciona propósito sem repetir o título
minHeight: 48 atende às diretrizes de alvo de toque
Componentes de design-system devem aceitar e encaminhar props de acessibilidade sem removê-las.
import type { PressableProps, ViewProps } from "react-native" ;
import { Pressable, View } from "react-native" ;
type CardProps = ViewProps & {
onPress ?: PressableProps [ "onPress" ];
accessibilityLabel : string ;
};
export function Card ({ onPress , accessibilityLabel , children , ... viewProps } : CardProps ) {
if (onPress) {
return (
< Pressable
accessibilityRole = "button"
accessibilityLabel = {accessibilityLabel}
onPress = {onPress}
style = {viewProps.style}
>
{children}
</ Pressable >
);
}
return (
< View
accessibilityRole = "summary"
accessibilityLabel = {accessibilityLabel}
{ ... viewProps}
>
{children}
</ View >
);
}
Use ComponentProps<typeof Pressable> em TypeScript - veja Tipando Componentes e Props
accessibilityLabel padrão obrigatório em wrappers interativos - torne o esquecimento de um rótulo um erro de tipo
Quando o texto do rótulo estiver visível, referencie-o por nativeID em vez de duplicar strings.
import { Text, TextInput, View, StyleSheet } from "react-native" ;
export function Field ({ label , value , onChangeText } : { label : string ; value : string ; onChangeText : ( t : string ) => void }) {
const labelId = `field-${ label . replace ( / \s / g , "-" ). toLowerCase () }` ;
return (
< View style = {styles.field}>
< Text nativeID = {labelId} style = {styles.label}>
{label}
</ Text >
< TextInput
value = {value}
onChangeText = {onChangeText}
accessibilityLabelledBy = {labelId}
accessibilityLabel = {label}
/>
</ View >
);
}
const styles = StyleSheet. create ({
field: { gap: 4 },
label: { fontWeight: "600" },
});
Forneça ambos accessibilityLabelledBy e accessibilityLabel para alvos de OS mais antigos
nativeID deve ser único por tela - prefixe com o ID da tela ou formulário em assistentes
Padrão de UI Papel Fonte do rótulo CTA Principal buttonTexto visível ou accessibilityLabel Texto que navega linkContexto de destino - "Ver detalhes do pedido" Título da seção headerTexto do cabeçalho Linha de alternância com Switch switch no SwitchTítulo da linha; estado no Switch Checkbox customizado checkboxNome do item + accessibilityState.checked Campo de busca search"Buscar produtos" Parágrafo estático text ou nenhumGeralmente automático do Text Ícone da barra de ferramentas buttonaccessibilityLabel obrigatório
< Pressable
accessibilityRole = "button"
accessibilityLabel = "Adicionar aos favoritos"
accessibilityState = {{
disabled: isLoading,
selected: isFavorite,
busy: isLoading,
}}
/>
Chave de estado Anunciado como Usar quando disabled"desabilitado" / indisponível Bloqueio assíncrono ou de validação selected"selecionado" Abas, filtros, controles segmentados checked"marcado" / "desmarcado" Alternâncias customizadas expanded"expandido" / "recolhido" Acordeões busyEm andamento Submissão em andamento
import { Pressable, Text, View, StyleSheet } from "react-native" ;
export function QuantityStepper ({
value ,
onIncrement ,
onDecrement ,
} : {
value : number ;
onIncrement : () => void ;
onDecrement : () => void ;
}) {
return (
< View
accessible
accessibilityRole = "adjustable"
accessibilityLabel = "Quantidade"
accessibilityValue = {{ text: String (value) }}
accessibilityActions = {[
{ name: "increment" , label: "Aumentar quantidade" },
{ name: "decrement" , label: "Diminuir quantidade" },
]}
onAccessibilityAction = {( e ) => {
if (e.nativeEvent.actionName === "increment" ) onIncrement ();
if (e.nativeEvent.actionName === "decrement" ) onDecrement ();
}}
style = {styles.row}
>
< Pressable accessibilityLabel = "Diminuir" onPress = {onDecrement}>
< Text >−</ Text >
</ Pressable >
< Text >{value}</ Text >
< Pressable accessibilityLabel = "Aumentar" onPress = {onIncrement}>
< Text >+</ Text >
</ Pressable >
</ View >
);
}
const styles = StyleSheet. create ({
row: { flexDirection: "row" , alignItems: "center" , gap: 12 },
});
O papel adjustable é adequado para ajustes do rotor do iOS - combine com accessibilityActions para steppers customizados
Botões físicos permanecem para usuários videntes - não remova controles visíveis
< Pressable
accessibilityRole = "button"
accessibilityLabel = { `Mensagens, ${ unread } não lidas` }
accessibilityHint = "Abre a caixa de entrada"
/>
Inclua contagens dinâmicas no rótulo quando elas transmitirem status
Não chame announceForAccessibility a cada pesquisa - reserve para transições significativas
Definir um container accessible mescla os filhos em um único anúncio no iOS.
Deixar os filhos expostos dá navegação granular - escolha de acordo com a intenção do UX.
importantForAccessibility no Android substitui a visibilidade dos filhos quando os pais estão ocultos.
Hints descrevem o que acontece a seguir - "Abre a folha de pagamento", não "Botão"
Omita hints quando o rótulo for autoexplicativo - a verbosidade retarda usuários experientes de leitores de tela
Localize hints com a mesma prioridade que os rótulos
Aplique rótulos em CI com eslint-plugin-react-native-a11y - então proíba Pressable bruto em features:
features/** → deve importar { AppButton, IconButton } de @/shared/ui
Veja Regras Customizadas de ESLint para RN e Construindo uma Biblioteca de Componentes Interna .
Botões com gesture-handler - wrappers GestureDetector não são acessíveis até que um Pressable interno ou uma view accessible exponha semânticas.
Papéis duplicados - accessibilityRole="button" tanto no pai quanto no filho causa anúncios aninhados de "botão".
Fontes de ícones - pontos de código de glifos não são falados; sempre forneça um rótulo de texto.
Strings traduzidas em rótulos - interpole valores, não assuma a ordem das frases do inglês.
accessibilityElementsHidden no Android - combine com importantForAccessibility para ocultação confiável.
Componentes customizados devem definir accessibilityLabel por padrão?
Sim para primitivas interativas - exija a prop label ou um padrão sensato a partir do texto dos filhos.
Contêineres de layout estático geralmente não precisam de rótulo, a menos que resumam um card complexo.
link ou button para navegação?
Use link quando a ação abre conteúdo relacionado ou outra tela em um contexto de navegação.
Use button para modais, submissões e confirmações destrutivas - corresponde ao modelo mental do usuário em leitores de tela de celular.
Como testar semânticas sem um dispositivo?
React Native Testing Library: getByRole("button", { name: "Salvar" }) - veja Noções Básicas de Teste Mobile .
Testes em dispositivo ainda são necessários para hints, agrupamento e granularidade do TalkBack.
Versões do Stack: Esta página foi escrita para React 19.2.3 , React Native 0.86.0 , e Expo SDK 57 (expo ~57.0.4).