Ícones acessíveis
A maioria dos ícones fica ao lado de um rótulo e não deve atrapalhar as tecnologias assistivas; alguns carregam significado por conta própria e precisam de um nome. O glyphfox acerta o primeiro caso por padrão e resolve o segundo com uma única prop.
Decorativos por padrão
Todo ícone do glyphfox é renderizado com aria-hidden="true", a menos que você dê um nome a ele. Os leitores de tela o ignoram, e é exatamente isso que você quer para um ícone ao lado de texto visível: o texto já diz o que o controle faz, e ouvir "imagem" a mais no anúncio é só ruído.
<!-- <DownloadIcon /> next to a label --> <button type="button"> <svg class="glyphfox glyphfox-download" aria-hidden="true" ...>...</svg> Download </button>
O mesmo padrão vale em todo lugar: nos componentes React, Vue, Svelte e Angular, no elemento glyph-fox, no runtime para HTML puro e nas strings do toSvg. Você só muda isso quando o ícone é o único portador do seu significado.
Ícones que carregam significado
Passe title e o ícone vira uma imagem com nome. O renderizador adiciona três coisas: um elemento <title> como primeiro filho, role="img" e aria-label com o mesmo texto, para que o nome seja exposto de forma consistente em todos os navegadores e leitores de tela.
import { CircleCheckIcon, TriangleAlertIcon } from 'glyphfox/react'; export function SyncStatus({ ok }) { return ok ? <CircleCheckIcon title="Synced" color="#15803d" /> : <TriangleAlertIcon title="Sync failed" color="#b91c1c" />; }
<svg class="glyphfox glyphfox-circle-check" role="img" aria-label="Synced" ...> <title>Synced</title> ... </svg>
Em React, Vue e Svelte, você também pode passar aria-label ou aria-labelledby direto para o componente. Eles vão para o svg, e o ícone deixa de ser decorativo automaticamente, sem o elemento <title> e o tooltip que ele mostra no hover. decorative substitui a escolha automática nos dois sentidos: decorative esconde um ícone mesmo com título, e decorative={false} o expõe.
Escreva o nome pelo que o ícone significa naquele lugar, e não pelo que ele retrata. Um ícone trash é "Excluir rascunho", não "Lixeira"; um circle check em uma lista de pagamentos é "Pago".
Botões e links só com ícone
Quando um ícone é o único conteúdo de um botão ou de um link, dê nome ao controle, não ao ícone. É o controle que recebe o foco e que o leitor de tela anuncia; o ícone dentro dele continua decorativo.
import { XIcon, SettingsIcon, SearchIcon } from 'glyphfox/react'; export function Toolbar({ onClose }) { return ( <> <button type="button" aria-label="Close dialog" onClick={onClose}> <XIcon /> </button> <a href="/settings" aria-label="Settings"> <SettingsIcon /> </a> <button type="submit"> <SearchIcon /> <span className="visually-hidden">Search the docs</span> </button> </> ); }
O texto visualmente oculto, o terceiro padrão, é traduzido pelas ferramentas de tradução do navegador, que muitas vezes ignoram atributos; prefira esse padrão em sites multilíngues. Uma classe visually-hidden típica recorta o elemento para um pixel sem removê-lo da árvore de acessibilidade.
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
Botões de alternância precisam de estado, não de um nome novo. Um botão de menu que se transforma de menu em x mantém o rótulo e define aria-expanded; um botão de play que se transforma em pause muda o rótulo ou usa aria-pressed. O morph é a metade visual dessa mudança; o guia de ícones animados mostra as duas metades juntas.
Status, cor e contraste
A cor sozinha não pode carregar significado (WCAG 1.4.1). Use formas diferentes para estados diferentes, como circle-check, circle-alert e circle-x, e combine com texto sempre que possível. Os selos também ajudam: file+check e file+alert diferem na forma, não só na cor.
Ícones de que as pessoas precisam para entender a página ou encontrar um controle devem atingir um contraste de 3 para 1 com o fundo (WCAG 1.4.11, contraste não textual). Meça a cor do traço, porque é ele que desenha a forma; a camada duotone translúcida é uma decoração por cima dele. Ícones decorativos ao lado de texto não entram na regra, mas o contraste baixo ainda os torna mais difíceis de identificar numa leitura rápida.
Traços finos parecem mais claros do que a cor sugere, porque o antialiasing os mistura com o fundo. O glyphfox compensa isso abaixo de 24 pixels, deixando o traço até 30 por cento mais grosso; em 14 ou 16 pixels com uma cor suave, um weight de 2,25 ou 2,5 mantém os ícones legíveis. absoluteWeight desliga a compensação, então use com cuidado em tamanhos pequenos.
Movimento
Todo estado vivo e todo morph respeitam prefers-reduced-motion: reduce: os presets não tocam, os morphs em CSS trocam sem transição e os morphs de componentes pulam direto para a forma final. O estado final continua visível, então nenhuma informação se perde.
Conteúdo que se move sozinho por mais de cinco segundos precisa de uma forma de ser pausado (WCAG 2.2.2). Vincule animações em loop como spin ao processo que elas mostram e pare-as quando ele terminar, e evite loops feitos para chamar atenção, como um sino que balança para sempre.
Layouts da direita para a esquerda
Em interfaces em árabe, hebraico ou persa, os ícones que apontam no sentido da leitura precisam ser espelhados: setas de voltar e avançar, chevrons em breadcrumbs, responder e desfazer. mirrorRtl adiciona uma classe que espelha o ícone sempre que ele está dentro de dir="rtl", então a mesma marcação funciona nas duas direções.
import { ChevronLeftIcon, ChevronRightIcon } from 'glyphfox/react'; export function Pager({ onPrev, onNext }) { return ( <nav aria-label="Pagination"> <button type="button" onClick={onPrev}><ChevronLeftIcon mirrorRtl /> Previous</button> <button type="button" onClick={onNext}>Next <ChevronRightIcon mirrorRtl /></button> </nav> ); }
Não espelhe ícones cuja direção não acompanha o texto: controles de mídia como play e fast-forward continuam apontando para a direita, um clock continua girando no sentido horário, um check fica como está, e rotate-cw ainda significa sentido horário.
Máscaras, sprites, o elemento e Angular
- Classes do Tailwind CSS renderizam um elemento vazio com uma máscara, que não tem semântica nenhuma. Adicione
aria-hidden="true"e dê ao controle um rótulo visualmente oculto. No modo de alto contraste do Windows, um ícone de máscara pode sumir; o guia de Tailwind CSS traz a solução. - Sprites SVG deixam o
svgexterno por sua conta: adicionearia-hidden="true"para um ícone decorativo, ourole="img"earia-labelpara um que carrega significado. O guia de sprites tem exemplos. - O elemento
glyph-foxe ogf-icondo Angular renderizam osvgdentro do elemento host. Umaria-labelno host transforma osvginterno em imagem, mas não dá nome a ele, e um rótulo em um elemento genérico não é anunciado de forma confiável. Use o atributo ou inputtitle, que dá nome aosvginterno.
<!-- names the inner svg: role img, aria-label and a title --> <glyph-fox name="circle-alert" title="Payment failed"></glyph-fox> <!-- Angular --> <gf-icon name="circle-alert" title="Payment failed"></gf-icon>
Como testar
- Abra a árvore de acessibilidade nas devtools do navegador e selecione o controle: o nome dele deve ser o rótulo que você escreveu, e os ícones decorativos não devem aparecer de jeito nenhum.
- Navegue com um leitor de tela: VoiceOver no macOS e no iOS, NVDA no Windows, TalkBack no Android. Fique atento a nomes duplicados e a qualquer coisa anunciada apenas como "imagem".
- Emule
prefers-reduced-motioneforced-colorsnas ferramentas de renderização das devtools do Chromium e confira se os ícones continuam visíveis e parados. - Verifique o contraste dos ícones com significado usando um seletor de cores sobre o fundo real, incluindo os estados de hover e foco.
Props que importam aqui
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
color | string | currentColor | Cor principal. |
weight | number | 2 | Traço de 1 a 3 na grade de 24, com compensação óptica abaixo de 24 px. |
absoluteWeight | boolean | false | Mantém o traço com weight pixels em qualquer tamanho. |
mirrorRtl | boolean | false | Espelha em layouts da direita para a esquerda. |
title | string | nenhum | Nome acessível: title, role img e aria-label. |
decorative | boolean | true sem title | Esconde o ícone das tecnologias assistivas. |
Armadilhas comuns
- Dar nome ao controle e ao ícone
- Um
titleem um ícone dentro de um botão que já tem rótulo faz os leitores de tela anunciarem os dois, e o<title>também aparece como tooltip no hover. Dê nome a uma coisa só: o controle. - Uma imagem sem nome
decorative={false}semtitlee semaria-labelexpõe uma imagem sem nome, que os leitores de tela anunciam como "imagem" ou pelo nome do arquivo. Dê um nome a todo ícone exposto.- Estado que só o ícone mostra
- Um morph de
copyparachecké invisível para um leitor de tela. Anuncie o resultado em texto: mude o rótulo do botão ou coloque "Copiado" em uma live region comrole="status". - Tooltips gerados pelo title
- O tooltip do navegador gerado pelo
<title>só aparece quando o mouse passa por cima, nunca no foco nem no toque. Não coloque ali informações que não estejam disponíveis de outra forma. - Rótulos no elemento host
- Em
glyph-foxegf-icon, usetitleem vez dearia-label, como descrito acima.
Perguntas frequentes
Todo ícone deve ter um título?
Não. A maioria dos ícones fica ao lado de texto ou dentro de um controle com rótulo, e um título ali só acrescenta ruído. Dê um título aos ícones que aparecem sozinhos e carregam informação, como um ícone de status em uma célula de tabela.
O aria-hidden é suficiente para ícones decorativos?
Sim. aria-hidden="true", que o glyphfox adiciona por padrão, remove o ícone da árvore de acessibilidade. Ícones SVG não recebem foco, então não há mais nada para esconder.