(85) 99645-7140 nelclassico@gmail.com Praça Coronel Melquiades, 124
accessibility Articles JavaScript Tecnologia

O canto da sereia de ariaNotify()

Preciso que todos vocês me prometam que ficarão bem com isso. Estou aqui para falar sobre um próximo recurso da plataforma da web que está longo chegando; um recurso que não apenas atende a um caso de uso que precisava de uma solução melhor, mas também por meio de uma sintaxe que é imediatamente compreensível e aparentemente poderosa. Isso mesmo, essa coisa é desenvolvedor catnip , e não me importo de dizer que estava muito animado para experimentá-lo – depois disso, me esforcei para guardá-lo em uma gaveta e tirá-lo da cabeça. Esta é uma ferramenta para ser usada apenas em situações em que é absolutamente, cem por cento necessário, para resolver um problema que não pode ser resolvido de nenhuma outra forma, até e incluindo “recuar contra a construção de um recurso em primeiro lugar”. Então seja tranquilo sobre isso, ok? OK.

Há um novo método ariaNotify() — definido pela especificação Accessible Rich Internet Applications (WAI-ARIA) 1.3 — que fornece um meio de acionar programaticamente a narração em uma tela leitor. Ele aceita uma string como primeiro argumento e um objeto de configuração opcional como segundo:

document.ariaNotify( "Hello, World." );
// When invoked, a screen reader will narrate "Hello, World."

Isso pode parecer uma solução simples para um caso de uso igualmente simples aqui impresso, mas historicamente isso tem um problema complicado que só poderia ser resolvido pelo uso ligeiramente off-label das regiões ativas da ARIA. Isso significa que compreender as regiões vivas — e suas deficiências — é a chave para entender o que ariaNotify faz por nós. Se você já trabalhou com regiões ativas antes, provavelmente fechou esta guia logo após o trecho de código e está atualmente na terceira ou quarta volta pela sala com os braços erguidos em triunfo. Se você nunca trabalhou com regiões ativas antes, bem, para colocar isso nos termos técnicos mais estritos possíveis: uau que bagunça.

Em um contexto de navegação assistida, se alguma parte de uma página mudar em resposta a uma interação do usuário, ou algo for carregado e adicionado à página de forma assíncrona, essas alterações não serão detectáveis até que o usuário mova seu foco para esse conteúdo alterado – um usuário não teria como saber que algo tinha mudado, muito menos o que . As regiões ativas abordam isso, pelo menos por design: um elemento com um atributo aria-live solicitará narração para alterações na marcação contida nesse elemento – quando a marcação é alterada, a marcação alterada é narrada em voz alta. Se aria-live tiver um valor de assertive, informa à tecnologia assistiva “isso é urgente e deve ser narrado imediatamente”. Se aria-live tiver um valor de polite, diz “isso deve ser narrado, na próxima oportunidade natural para fazê-lo”. Usar role="alert" ou role="status" em um elemento é funcionalmente equivalente a aria-live="assertive" e aria-live="polite", respectivamente. Parece bastante razoável no papel, certo?

Naturalmente, precisávamos de uma maneira de ajustar exatamente quais informações são narradas e como, portanto, existem alguns outros atributos que determinam o comportamento de uma região ativa:

aria-atomic

  • true: anuncia apenas o texto que muda dentro do elemento
  • false (padrão): narra todo o conteúdo da região ao vivo quando algo nela muda.

aria-relevant

  • text: notificar o usuário quando frasear o conteúdo — texto, assim como diz na lata — muda dentro da região ao vivo
  • additions: notificar o usuário quando um nó for adicionado à região ativa
  • removals: notificar o usuário quando um nó for removido da região ativa
  • all (padrão): notificar o usuário se o texto for alterado e/ou elementos forem adicionados ou removidos do DOM

