Quando CSS não é suficiente: requisitos de JavaScript para componentes acessíveis

Publicados: 2022-03-10
Resumo rápido ↬ Alerta de spoiler: dicas de ferramentas, modais, guias, carrosséis e menus suspensos são alguns dos componentes da interface do usuário que exigem mais do que CSS. Para garantir a acessibilidade de sua interface, o JavaScript é uma adição necessária para realizar o gerenciamento de foco, responder a eventos de teclado e alternar atributos ARIA.

Como autor de ModernCSS.dev, sou um grande defensor de soluções CSS. E adoro ver as maneiras inteligentes como as pessoas usam CSS para designs e interatividade realmente inovadores! No entanto, notei uma tendência de promover componentes “somente CSS” usando métodos como o “hack de caixa de seleção”. Infelizmente, hacks como esses deixam uma quantidade significativa de usuários incapazes de usar sua interface.

Este artigo aborda vários componentes comuns e por que o CSS não é suficiente para cobrir a acessibilidade, detalhando os requisitos do JavaScript. Esses requisitos são baseados nas Diretrizes de Acessibilidade de Conteúdo da Web (WCAG) e pesquisas adicionais de especialistas em acessibilidade. Não vou prescrever soluções JavaScript ou CSS de demonstração, mas sim examinar o que precisa ser considerado ao criar cada componente. Um framework JavaScript certamente pode ser usado, mas não é necessário para adicionar os eventos e recursos discutidos.

Os requisitos listados são, em geral, não opcionais — eles são necessários para ajudar a garantir a acessibilidade de seus componentes.

Se você estiver usando uma estrutura ou biblioteca de componentes, poderá usar este artigo para ajudar a avaliar se os componentes fornecidos atendem aos requisitos de acessibilidade . É importante saber que muitos dos itens mencionados não serão totalmente cobertos por ferramentas automatizadas de teste de acessibilidade como o aXe e, portanto, precisam de alguns testes manuais. Ou você pode usar uma estrutura de teste como o Cypress para criar testes para a funcionalidade necessária.

Tenha em mente que este artigo está focado em informá-lo sobre as considerações de JavaScript para cada componente de interface. Este não é um recurso abrangente para todos os detalhes de implementação para a criação de componentes totalmente acessíveis, como ária necessária ou mesmo marcação. Os recursos são incluídos para cada tipo para ajudá-lo a aprender mais sobre as considerações mais amplas de cada componente.

Mais depois do salto! Continue lendo abaixo ↓

Determinando se somente CSS é uma solução apropriada

Aqui estão algumas perguntas a serem feitas antes de prosseguir com uma solução somente CSS. Abordaremos alguns dos termos apresentados aqui em mais contexto, juntamente com seus componentes relacionados.

  • Isso é para seu próprio prazer?
    Então, vá com tudo no CSS, ultrapasse os limites e aprenda o que a linguagem pode fazer!
  • O recurso inclui mostrar e ocultar conteúdo?
    Então você precisa do JS para, no mínimo, alternar a ária e habilitar o fechamento em Esc . Para certos tipos de componentes que também mudam de estado, você também pode precisar comunicar as alterações acionando atualizações em uma região ativa ARIA.
  • A ordem de foco natural é a mais ideal?
    Se a ordem natural perder a relação entre um acionador e o elemento que acionou, ou um usuário de teclado não puder acessar o conteúdo por meio da ordem de tabulação natural, você precisará do JS para auxiliar no gerenciamento de foco.
  • O controle estilizado oferece as informações corretas sobre a funcionalidade?
    Os usuários de tecnologia assistiva, como leitores de tela, recebem informações baseadas em semântica e ARIA que os ajudam a determinar o que um controle faz. E os usuários de reconhecimento de fala precisam ser capazes de identificar o rótulo ou tipo do componente para descobrir a frase a ser usada para operar os controles. Por exemplo, se o seu componente tem o estilo de guias, mas usa botões de opção para “funcionar” como guias, um leitor de tela pode ouvir “botão de rádio” e um usuário de fala pode tentar usar a palavra “tab” para operá-los. Nesses casos, você precisará do JS para habilitar o uso dos controles e semântica apropriados para obter a funcionalidade desejada.
  • O efeito depende de foco e/ou foco?
    Então você pode precisar de JS para ajudar em uma solução alternativa para fornecer acesso igual ou acesso persistente ao conteúdo, especialmente para usuários de tela sensível ao toque e aqueles que usam zoom de desktop de 200%+ ou software de ampliação.

