Botão só com ícone: como dar o nome que o leitor de tela anuncia
Deixe a barra de ícones limpa sem criar botões mudos: o desenho vira decoração, o botão recebe um nome que descreve a ação, e estado e contraste não dependem só da cor.
O painel de pedidos ganhou uma barra elegante: lápis, duas folhas, impressora e lixeira. Quem enxerga entende em meio segundo. Quem usa leitor de tela ouve "botão, botão, botão, botão" e precisa apertar cada um para descobrir qual apaga o pedido. A biblioteca de ícones entregou o desenho; o nome do botão ela não coloca por você.
A resposta curta: o ícone fica escondido da tecnologia assistiva com aria-hidden="true", e o botão recebe um nome acessível que descreve a ação, não o desenho. Esse nome vem de texto visível, de um texto escondido só visualmente ou de aria-label. Depois, confira no inspetor de acessibilidade do navegador se cada botão tem nome.
Por que o botão fica mudo
Bibliotecas como Bootstrap Icons e Font Awesome desenham o ícone de dois jeitos: com SVG ou com fonte de ícones, em que um elemento vazio recebe uma classe e o CSS injeta um caractere especial. Nos dois casos, não existe texto real dentro do botão. O navegador calcula o "nome acessível" de um botão a partir do conteúdo de texto e de alguns atributos; se não há nada, o nome sai vazio.
Com fonte de ícones, a situação pode ser pior que o silêncio. Alguns leitores tentam ler o caractere injetado, que fica numa área de código reservada da fonte, e anunciam um símbolo sem sentido. Por isso o ícone precisa ser escondido explicitamente, e o nome precisa vir de outro lugar.
Este guia foca em fonte de ícones e no padrão de botões de barra de ferramentas. Se você usa SVG inline, os detalhes de title e role no próprio SVG estão em ícone SVG de biblioteca precisa de nome. Para escolher quais ícones usar, veja como escolher ícones sem poluir a interface.
Três jeitos certos de nomear o botão
O exemplo é a barra de um pedido no painel da loja. O código usa as classes do Bootstrap 5 e do Bootstrap Icons.
<!-- Errado: o leitor anuncia só "botão" -->
<button type="button"><i class="bi bi-trash"></i></button>
<!-- 1. Texto visível: o ícone é só decoração -->
<button type="button" class="btn btn-outline-danger">
<i class="bi bi-trash" aria-hidden="true"></i> Excluir
</button>
<!-- 2. Texto escondido só visualmente -->
<button type="button" class="btn btn-outline-danger">
<i class="bi bi-trash" aria-hidden="true"></i>
<span class="visually-hidden">Excluir pedido 1042</span>
</button>
<!-- 3. aria-label no próprio botão -->
<button type="button" class="btn btn-outline-danger" aria-label="Excluir pedido 1042">
<i class="bi bi-trash" aria-hidden="true"></i>
</button>
Quando cabe, o primeiro é o melhor: todo mundo lê o mesmo texto, inclusive quem enxerga mas não reconhece o ícone. O segundo é útil porque o texto escondido é traduzido pelas ferramentas de tradução automática da página com mais frequência que um atributo. O terceiro é o mais curto e funciona bem em componentes.
A classe visually-hidden é a do Bootstrap 5; na versão 4, o equivalente se chamava sr-only. Se você não usa Bootstrap, a técnica é a mesma: o texto continua no DOM, mas é recortado para ocupar um pixel invisível. Não use display: none para isso, porque ele esconde o texto também do leitor de tela.
Passo a passo para revisar uma barra existente
- Liste todos os botões e links que mostram só ícone. Inclua os de fechar modal, abrir menu e os de paginação.
- Escreva, para cada um, o verbo da ação: "Editar", "Duplicar", "Imprimir", "Excluir". Se a mesma barra se repete em cada linha da tabela, inclua o objeto: "Excluir pedido 1042".
- Adicione
aria-hidden="true"em todo elemento de ícone. - Escolha um dos três padrões de nome e aplique em todos os botões da barra, para manter a consistência.
- Abra as ferramentas do navegador, selecione o botão e veja o painel de acessibilidade. No Chrome e no Edge, a aba Accessibility mostra o nome calculado; o Firefox tem um inspetor de acessibilidade próprio.
- Teste com um leitor real ao menos uma vez: NVDA no Windows, VoiceOver no macOS e no iPhone, TalkBack no Android.
Descreva a ação, não o desenho
"Lixeira" diz o que você vê; "Excluir pedido" diz o que acontece. A pessoa não quer saber que há uma impressora desenhada, quer saber se o botão imprime a etiqueta ou a nota. Nomes como "ícone de lápis" ou "imagem de lixeira" são o erro mais comum depois do nome vazio.
Um caso que pega muita gente: o botão tem texto visível curto, como "Enviar", e alguém coloca aria-label="Submeter formulário de cadastro". Quem usa controle por voz fala "clicar em Enviar" e nada acontece, porque o nome acessível não contém a palavra que está na tela. A regra prática: quando existe texto visível, o nome acessível começa com esse texto, ou simplesmente é ele.
Estado, tamanho e contraste também contam
A estrela de favorito que fica amarela quando ativa comunica estado só pela cor. Para quem não distingue a cor, ou não vê a tela, nada mudou. Em botões de alternância, use aria-pressed e troque também a forma do ícone, de contorno para preenchido:
<button type="button" aria-pressed="false" aria-label="Favoritar pedido 1042" id="fav-1042">
<i class="bi bi-star" aria-hidden="true"></i>
</button>
<script>
const botao = document.getElementById('fav-1042');
botao.addEventListener('click', function () {
const ativo = botao.getAttribute('aria-pressed') === 'true';
botao.setAttribute('aria-pressed', String(!ativo));
const icone = botao.querySelector('i');
icone.classList.toggle('bi-star', ativo);
icone.classList.toggle('bi-star-fill', !ativo);
});
</script>
Repare que o nome não muda: o leitor anuncia "Favoritar pedido 1042, botão de alternância, pressionado". Trocar o rótulo para "Desfavoritar" e ao mesmo tempo usar aria-pressed cria uma frase contraditória.
Ícone é componente gráfico, então segue regras próprias. As diretrizes WCAG pedem contraste de pelo menos 3:1 para elementos gráficos necessários para entender a interface, e a versão 2.2 trouxe um critério de tamanho mínimo de alvo de toque. Um ícone cinza-claro de 14 pixels sobre fundo branco costuma falhar nos dois. Aumente a área clicável com espaçamento interno, mesmo que o desenho continue pequeno.
O atributo title não resolve sozinho
Muita gente coloca title="Excluir" e considera resolvido, porque aparece uma dica ao passar o mouse. O problema: a dica não aparece no toque nem no foco por teclado na maioria dos navegadores, e o suporte em leitores de tela varia. Use title, se quiser, como complemento visual, nunca como único nome. Para dicas visíveis acessíveis, um componente de tooltip que também abre no foco é o caminho.
Erros comuns
- Botão só com o elemento do ícone: o leitor anuncia "botão" sem nome e a pessoa precisa adivinhar, às vezes apagando o que não queria.
- Esquecer
aria-hiddenno ícone de fonte: alguns leitores anunciam o caractere interno da fonte, um ruído que se repete em cada linha da tabela. - Nome que descreve o desenho: "lápis" e "lixeira" obrigam a pessoa a traduzir metáfora em ação, e algumas metáforas não são universais.
- Esconder o texto com
display: none: o texto some também para a tecnologia assistiva e o botão volta a ser mudo. - Estado indicado só pela cor: quem não distingue a cor não sabe se o filtro ou o favorito está ativo.
aria-labeldiferente do texto visível: quem usa comando de voz fala o que vê na tela e o botão não responde.
Checklist
- Todo botão e link só com ícone tem nome no painel de acessibilidade do navegador.
- Todo elemento de ícone tem
aria-hidden="true". - Os nomes são verbos de ação e, em listas repetidas, citam o item.
- Botões com texto visível têm nome acessível que contém esse texto.
- Botões de alternância usam
aria-pressede mudam a forma do ícone, não só a cor. - O ícone tem contraste suficiente com o fundo e a área de toque não é minúscula.
- Nenhum botão depende só de
titlepara ter nome.
Abra a tela mais usada do seu sistema, selecione cada botão de ícone no inspetor e leia o nome calculado. Quantos aparecem vazios, e qual deles a pessoa clicaria por engano se tivesse de adivinhar?
Referência: https://icons.getbootstrap.com/