Novamente, sólido o suficiente em teoria! Problema resolvido, até o ponto em que você tenta usar regiões ativas, para praticamente qualquer coisa, sempre. Na prática, navegadores e tecnologias assistivas são descontroladamente inconsistente quanto à implementação , especialmente no que se refere à marcação aninhada em uma região ativa – se você quiser aria-live para funcionar conforme o esperado, muitas vezes você acabará precisando remover toda a marcação semanticamente significativa que você deveria estar usando. Para funcionar de maneira confiável em contextos de navegação assistida, uma região ativa já deve existir significativamente no DOM no momento em que a narração é acionada . Uma região ao vivo não pode ser alternada de display: none ou injetada na página junto com com o conteúdo a ser narrado ou você terá problemas de tempo que impedem que o conteúdo seja narrado – quando o navegador “vê” pela primeira vez a região ao vivo, ele bloqueia “ok, narre qualquer coisa que mude neste contêiner”, o que não inclui necessariamente o conteúdo inicial. A forma como os valores "assertive" e "polite" funcionam não está especialmente bem definida na especificação ou realizado em leitores de tela e combinações de navegador , também. Novamente: eles são uma bagunça .

Mesmo que tudo isso não fosse o caso, há uma incompatibilidade fundamental entre o propósito das regiões ativas e a forma como a web moderna é construída. Como eu disse, as regiões ativas só funcionam quando a marcação é adicionada/removida do elemento em questão, e essa não é a realidade da maioria das interações que resultam em uma alteração no conteúdo visível de uma página. As regiões ativas não ajudam quando você revela marcações que já estão no documento, mas inertes – por exemplo, trocando entre uma propriedade display visível e display: none. Esse caso de uso é tão comum quanto as mudanças estruturais dinâmicas no documento atual, se não mais.

Todas essas limitações fizeram com que regiões ativas fossem usadas quase exclusivamente como APIs de notificação improvisadas: ter um ou mais aria-live elementos enterrados na página, visualmente ocultos ( mas não removidos da árvore de acessibilidade via display: none), que você atualiza conforme necessário com qualquer texto que deseja narrar. Eu estive lá e fiz isso, e é desajeitado, principalmente por causa de como as regiões vivas são inconsistentes em sua essência. Esse conteúdo injetado também está necessariamente disponível para um usuário que navega pela página por meio de tecnologia assistiva, apenas flutuando no documento divorciado de seu significado original – se você não for meticuloso na limpeza posterior, você adicionou uma fonte potencialmente confusa de narração contextualmente irrelevante à página. Acima de tudo, porém, você adicionou uma preocupação nova e invisível ; um recurso que precisará de testes e manutenção dedicados, e algo que pode quebrar de maneiras literalmente invisíveis, e fazê-lo de maneiras que têm o potencial de ser irritantes, enganosas, confusas ou todas as opções acima. É isso que acontece com o trabalho de acessibilidade: decisões rápidas e fáceis tomadas isoladamente podem ter consequências imprevistas no contexto da experiência geral e, a menos que essas suposições sejam testadas com muito cuidado – cedo e frequentemente – não podemos saber quais podem ser essas consequências.

O método ariaNotify substitui esse tipo de Rube Goldberg engenhoca de acessibilidade, fornecendo a você uma notificação real do leitor de tela API, sem necessidade de marcação complicada (e instável):

document.ariaNotify( "Hello, World." );

ariaNotify está disponível como um método na interface Element ou no Document interface — não há nenhuma diferença significativa entre os dois para os exemplos que você verá aqui, mas vale a pena saber que chamar document.ariaNotify() significa que o lang atributo especificado no elemento html, especificamente, será usado para inferir o idioma da notificação:

const btn = document.querySelector( "button.announce" );