Dica rápida : Outra referência ao criar qualquer tipo de controle personalizado é a Lista de Verificação de Desenvolvimento Acessível de Controle Personalizado do guia W3 “Usando ARIA”. Isso menciona vários pontos acima, com algumas considerações adicionais de design e semântica.

Dicas de ferramentas

Restringir a definição de uma dica de ferramenta é um pouco complicado, mas nesta seção estamos falando de pequenos rótulos de texto que aparecem ao passar o mouse perto de um elemento acionador. Eles se sobrepõem a outro conteúdo, não exigem interação e desaparecem quando um usuário remove o foco ou o foco.

Dicas de ferramentas de exemplo do GitHub, Whimsical e Notion
Dicas de ferramentas de exemplo do GitHub, Whimsical e Notion. (Visualização grande)

A solução somente CSS aqui pode parecer completamente boa e pode ser realizada com algo como:

 <button class="tooltip-trigger">I have a tooltip</button> <span class="tooltip">Tooltip</span> .tooltip { display: none; } .tooltip-trigger:hover + .tooltip, .tooltip-trigger:focus + .tooltip { display: block; }

No entanto, isso ignora uma grande lista de preocupações de acessibilidade e exclui muitos usuários de acessar o conteúdo da dica de ferramenta.

Um grande grupo de usuários excluídos são aqueles que usam telas sensíveis ao toque onde :hover possivelmente não será acionado, pois nas telas sensíveis ao toque, um evento :hover é acionado em sincronia com um evento :focus . Isso significa que qualquer ação relacionada conectada ao elemento acionador - como um botão ou link - será acionada junto com a dica de ferramenta sendo revelada. Isso significa que o usuário pode perder a dica de ferramenta ou não ter tempo de ler seu conteúdo.

Caso a dica de ferramenta esteja anexada a um elemento interativo sem eventos, a dica de ferramenta pode ser exibida, mas não pode ser dispensada até que outro elemento ganhe foco e, nesse meio tempo, pode bloquear o conteúdo e impedir que um usuário execute uma tarefa.

Além disso, os usuários que precisam usar software de zoom ou ampliação para navegar também enfrentam uma grande barreira ao usar dicas de ferramentas. Como as dicas de ferramentas são reveladas ao passar o mouse, se esses usuários precisarem alterar seu campo de visão movendo a tela para ler a dica de ferramenta, isso poderá fazer com que ela desapareça. As dicas de ferramenta também removem o controle do usuário, pois geralmente não há nada para informar ao usuário que uma dica de ferramenta aparecerá antes do tempo. A sobreposição de conteúdo pode impedi-los de realizar uma tarefa. Em algumas circunstâncias, como uma dica de ferramenta vinculada a um campo de formulário, teclados móveis ou outros teclados na tela podem obscurecer o conteúdo da dica de ferramenta. E, se eles não estiverem conectados adequadamente ao elemento acionador, alguns usuários de tecnologia assistiva podem nem saber que uma dica de ferramenta apareceu.

A orientação para o comportamento das dicas de ferramentas vem do Critério de Sucesso WCAG 1.4.13 — Conteúdo em foco ou foco. Este critério destina-se a ajudar os usuários de baixa visão e aqueles que usam software de zoom e ampliação. Os princípios orientadores da dica de ferramenta (e outros conteúdos que aparecem ao passar o mouse e focar) incluem:

  • Dispensável
    A dica de ferramenta pode ser dispensada sem mover o foco ou o foco
  • Hoverable
    O conteúdo da dica de ferramenta revelada pode ser pairado sem que ela desapareça
  • Persistente
    O conteúdo adicional não desaparece com base em um tempo limite, mas aguarda que um usuário remova o foco ou o foco ou o descarte de outra forma

