Complementando elementos textuais para leitores de tela

Ao digitar o conteúdo de nossas páginas web, muitas vezes utilizamos siglas, abreviações e outros elementos visuais que são triviais para usuários videntes, mas que não conseguem ser interpretadas corretamente por leitores de tela (screen readers). Assim, a acessibildade do site fica prejudicada.

Recentemente, aqui no Elo7, enfrentamos o seguinte problema: como fazer com que nossos cards de produtos sejam lidos da forma esperada por leitores de tela? Por exemplo, tendo um preço com fonte menor e tachada, e outro com fonte maior, é possível entender que o primeiro é o preço original, enquanto o segundo é o promocional. No entanto, um leitor de tela lê os dois preços da mesma forma, sem distinção. No caso de parcelamento, interpretamos o texto "12x sem juros" como "doze vezes sem juros", enquanto um leitor de tela lê "12 xis sem juros".

Neste post, veremos exemplos (visuais e com áudios de leituras de tela) dos problemas citados e como resolvê-los usando apenas atributos ARIA (link externo) do HTML e um pouquinho de CSS!

Entendendo melhor o problema

Vale notar que este problema ocorre pelo fato de usarmos elementos puramente textuais. No caso de elementos não-textuais, como imagens e ícones, conseguimos usar atributos HTML como alt e aria-label para descrever seus conteúdos. Para tags textuais como <p> e <h1>, o leitor de telas dá preferência para o texto visível. Logo, caso uma tag textual possua aria-label, o leitor ignora e lê apenas o conteúdo da tag.

Também é importante saber que não há um padrão de implementação dos screen readers. Nos exemplos a seguir, usamos o NVDA (link externo, em inglês) versão 2020.3 no Windows 10. Assim, caso você use outro leitor ou sistema operacional, há possibilidade de se obter outros resultados.

Exemplificando

No CodePen a seguir, está um exemplo de card de produto - parecido com o de nosso site, com algumas adaptações - que contém os problemas descritos na introdução deste artigo:

O exemplo acima é fictício, então não se assuste com o preço! Preste atenção no HTML. Note que temos aria-labels mais descritivos nos elementos do card.

Agora, no áudio a seguir temos a leitura deste componente feita via NVDA:

Transcrição do áudio: “Frame. Figura. Origami Tsuru Colorido (Um unid). Sessenta reais, quarenta e oito reais, doze xis sem juros de quatro reais, quê tê dê min dez, frete grátis, legenda.”

Confuso, não? Como foi dito anteriormente, o preço original e o promocional são lidos sequencialmente, sem diferenciação. Além disso, tanto a leitura do parcelamento quanto da quantidade mínima não ficaram claras. Isso se agrava se levarmos em conta que aqueles que utilizam screen readers configuram a leitura para algo muito mais rápido do que o exemplo acima.

E agora, o que fazer?

Resolvendo o problema

O Wordpress criou uma solução para este problema usando CSS (link externo, em inglês) - e não se preocupe pois ela não funciona exclusivamente com Wordpress!

Basta usarmos esta classe:

.screen-reader-text {
  border: 0;
  clip: rect(1px, 1px, 1px, 1px);
  clip-path: inset(50%);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;
  word-wrap: normal !important;
}

Vamos adaptar o conteúdo do primeiro CodePen e adicionar esta classe para nos ajudar com a leitura de nosso card:

Podemos ver que visualmente o card continua o mesmo, mas notamos que há o uso da classe CSS .screen-reader-text juntamente com tags <span>. Há duas abordagens diferentes nesta solução:

  • no caso de descrições que complementam o que está na tela, como De R$ 60,00 e Por R$ 48,00, adicionamos um <span> no meio do parágrafo.
  • por outro lado, quando a descrição é muito diferente do texto visível, usamos o atributo aria-hidden='true' nas tags <p> (que “esconde” o elemento do leitor de tela, fazendo com que ele o ignore) e adicionamos o texto mais descritivo diretamente num <span>, como em <span class='screen-reader-text'>em até 12 vezes de R$ 4,00 sem juros. </span>.

Assim, segue a nova leitura feita pelo NVDA:

Transcrição do áudio: “Frame. Figura. Origami Tsuru Colorido (Um unid). De sessenta reais por quarenta e oito reais, em até doze vezes de quatro reais sem juros. Quantidade mínima: dez. Frete grátis. Legenda.”

Bem melhor agora! A descrição feita é mais inteligível e possui breves pausas entre os diferentes conteúdos do card (note a pontuação utilizada nas descrições, elas fazem diferença na leitura!).

Entendendo a solução

A solução foi bem simples, mas por quê margin negativa? Qual a necessidade do clip e do clip-path? O artigo do WordPress mencionado no início da seção anterior explica melhor tudo isso, mas se você tem dificuldades com inglês, segue aqui uma tradução:

  • O valor width e height é 1px pois alguns leitores de tela não leem elementos de tamanho 0px;
  • margin: -1px; esconde o elemento completamente;
  • word-wrap: normal; evita que o leitor de tela leia um texto letra por letra, dado que o texto está contido num espaço de apenas 1 pixel. Muitas combinações de leitores de tela e browsers leem palavras quebradas da forma como elas são dispostas visualmente;
  • clip está depreciado, mas foi adicionado para manter suporte a browsers mais antigos que ainda não suportam clip-path.

Nota: display: none; e visibility: hidden; escondem o texto da tela, mas também o escondem dos leitores de tela. Assim, estes atributos não podem ser utilizados para dar descrições adicionais a usuários de screen readers.

Ressalto que a classe disponibilizada pelo WordPress tem como objetivo funcionar no máximo de combinações browser-leitor de tela. Caso esta não seja sua situação, algo do como:

.visually-hidden {
  clip: rect(0.1rem, 0.1rem, 0.1rem, 0.1rem);
  height: 0.1rem;
  overflow: hidden;
  position: absolute !important;
  width: 0.1rem;
}

Também funciona. E vale lembrar que você pode alterar o nome da classe para o que achar que faz mais sentido!

Eai, o que achou? Já tinha ouvido um leitor de tela antes? Este post te ajudou a resolver seu problema? Conhece outra solução? Deixe nos comentários!

Obrigado por ler meu primeiro post no blog :) Espero escrever mais em breve!