Falhas de acessibilidade são decisões de design. A prova está aqui. abriu com uma estatística que deveria parar qualquer pessoa construindo interfaces: páginas que usam ARIA têm em média 34% mais erros de acessibilidade do que páginas que não tocam nele. Aquele artigo cobriu o motivo em um parágrafo: ninguém especificou o comportamento, então o atributo foi implementado como um chute. Este artigo cobre o resto: o que o ARIA realmente é, a única regra que evita a maior parte desse dano, o punhado de padrões que um designer de produto realmente vai encontrar, e como documentar comportamento interativo para que um desenvolvedor implemente uma vez, corretamente, em vez de chutar.

01

O que o ARIA realmente é

1.1 Papéis, estados e propriedades

Atributos ARIA se dividem em três categorias, e confundi-las é onde a maior parte do uso errado começa.

Papéis (roles) dizem à tecnologia assistiva o que um elemento é. role="dialog", role="tablist", role="alert". Um papel substitui ou adiciona à semântica padrão de um elemento: uma <div> com role="button" é anunciada como um botão, mesmo sem ter nenhum dos comportamentos reais de um botão.

Estados descrevem uma condição que muda durante o uso. aria-expanded="true", aria-checked="false", aria-disabled="true". Estados existem para mudar (esse é todo o propósito deles), e um estado que nunca é atualizado depois que a interface muda é pior do que nenhum estado, porque diz ao usuário algo falso.

Propriedades descrevem uma relação ou característica que na maior parte permanece fixa. aria-label, aria-labelledby, aria-describedby, aria-controls. Propriedades dizem à tecnologia assistiva a que um elemento está conectado, ou como chamá-lo quando não tem texto visível.

Papéis

role="dialog" role="tablist" role="alert"

O que o elemento é.

Estados

aria-expanded aria-checked aria-disabled

O que muda durante o uso.

Propriedades

aria-label aria-describedby aria-controls

A que está conectado.

Três categorias. A maior parte da confusão sobre ARIA é uma confusão entre essas três.

1.2 A primeira regra do ARIA

A própria orientação de uso do W3C começa com o que profissionais chamam de primeira regra do ARIA: se um elemento HTML nativo já tem a semântica e o comportamento de que você precisa, use-o em vez de reconstruí-lo a partir de uma <div> estilizada mais um atributo de papel.

O motivo não é estético. Um <button> nativo já vem com ativação por teclado no Enter e Espaço, um estado de foco visível, o papel correto anunciado automaticamente, e comportamento correto de envio de formulário quando está dentro de um <form>. role="button" numa <div> dá exatamente uma dessas coisas: o papel anunciado. Nada mais vem de graça. Tratamento de teclado e gerenciamento de foco precisam ser construídos e mantidos manualmente, e é fácil esquecer um deles. É a mesma história do exemplo div-vs-button do artigo anterior desta série: o ARIA é frequentemente usado para remendar um problema de escolha de componente que um elemento nativo teria resolvido de graça.

1.3 O que o ARIA não consegue fazer

Este é o ponto que a maioria dos designers erra: o ARIA muda o que é anunciado. Não muda o que acontece. Adicionar aria-expanded="true" numa div não faz Enter ou Espaço abrirem nada. Um desenvolvedor ainda precisa programar esse comportamento separadamente, em JavaScript, independente do que a marcação diz. O ARIA é uma camada de rotulagem sobre um comportamento que precisa existir de forma independente dele. É exatamente por isso que documentar o comportamento, abordado na seção 4, importa tanto quanto saber o nome do atributo.

02

Os padrões que designers realmente usam

2.1 Modais e diálogos

Um modal precisa de role="dialog", aria-modal="true", e aria-labelledby apontando para o texto que serve de título. Essa parte é rápida. A parte que determina se realmente funciona é o comportamento ao redor, e é aí que moram as decisões do designer: o que aciona, onde o foco cai quando abre, se Tab e Shift+Tab ficam presos dentro dele ou vazam para a página atrás, o que fecha, e para onde o foco vai quando fecha.