Para atender totalmente a essas diretrizes, é necessário alguma assistência de JavaScript, principalmente para permitir a dispensa do conteúdo.

  • Os usuários de tecnologia assistiva presumirão que o comportamento de demissão está vinculado à tecla Esc , que requer um ouvinte JavaScript.
  • De acordo com a pesquisa de Sarah Higley descrita na próxima seção, adicionar um botão “fechar” visível na dica de ferramenta também exigiria JavaScript para lidar com seu evento de fechamento.
  • É possível que o JavaScript precise aumentar sua solução de estilo para garantir que um usuário possa passar o mouse sobre o conteúdo da dica de ferramenta sem que ele seja dispensado enquanto o usuário move o mouse.

Alternativas às dicas de ferramentas

As dicas de ferramentas devem ser o último recurso. Sarah Higley — uma especialista em acessibilidade que tem uma paixão especial por dissuadir o uso de dicas de ferramentas — oferece este teste simples:

“Por que estou adicionando este texto à interface do usuário? Para onde mais poderia ir?”

— Sarah Higley da apresentação “Dicas de ferramentas: investigação em quatro partes”

Com base na pesquisa com a qual Sarah se envolveu em sua função na Microsoft, uma solução alternativa é uma “dica de alternância” dedicada. Essencialmente, isso significa fornecer um elemento adicional para permitir que um usuário acione intencionalmente a exibição e ocultação de conteúdo extra . Ao contrário das dicas de ferramentas, as dicas de alternância podem reter a semântica dos elementos no conteúdo revelado. Eles também devolvem ao usuário o controle de alterná-los e mantêm a capacidade de descoberta e operacionalidade por mais usuários e, em particular, usuários de tela sensível ao toque.

Se você lembrou que o atributo title existe, saiba que ele sofre os mesmos problemas que observamos em nossa solução somente CSS. Em outras palavras, não use title supondo que seja uma solução de dica de ferramenta aceitável.

Para obter mais informações, confira a apresentação de Sarah no YouTube, bem como seu extenso artigo sobre dicas de ferramentas. Para saber mais sobre dicas de ferramentas versus dicas de alternância e um pouco mais de informações sobre por que não usar title , revise o artigo de Heydon Pickering em Componentes Inclusivos: Dicas de ferramentas e dicas de alternância.

Modais

Os modais — também conhecidos como lightboxes ou diálogos — são janelas na página que aparecem após uma ação de acionamento. Eles se sobrepõem ao conteúdo de outra página, podem conter informações estruturadas, incluindo ações adicionais, e geralmente têm um pano de fundo semitransparente para ajudar a distinguir a janela modal do resto da página.

Exemplos de modais do GitHub e do Material Design
Exemplos de modais do GitHub e do Material Design. (Visualização grande)

Eu vi algumas variações de um modal somente CSS (e sou culpado de fazer um para uma versão mais antiga do meu portfólio). Eles podem usar o “hack de caixa de seleção”, fazer uso do comportamento de :target , ou tentar modelá-lo fora de :focus (o que provavelmente é realmente uma dica de ferramenta muito grande disfarçada).

Quanto ao elemento de dialog HTML, esteja ciente de que ele não é considerado totalmente acessível. Então, enquanto eu absolutamente encorajo as pessoas a usarem HTML nativo antes de soluções personalizadas, infelizmente esta quebra essa ideia. Você pode saber mais sobre por que a caixa de dialog HTML não está acessível.

