Ícone SVG em botão: aria-label, aria-hidden e o que o leitor de tela anuncia
Como marcar ícones SVG de biblioteca, como o Bootstrap Icons, para que botões só de ícone tenham nome, ícones decorativos fiquem em silêncio e o desenho continue visível em alto contraste.
O cabeçalho do painel administrativo tem três botões só com ícone: uma pessoa (conta), um sino (notificações) e uma seta saindo de uma caixa (sair). Todos copiados do Bootstrap Icons, nítidos em qualquer tela. Com o leitor de tela ligado, a navegação por Tab anuncia "botão", "botão", "botão". Um usuário cego do time de suporte descobriu qual era "sair" clicando em todos.
A resposta curta: o ícone não traz a sua intenção; o nome do botão é você quem dá. Botão só de ícone recebe aria-label (ou texto visualmente oculto) e o SVG dentro dele ganha aria-hidden="true". Ícone ao lado de texto é decoração e também fica com aria-hidden="true". Ícone que sozinho transmite informação, fora de botão, usa role="img" com um nome. E use fill="currentColor", para o desenho acompanhar a cor do texto, inclusive em modo de alto contraste.
Por que o ícone fica mudo
O leitor de tela não enxerga o desenho. Ele lê o nome acessível que o navegador calcula para cada elemento, a partir do conteúdo de texto, de aria-label, de aria-labelledby e de algumas outras fontes. Um button que contém só um svg com caminhos geométricos não tem texto nenhum. O resultado é "botão" e mais nada.
Dependendo de como o ícone foi copiado, pode ser pior: se o SVG vier com um title genérico em inglês, o leitor anuncia "person" ou "bell". Se for uma fonte de ícones, pode ler um caractere estranho da área de uso privado da fonte. Em nenhum caso sai "Minha conta". O conceito geral, para qualquer tipo de ícone, está em ícone no botão: a figura pode ser a decoração, o nome não pode faltar. Aqui o assunto é a marcação de SVG.
Os quatro casos e a marcação de cada um
1. Botão só de ícone
<button type="button" aria-label="Minha conta">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"
fill="currentColor" viewBox="0 0 16 16" aria-hidden="true">
<path d="..."/>
</svg>
</button>
O aria-label dá o nome ao botão. O aria-hidden="true" no SVG garante que nada de dentro dele seja anunciado em duplicidade. O leitor passa a dizer "Minha conta, botão".
Uma alternativa é texto real escondido visualmente, com a mesma classe utilitária de esconder campos de formulário (o Bootstrap traz uma, visually-hidden):
<button type="button">
<svg ... aria-hidden="true">...</svg>
<span class="visually-hidden">Minha conta</span>
</button>
A vantagem do texto oculto é que ferramentas de tradução automática do navegador costumam traduzi-lo, enquanto o tratamento de aria-label varia. Para um painel só em português, as duas formas funcionam.
2. Ícone ao lado de texto
<button type="submit">
<svg ... aria-hidden="true">...</svg>
Salvar
</button>
O texto "Salvar" já é o nome. Sem o aria-hidden, um title perdido no SVG poderia virar "disquete Salvar". Não repita o texto no leitor.
3. Ícone que informa, fora de botão
Na tabela de pedidos, um ícone verde de "check" sozinho na coluna de status significa "pago". Aqui o ícone é conteúdo:
<svg role="img" aria-label="Pago" ... fill="currentColor">...</svg>
Melhor ainda: escreva "Pago" ao lado. Cor e forma sozinhas não servem a quem não distingue verde de vermelho nem a quem lê a tela impressa.
4. Ícone com estado
O sino mostra um número quando há notificações novas. O número na bolinha vermelha é visual. O nome do botão precisa carregar a mesma informação:
const sino = document.getElementById('botao-notificacoes');
const contador = document.getElementById('contador-notificacoes');
function mostrarNovas(quantidade) {
contador.textContent = quantidade > 0 ? String(quantidade) : '';
sino.setAttribute('aria-label',
quantidade > 0 ? 'Notificações, ' + quantidade + ' novas' : 'Notificações');
}
Com o aria-label no botão, o conteúdo de dentro, inclusive a bolinha, deixa de entrar no nome. Por isso a contagem vai no rótulo. Para um botão de alternar, como "fixar menu", mantenha o rótulo fixo e use aria-pressed="true" ou "false" para o estado.
Sprite, fonte de ícones e SVG inline
O Bootstrap Icons, como outras bibliotecas, pode ser usado de três formas, e a regra de nome vale para todas:
- SVG inline: o mais flexível. Aplique os atributos acima direto no
svg. - Sprite com
use:<svg aria-hidden="true"><use href="/icones/bootstrap-icons.svg#bell"/></svg>. Oaria-hiddenvai nosvgexterno. - Fonte de ícones: um
icom as classes do ícone. Coloquearia-hidden="true"noie o nome no botão. Fontes de ícone podem desaparecer quando a pessoa força a própria fonte no navegador, um motivo a mais para preferir SVG.
Se você encontrar focusable="false" em exemplos antigos, ele existia para impedir que versões antigas do Internet Explorer pusessem o SVG na ordem do Tab. Não atrapalha, mas não resolve nome nenhum.
Cor, contraste e o foco que some
Com fill="currentColor", o ícone usa a cor do texto do botão. No modo de cores forçadas do Windows (alto contraste), o navegador troca as cores do texto pelas do sistema, e o ícone acompanha. Um fill="#6c757d" fixo pode ficar invisível sobre o fundo escolhido pela pessoa.
Botões só de ícone costumam vir com border: 0; background: none, e às vezes alguém tira o contorno de foco junto. Garanta um foco visível:
.botao-icone:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
Para escolher quais ícones usar, sem poluir a interface, veja como escolher ícones. E confira a licença do pacote, como explica pacote de ícone grátis ainda tem licença.
Como conferir o nome sem leitor de tela
- Abra o DevTools do Chrome ou do Edge e selecione o botão no painel Elements.
- Na aba Accessibility, veja o campo "Name" na seção de propriedades computadas.
- Repita para cada botão só de ícone do cabeçalho.
- Depois, faça um teste real com NVDA no Windows ou VoiceOver no Mac, navegando por Tab.
Erros comuns
- Botão só de ícone sem nome: o leitor anuncia "botão" e a pessoa precisa adivinhar a ação.
- Usar o atributo
titlecomo nome: aparece só no passar do mouse, some no toque e no teclado, e não é anunciado de forma consistente. - Esquecer o
aria-hiddenno ícone decorativo: o leitor repete o texto ou lê o nome do desenho em inglês. - Cor fixa no SVG: o ícone some no modo de alto contraste.
- Contador só visual: quem usa leitor de tela não sabe que há notificações novas.
- Tirar o contorno de foco do botão transparente: quem navega por teclado não vê em qual ícone está.
Checklist
- Todo botão só de ícone tem
aria-labelou texto visualmente oculto. - SVGs decorativos e dentro de botões nomeados têm
aria-hidden="true". - Ícone que informa sozinho tem
role="img"e nome, ou texto ao lado. - Os ícones usam
currentColor. - Estados como contagem aparecem também no nome acessível.
- O foco é visível em todos os botões de ícone.
- O campo "Name" do DevTools mostra a ação de cada botão.
Selecione no DevTools o botão de sair do seu sistema e olhe a aba Accessibility: o campo Name diz "Sair" ou está vazio?
Referência: https://icons.getbootstrap.com/?q=user