Excluir projeto?

Isso não pode ser desfeito. Todos os dados serão removidos permanentemente.

Excluir
  1. Gatilho: clique em "Excluir projeto" no menu do projeto
  2. Ao abrir: o foco vai para o título do diálogo, não para o primeiro botão
  3. Enquanto aberto: Tab e Shift+Tab ficam presos só dentro do diálogo
  4. Fecha ao: pressionar Escape, clicar fora, ou em qualquer um dos botões
  5. Ao fechar: o foco volta para o item de menu que abriu, não para o corpo da página

Isso é o que um designer especifica. Um desenvolvedor transforma isso diretamente no ARIA e gerenciamento de foco corretos.

2.2 Dropdowns e comboboxes customizados

Um <select> nativo não precisa de nenhum ARIA. Já tem papel, suporte completo de teclado, e anúncio para leitor de tela embutidos em todo navegador, de graça. No momento em que um dropdown é reestilizado além do que o <select> permite e reconstruído a partir de <div>s, ele passa a precisar de um conjunto funcional de seis ou mais atributos só para alcançar paridade: role="combobox" no gatilho, aria-expanded refletindo o estado de aberto, aria-controls apontando para a lista, role="listbox" no container de opções, role="option" em cada item, e aria-activedescendant rastreando qual opção tem foco virtual enquanto as setas movem pela lista. Nada disso é exigido pelo elemento nativo. Cada um desses atributos precisa ficar perfeitamente sincronizado com o comportamento real de teclado, ou o descompasso é pior do que não fazer nada.

<select> nativo

Notificações
papel: automático teclado: automático anúncio: automático

0 atributos ARIA necessários

Dropdown customizado

Notificações
role="combobox" aria-expanded aria-controls role="listbox" role="option" aria-activedescendant

6 atributos ARIA necessários, todos sincronizados manualmente

Mesma interação, mesmo resultado visual alcançável de qualquer um dos jeitos. Só um deles exige manutenção contínua para continuar correto.

2.3 Regiões ao vivo (live regions)

Algumas mudanças de interface precisam ser anunciadas sem mover o foco do usuário: um toast confirmando um salvamento, um resumo de validação aparecendo acima de um formulário, um estado de carregamento sendo resolvido. aria-live="polite" no container que recebe esse conteúdo diz aos leitores de tela para anunciá-lo assim que a ação atual do usuário terminar, sem interromper e sem roubar o foco. aria-live="assertive" interrompe imediatamente: reserve para coisas que bloqueiam a tarefa, como um aviso de expiração de sessão, não para confirmações de rotina.

O erro que mais vejo: um toast que renderiza com uma animação CSS e parece perfeito para um usuário vidente, sem estar conectado a nada. Nenhuma região ao vivo, nenhum role="status". Visualmente funciona exatamente como projetado. Para um usuário de leitor de tela, a confirmação nunca aconteceu. O salvamento pode muito bem ter falhado silenciosamente.

Só visual

✓ Alterações salvas
Não anunciado

aria-live="polite"

✓ Alterações salvas
Anunciado ao salvar

Toast idêntico. Um existe para usuários de leitor de tela, o outro só existe na tela.

2.4 Disclosure e acordeões

aria-expanded no gatilho, aria-controls apontando para o id do painel. Simples, barato, e um dos atributos mais comumente esquecidos na web: o expandir e recolher visual funciona perfeitamente, o anúncio desse estado para um leitor de tela é a parte que fica de fora sob prazo apertado.

03

Por que o uso errado do ARIA é pior que nenhum ARIA

3.1 Os 34%, desmembrados

O relatório WebAIM Million rastreia isso há anos: homepages com ARIA presente carregam, em média, consideravelmente mais erros de acessibilidade detectados do que homepages sem nenhum. O motivo é estrutural, não uma coincidência de quem usa ARIA. O ARIA sobrescreve a semântica nativa incondicionalmente. Assim que role="button" é colocado num elemento, leitores de tela confiam nele completamente, mesmo que o elemento por baixo seja um link, uma imagem, ou texto simples com um evento de clique colado. Um atributo ARIA errado ou desatualizado não simplesmente deixa de ajudar. Ele desinforma ativamente o único canal de informação que um usuário de leitor de tela tem sobre aquele elemento. Isso é estruturalmente diferente de um alt ausente, que apenas fica em silêncio. O silêncio faz um leitor de tela recorrer a outra coisa. Uma afirmação falsa não.