Ao contrário das dicas de ferramentas, os modais destinam-se a permitir conteúdo estruturado. Isso significa potencialmente um título, algum conteúdo de parágrafo e elementos interativos como links, botões ou até mesmo formulários. Para que a maioria dos usuários acessem esse conteúdo, eles devem poder usar eventos de teclado , principalmente tabulação. Para conteúdo modal mais longo, as teclas de seta também devem manter a capacidade de rolar. E como dicas de ferramentas, elas devem ser descartadas com a tecla Esc - e não há como habilitar isso apenas com CSS.

JavaScript é necessário para gerenciamento de foco dentro de modais. Os modais devem interceptar o foco, o que significa que, uma vez que o foco esteja dentro do modal, um usuário não poderá sair dele para o conteúdo da página por trás dele. Mas primeiro, o foco precisa entrar no modal, que também requer JavaScript para uma solução modal totalmente acessível.

Aqui está a sequência de eventos modais relacionados que devem ser gerenciados com JavaScript:

  1. O ouvinte de eventos em um botão abre o modal
  2. O foco é colocado dentro do modal; qual elemento varia com base no conteúdo modal (consulte a árvore de decisão)
  3. O foco fica preso dentro do modal até que seja dispensado
  4. De preferência, um usuário é capaz de fechar um modal com a tecla Esc , além de um botão de fechar dedicado ou uma ação destrutiva do botão, como “Cancelar” se for necessário o reconhecimento do conteúdo modal
    1. Se Esc for permitido, os cliques no pano de fundo modal também devem dispensar o modal
  5. Após a dispensa, se nenhuma navegação ocorreu, o foco é colocado novamente no elemento do botão de acionamento

Árvore de decisão de foco modal

Com base no exemplo de diálogo modal de práticas de autoria WAI-ARIA, aqui está uma árvore de decisão simplificada para onde colocar o foco quando um modal é aberto. O contexto sempre ditará a escolha aqui e, idealmente, o foco é gerenciado além de simplesmente “o primeiro elemento focalizável”. De fato, às vezes, elementos não focalizáveis ​​precisam ser selecionados.

  • O assunto principal do modal é um formulário.
    Concentre-se no primeiro campo do formulário.
  • O conteúdo modal é significativo em comprimento e afasta as ações modais da vista.
    Concentre-se em um título, se presente, ou no primeiro parágrafo.
  • A finalidade do modal é processual (exemplo: confirmação de ação) com múltiplas ações disponíveis.
    Concentre-se na ação “menos destrutiva” com base no contexto (exemplo: “OK”).
  • A finalidade do modal é processual com uma ação.
    Concentre-se no primeiro elemento focalizável

Dica rápida : No caso de precisar focar um elemento não focalizável, como um título ou parágrafo, adicione tabindex="-1" que permite que o elemento se torne programaticamente focável com JS, mas não o adiciona à ordem de tabulação DOM .

Consulte a demonstração modal WAI-ARIA para obter mais informações sobre outros requisitos para configurar ARIA e detalhes adicionais sobre como selecionar qual elemento adicionar foco. A demonstração também inclui JavaScript para exemplificar como fazer o gerenciamento de foco.

Para uma solução pronta para uso, Kitty Giraudel criou uma caixa de diálogo11y que inclui os requisitos de recursos que discutimos. Adrian Roselli também pesquisou o gerenciamento do foco de diálogos modais e criou uma demonstração e compilou informações sobre como diferentes combinações de navegadores e leitores de tela comunicarão o elemento focado.

Abas

As interfaces com guias envolvem uma série de acionadores que exibem os painéis de conteúdo correspondentes, um de cada vez. Os “hacks” de CSS que você pode encontrar envolvem o uso de botões de opção estilizados, ou :target , que permitem apenas revelar um único painel de cada vez.

Guias de exemplo do Shopify Polaris e IBM Carbon
Guias de exemplo do Shopify Polaris e IBM Carbon. (Visualização grande)