btn.addEventListener("click", function( e ) {
  document.ariaNotify( "Hello, World." );
});
/*
* Clicking the button results in the "polite"-timed announcement "hello, world," 
* using the `lang` attribute specified on the `<html>` element. If there isn't 
* one, the browser's default language is used.
/*

Ao chamar ariaNotify() de um elemento significa que o atributo lang do ancestral mais próximo do elemento será usado para determinar o idioma da notificação:

const btn = document.querySelector( "button.announce" );

btn.addEventListener("click", function( e ) {
  this.ariaNotify( "Hello, World." );
});
/*
* Clicking the button results in the "polite"-timed announcement "hello, world," 
* using the `lang` attribute of the `button` (or the closest parent element with 
* `lang`) to  determine pronunciation. If there isn't one in the document (all
* the way up to and including `<html>`), the browser's default language is used.
/*

ariaNotify aceita um segundo parâmetro que permite definir um nível de prioridade explícito:

const btn = document.querySelector( "button.announce" );

btn.addEventListener("click", function( e ){
  this.ariaNotify( "Hello, world.", {
    priority: "high"
  });
});

A prioridade padrão para essas notificações é priority: "normal", que se comporta como aria-live="polite" (ou role="status"). Definir um priority: "high" explícito priorizará e potencialmente interromperá a narração atual, da maneira aria-live="assertive" (ou role="alert") faria.

Essa é toda a API até agora, bem aqui. Sem complicações com marcação, sem ajustes de tempo; se precisar de algo narrado, você chama ariaNotify e é narrado , simples assim. Você pode experimentar isso no Firefox enquanto falamos, embora não pareça que lang os atributos ainda não estejam incluídos:

CodePen Incorporar Fallback

MANDÍBULAS

Educado, botão. Para ativar, pressione a barra de espaço. [barra de espaço pressionada] Espaço. Olá, mundo.

Assertivo, botão. Para ativar, pressione a barra de espaço. [barra de espaço pressionada] Espaço. Olá, mundo.

Educado [pronunciado corretamente], botão. Para ativar, pressione a barra de espaço. Espaço. Olá, Mundo [pronunciado incorretamente].

NVDA

Educado, botão. [barra de espaço pressionada] Olá, mundo.

Assertivo, botão. [barra de espaço pressionada] Olá, mundo.

Educado [pronunciado corretamente], botão. [barra de espaço pressionada] Hola, Mundo [pronunciado incorretamente].

VoiceOver

Educado, botão. Você está atualmente em um botão [barra de espaço pressionada] dentro de um quadro. Para clicar neste botão, pressione Control Opção Espaço . Para sair desta área da web, pressione Control Opção Shift-Up Arrow . Olá, mundo.

Assertivo, botão. Você está atualmente em um botão [barra de espaço pressionada] Olá, mundo.

Educado [pronunciado corretamente], botão. Você está atualmente em um botão [barra de espaço pressionada] dentro de um quadro. Para clicar neste botão, pressione Control Opção Espaço . Para sair desta área da web, pressione Control Opção Shift-Up Arrow . Olá, Mundo [pronunciado incorretamente].

Bastante sólido, hein? Enorme melhoria em relação a como todos nós ficamos presos ao uso de regiões ativas. Eu, por exemplo, mal posso esperar para quase usar ariaNotify, então – de novo – prontamente me convenço do contrário!

Por que a relutância? Bem, nos círculos de acessibilidade, às vezes se diz que existem três estágios para aprender a usar ARIA:

  1. Você não usa ARIA.
  2. Você usa ARIA.
  3. Você não usa ARIA.

O W3C coloca isso em termos mais formais, como é sua especialidade:

Se você puder usar um elemento HTML nativo [HTML] ou atributo com a semântica e o comportamento necessários já integrados, em vez de redirecionar um elemento e adicionar uma função, estado ou propriedade ARIA para torná-lo acessível, faça-o.

Usando ARIA: Primeira regra de uso de ARIA

Esse segundo estágio do domínio ARIA é onde nos metemos em problemas. A web é um lugar caótico, mas as tecnologias assistivas evoluíram junto com ela e aprenderam a encobrir alguns dos problemas mais comuns que um usuário pode encontrar. Por exemplo, digamos que você tenha um h2 que revela algum conteúdo visualmente oculto que o segue quando clicado – esse elemento pode ser apresentado de uma maneira que torna essa interação clara visualmente , mas sem nossa intervenção, ele pode não ser sinalizado para um usuário navegando por meio do leitor de tela. Para contornar isso, as tecnologias assistivas podem narrar de forma útil esse elemento de título como “clicável” quando ele recebe o foco do usuário ao encontrar um ouvinte de evento click vinculado a esse título. É verdade que isso não é tão bom quanto sinalizar explicitamente o propósito deste elemento para o usuário, mas é viável , mesmo que não tenhamos tornado essa interação explícita.

O problema está em como nós que tornamos essa interação explícita. Se você estiver familiarizado com ARIA, poderá pensar “bem, este elemento se comporta como um botão, então devo colocar role="button" nele para informar ao usuário que isso faz alguma coisa”. Esse impulso não é estritamente errado, mas com esse atributo vem uma consequência provavelmente não intencional: ao sermos explícitos sobre a função do elemento, removemos seu implícito significado. Você está dizendo ao navegador e à tecnologia assistiva, em termos inequívocos, que este não é um título – então, se o usuário estiver navegando por meio do esboço do documento , esse elemento não fará mais parte dessa navegação, e o que parecia uma solução rápida simples e útil acaba tendo uma consequência não intencional. ARIA não deixa espaço para interpretação; o que dizemos vale, ponto final. Dizemos “narre isso”, é narrado. Não negociável.

Então, dado um recurso muito fácil de usar que indiscutivelmente diz “quando eu disser para você narrar isso, você narra”, por favor, presuma que estou fazendo contato visual firme e sem piscar aqui enquanto digo, em voz alta, “alert()” – um cenário imaginado feito ainda mais perturbador pelo fato de que de alguma forma consegui dizer isso em uma fonte monoespaçada.

window.alert( "Hello world, like it or not." );

Você se lembra de alert() de antigamente, certo? Um método tão infame quanto desagradável. Se você é novo no setor, talvez não esteja familiarizado com ele em primeira mão, para uma bênção. Como ariaNotify, era – é , tecnicamente – uma API rápida e fácil para apresentar informações imediatamente a um usuário:

A CSS-Tricks article with an alert on top that identifies the site address and says 'Hello World, like it or not.'

alert() é simples, eficaz, consistente e – na época em que era amplamente utilizado – incrivelmente irritante. ariaNotify() confia a você esse mesmo poder, desta vez apoiado pela autoridade invisível e inflexível da ARIA. Com ariaNotify() em seu bolso, “isso pode ser confuso, então vou narrar exatamente o que está acontecendo” será uma decisão rápida e fácil e pode significar que um usuário experiente – já hábil em navegar na web apesar de todo o seu caos e inconsistência inerentes – terá sua navegação interrompida por uma palestra sobre uma interação que eles já entendem.

Não é difícil imaginar um desenvolvedor – com o coração no lugar certo – usando ariaNotify para informar ao usuário que o conteúdo foi revelado por uma interação na página. No contexto, no entanto, revelar esse conteúdo provavelmente significava interagir com um elemento já narrado como “clicável” graças à presença de um ouvinte de evento associado, à semântica inerente do elemento ou à presença de um atributo aria-expanded="false" (a abordagem correta para sinalizar que a interação com um elemento revelará o conteúdo associado). Nesse caso, tudo o que fizemos foi adicionar ruído à experiência do usuário na página, e ninguém precisa disso. Quero dizer, imagine estar no meio da leitura desta frase quando alert( "There's a new comment on this article!" ) interrompe você pela terceira vez, ou passa o mouse sobre <button>Navigation</button> apenas para ser atingido por alert( "Click here to open the navigation." ) gosta de algum tutorial de videogame que não pode ser ignorado? Eca. Eu fecharia a guia.

Pior ainda, se uma instrução narrada ficar fora de sincronia com a realidade da interação em si — uma inconsistência invisível que não seria detectada por um processo de controle de qualidade sem testes de leitor de tela dedicado — poderíamos acabar fazendo o usuário enfrentar uma discussão entre a página subjacente e seu próprio leitor de tela enquanto ele está apenas tentando fazer as coisas e continuar com seu dia.

ARIA é algo poderoso. Isso nos dá a capacidade de definir os significados, estados e relacionamentos entre os elementos em uma página como absolutos e rígidos fato — ele fornece uma linha de comunicação entre aqueles de nós que estão construindo a web e aqueles de nós que a usam. Em nenhum lugar essa linha de comunicação é mais direta do que com ariaNotify(), um recurso que efetivamente nos permite falar diretamente para um usuário final usando a voz do navegador e a tecnologia assistiva que ele conhece e confia. É muita responsabilidade vinculada a um único método. Resolve um problema muito real, mas como tantas tecnologias: se não for usado com cuidado, pode causar outros tantos.

Estou animado com ariaNotify(), você sabe, de uma forma comedida e cautelosa. Finalmente, nos dá uma maneira de abordar um caso de uso que tem atormentado a web – e a mim, pessoalmente – há anos, de uma forma surpreendentemente fácil. Tão fácil, na verdade, que torna ariaNotify() um pouco perigoso.

Quer dizer, não para nós , certo? Porque todos nós vamos ser legal sobre isso, certo ?

Certo.


O canto da sereia de  ariaNotify() originalmente escrito à mão e publicado com amor em CSS-Tricks . Você realmente deveria receber o boletim informativo também.

Deixe um comentário

Seu email não será publicado. Campos obrigatórios marcados com *

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.