3.2 Os três padrões de falha que vale a pena reconhecer de vista

Papéis redundantes. role="button" num <button> de verdade. Inofensivo por si só, mas um sinal confiável de que ninguém checou o básico antes de adicionar ARIA em cima.

Estado conflitante ou desatualizado. aria-expanded="false" fixado na marcação e nunca atualizado pelo JavaScript quando o painel realmente abre. O usuário ouve "recolhido" enquanto olha para, ou não consegue ver, um painel totalmente aberto.

aria-hidden no lugar errado. aria-hidden="true" aplicado a um container que ainda contém filhos focáveis dentro. O conteúdo desaparece da árvore de acessibilidade, mas um usuário de teclado ainda consegue dar Tab para elementos que, segundo um leitor de tela, não existem, uma inconsistência entre duas formas de navegar pela mesma página.

04

Documentando comportamento para que desenvolvedores implementem certo

4.1 O que especificar

Não nomes de atributo. Comportamento. Para todo componente interativo, cinco perguntas cobrem quase todos os casos: o que aciona a mudança de estado, qual é o estado inicial, para onde vai o foco ao abrir e ao fechar, o que se espera do teclado (Escape, setas, limites do Tab), e o que deve ser anunciado quando o estado muda, descrito em linguagem simples em vez de marcação.

Um desenvolvedor que recebe "abre ao clicar, o foco vai para o título do diálogo, Escape e clique fora fecham, o foco volta para o gatilho" consegue implementar o ARIA correto sem o designer nunca escrever um nome de atributo. Um desenvolvedor que recebe só o mockup visual precisa chutar. Chutar sob prazo apertado é de onde vêm os 34%.

4.2 Mantenha as notas junto do componente, não num documento separado

Notas de comportamento que vivem num documento compartilhado de "interações" acabam sendo ignoradas, porque ninguém abre um segundo arquivo no meio da construção. Anexe-as diretamente ao frame ou componente em qualquer ferramenta onde o handoff acontece: um sticky note, uma anotação de dev-mode, uma thread de comentário fixada no próprio componente. Quanto mais perto a documentação estiver do artefato que um desenvolvedor já está olhando, maiores as chances de sobreviver ao handoff intacta.

05

Testando o que você lançou

5.1 Uma verificação de dois minutos que qualquer designer pode rodar

Sem precisar de licença de leitor de tela. Chrome DevTools → Elements → o painel Accessibility mostra o papel, nome e estado computados de qualquer elemento selecionado em tempo real. Abra um modal, selecione-o no DevTools, confirme que o papel lê "dialog" e que o nome corresponde ao título esperado. No Mac, o VoiceOver vem de graça com o sistema (Cmd+F5 ativa); percorra seu próprio componente com ele uma vez antes de lançar. No Windows, o NVDA é gratuito para instalar. Nada disso substitui uma auditoria completa. Tudo isso pega as falhas da seção 3 antes de um desenvolvedor precisar fazer isso.

5.2 O ARIA não conserta uma decisão tomada lá atrás

A quantidade correta de ARIA num componente bem escolhido costuma ficar perto de zero: um elemento nativo já carrega a maior parte disso. O ARIA necessário para remendar um mal escolhido só cresce, e cada atributo adicionado é mais um lugar onde a implementação pode se distanciar do design com o tempo. Falhas de acessibilidade são decisões de design cobre as falhas de escolha de componente com que isso se combina: o elemento errado escolhido na etapa de design, multiplicado por todo desenvolvedor que precisa contornar depois. A correção nos dois casos é a mesma: a decisão que importa acontece antes de um único atributo ser escrito, no arquivo que o designer controla.

Fontes