Aqui estão os recursos da guia que exigem JavaScript:

  1. Alternando o atributo aria-selected para true para a guia atual e false para guias não selecionadas
  2. Criando um índice de tabulação móvel para distinguir a seleção de tabulação do foco
  3. Mova o foco entre as guias respondendo aos eventos das teclas de seta (e opcionalmente Home e End )

Opcionalmente, você pode fazer com que a seleção de guias siga o foco - ou seja, quando uma guia está focada, ela também é selecionada e mostra seu painel de guias associado. O WAI-ARIA Authoring Practices oferece este guia para escolher se a seleção deve seguir o foco.

Independentemente de você escolher ou não ter a seleção seguindo o foco, você também usará JavaScript para ouvir eventos de tecla de seta para mover o foco entre os elementos da guia. Este é um padrão alternativo para permitir a navegação das opções de tabulação, pois o uso de um índice de tabulação móvel (descrito a seguir) altera a ordem natural de foco da tabulação do teclado.

Sobre o tabindex

O conceito de um tabindex móvel é que o valor do valor tabindex é controlado programaticamente para gerenciar a ordem de foco dos elementos. Em relação às guias, isso significa que apenas a guia selecionada faz parte da ordem de foco por meio da configuração tabindex="0" , e as guias não selecionadas são definidas como tabindex="-1" , o que as remove da ordem natural de foco do teclado.

A razão para isso é que, quando uma guia é selecionada, a próxima guia coloca o foco do usuário no painel de guias associado. Você pode optar por tornar o elemento que é o painel de guias focalizável atribuindo-lhe tabindex="0" , ou isso pode não ser necessário se houver uma garantia de um elemento focalizável dentro do painel de guias . Se o conteúdo do seu painel de guias for mais variável ou complexo, você pode considerar gerenciar o foco de acordo com a árvore de decisão que analisamos para modais.

Exemplos de padrões de guia

Aqui estão alguns padrões de referência para criar guias:

  • Demonstração do Tabpanel da Universidade Deque
  • Testes de widget de guia de Scott O'Hara (testa vários padrões funcionais)
  • Interfaces com guias de componentes inclusivos de Heydon Pickering, que demonstra como as guias podem ser um aprimoramento progressivo de um índice

Carrosséis

Também chamados de slideshows ou sliders, os carrosséis envolvem uma série de painéis de conteúdo giratórios (também conhecidos como “slides”) que incluem mecanismos de controle. Você os encontrará em muitas configurações com uma ampla variedade de conteúdo. Eles são notoriamente considerados um padrão de design ruim.

Um exemplo de demonstração de carrossel criado com bxSlider
Um exemplo de demonstração de carrossel criado com bxSlider. (Visualização grande)

A parte complicada dos carrosséis somente CSS é que eles podem não oferecer controles ou podem usar controles inesperados para manipular o movimento do carrossel. Por exemplo, você pode usar novamente o “hack de caixa de seleção” para fazer a transição do carrossel, mas as caixas de seleção transmitem o tipo errado de informação sobre a interação aos usuários de tecnologia assistiva. Além disso, se você estilizar os rótulos das caixas de seleção para que apareçam visualmente como setas para frente e para trás, provavelmente dará aos usuários do software de reconhecimento de fala a impressão errada do que eles devem dizer para controlar o carrossel.

Mais recentemente, o suporte CSS nativo para snap de rolagem chegou. A princípio, essa parece ser a solução perfeita somente para CSS. Mas, mesmo a verificação automatizada de acessibilidade os sinalizará como não navegáveis ​​por usuários de teclado , caso não haja como navegar por meio de elementos interativos. Existem outras preocupações de acessibilidade e experiência do usuário com o comportamento padrão desse recurso, algumas das quais incluí em minha demonstração de snap de rolagem no SmolCSS.

Apesar da ampla variedade de aparência dos carrosséis, existem alguns traços comuns. Uma opção é criar um carrossel usando a marcação de guias, pois efetivamente é a mesma interface subjacente com uma apresentação visual alterada. Em comparação com as guias, os carrosséis podem oferecer controles extras para anterior e seguinte e também pausar se o carrossel estiver sendo reproduzido automaticamente.

Veja a seguir as considerações sobre JavaScript, dependendo dos recursos do carrossel:

  • Usando controles paginados
    Após a seleção de um item numerado, foque programaticamente o slide do carrossel associado. Isso envolverá a configuração de contêineres de slides usando tabindex móvel para que você possa focar o slide atual, mas impedir o acesso a slides fora da tela.
  • Usando a reprodução automática
    Inclua um controle de pausa e também ative a pausa quando o slide estiver em foco ou um elemento interativo dentro dele estiver focado. Além disso, você pode verificar prefers-reduced-motion no JavaScript para carregar a apresentação de slides em um estado pausado para respeitar as preferências do usuário.
  • Usando os controles anterior/seguinte
    Inclua um elemento visualmente oculto marcado como aria-live="polite" e, ao ativar esses controles, preencha a região ao vivo com uma indicação da posição atual, como “Slide 2 de 4”.

Recursos para construir carrosséis acessíveis

  • Detalhes e considerações completos de implementação, bem como um exemplo de código completo do tutorial W3C Web Accessibility em carrosséis
  • O exemplo da Deque University de aprimorar uma interface de guia em um carrossel
  • O exemplo WAI-ARIA Authoring Practices de um carrossel de imagens com rotação automática
  • Uma seleção de recursos de carrossel no resumo de componentes acessíveis do Smashing

Menus suspensos

Isso se refere a um componente em que um botão abre uma lista de links, normalmente usados ​​para menus de navegação. Implementações de CSS que param de mostrar o menu em :hover ou :focus apenas perdem alguns detalhes importantes.

Exemplos de menus suspensos do Dribbble, pesquisa do Google e GitHub
Exemplos de menus suspensos do Dribbble, pesquisa do Google e GitHub. (Visualização grande)

Admito que até pensei que, usando a propriedade :focus-within mais recente, poderíamos implementar com segurança uma solução somente CSS. Você verá que meu artigo sobre menus suspensos CSS foi alterado para incluir notas e recursos sobre o JavaScript necessário (mantive o título para que outros que buscam essa solução também concluam a implementação do JS). Especificamente, confiar apenas em CSS significa violar o Critério de Sucesso 1.4.13 das WCAG: Conteúdo em foco ou foco sobre o qual aprendemos com dicas de ferramentas.

Precisamos adicionar JavaScript para algumas técnicas que devem parecer familiares neste momento:

  • Alternando aria-expanded no botão de menu entre true e false ouvindo os eventos de click
  • Fechando um menu aberto ao usar a tecla Esc e retornando o foco para o botão de alternância do menu
  • De preferência, fechar menus abertos quando o foco é movido para fora do menu
  • Opcional : Implemente as teclas de seta, bem como as teclas Home e End para navegação do teclado entre os botões de alternância de menu e links dentro das listas suspensas

Dica rápida : Garanta a implementação correta do menu suspenso associando a exibição do menu ao seletor de .dropdown-toggle[aria-expanded= " true " ] + .dropdown em vez de basear a exibição do menu na presença de um JS- adicional classe adicionada como active . Isso também remove alguma complexidade da sua solução JS!

Isso também é chamado de “padrão de divulgação” e você pode encontrar mais detalhes no menu de navegação de divulgação de exemplo do WAI-ARIA Authoring Practices.

Recursos adicionais sobre a criação de componentes acessíveis

  • Guia completo do Smashing para componentes front-end acessíveis
  • Artigo de Carie Fisher Bom, melhor, melhor: desvendando o complexo mundo dos padrões acessíveis
  • Demonstrações e informações sobre padrões de design e widgets comuns disponíveis no WAI-ARIA Authoring Practices 1.2
  • Biblioteca de Códigos da Universidade Deque
  • Componentes Acessíveis de Scott O'Hara
  • Componentes Inclusivos de Heydon Pickering