Programação Por Equipe DevBras

Capa do artigo: Componente pronto de interface: o que revisar para ele ser acessível de verdade

Componente pronto de interface: o que revisar para ele ser acessível de verdade

Revise um bloco pronto de login (shadcn/ui ou outra biblioteca) em 20 minutos: rótulo ligado ao campo, erro em texto, foco visível, contraste e ordem do Tab.

O painel da clínica ganhou um bloco de login copiado de uma biblioteca de componentes: alinhado, com a fonte certa, pronto em dez minutos. Na primeira semana, uma recepcionista que usa leitor de tela ligou dizendo que o sistema "tem dois campos sem nome", e um paciente com baixa visão não conseguia ver qual campo estava selecionado. A resposta curta: componente pronto entrega o desenho e uma base técnica; o nome de cada campo, a mensagem de erro e o contraste da sua instância continuam sendo seu trabalho. Revise cinco coisas antes de publicar: rótulo ligado, erro em texto amarrado ao campo, foco visível, contraste e ordem do Tab.

Este guia faz essa revisão no bloco de login da clínica, mostrando o que costuma vir faltando e a correção em JSX.

Por que o pacote não resolve sozinho

Bibliotecas como o shadcn/ui partem de primitivos que já tratam bem muita coisa difícil: foco dentro de um diálogo, navegação por setas num menu, atributos de estado num checkbox. Isso é valioso e você não deve reescrever. Mas o bloco de exemplo é um modelo para ser adaptado, e no shadcn/ui o código é copiado para o seu projeto, não fica num pacote fechado. A partir da cópia, cada mudança é sua.

E as mudanças acontecem. Alguém troca o rótulo por um placeholder para "economizar espaço". Alguém apaga o anel de foco porque achou feio. O tema da marca troca o cinza do texto auxiliar por um mais claro. O botão "Entrar com Google" vira só o logotipo. Nenhuma dessas mudanças quebra o visual, e todas quebram o uso para alguém.

Há ainda o que nenhum componente pode adivinhar: o texto do erro. A biblioteca não sabe que, na clínica, "credenciais inválidas" deve virar "E-mail ou senha não conferem. Confira se o Caps Lock está ligado."

A revisão em cinco pontos

  1. Rótulo ligado ao campo. Cada campo tem um label visível cujo htmlFor aponta para o id do campo. Clicar no texto do rótulo deve colocar o cursor no campo.
  2. Erro em texto, amarrado ao campo. A mensagem aparece em texto, perto do campo, ligada a ele por aria-describedby, e o campo recebe aria-invalid. Borda vermelha sozinha não conta.
  3. Foco visível. Navegando com Tab, você sempre sabe onde está, em tema claro e escuro.
  4. Contraste. Texto comum com pelo menos 4,5:1 contra o fundo; contorno de campo e indicador de foco com pelo menos 3:1, que são as referências da WCAG.
  5. Ordem do Tab igual à ordem visual, e todo botão com nome que diz o que faz, inclusive os que só têm ícone.

O bloco antes da revisão

É uma versão comum depois de alguns ajustes "de design":

<form onSubmit={entrar}>
  <Input type="email" placeholder="E-mail" className={erro ? "border-red-500" : ""} />
  <Input type="password" placeholder="Senha" className={erro ? "border-red-500" : ""} />
  <Button type="submit" className="focus-visible:ring-0">Continuar</Button>
  <Button variant="outline" type="button" onClick={entrarComGoogle}>
    <GoogleIcon />
  </Button>
</form>

O leitor de tela anuncia os campos pelo placeholder em alguns casos e em outros não; o placeholder some assim que a pessoa digita; o erro é só a borda; o anel de foco foi zerado; o segundo botão não tem nome nenhum.

O mesmo bloco depois

<form onSubmit={entrar} noValidate>
  <div>
    <Label htmlFor="email">E-mail</Label>
    <Input
      id="email"
      type="email"
      autoComplete="email"
      aria-invalid={erro ? true : undefined}
      aria-describedby={erro ? "login-erro" : undefined}
    />
  </div>
  <div>
    <Label htmlFor="senha">Senha</Label>
    <Input
      id="senha"
      type="password"
      autoComplete="current-password"
      aria-invalid={erro ? true : undefined}
      aria-describedby={erro ? "login-erro" : undefined}
    />
  </div>
  <p id="login-erro" role="alert">{erro}</p>
  <Button type="submit">Entrar no painel</Button>
  <Button variant="outline" type="button" onClick={entrarComGoogle}>
    <GoogleIcon aria-hidden="true" />
    Entrar com Google
  </Button>
</form>

O que mudou: cada campo tem rótulo ligado; autoComplete deixa o gerenciador de senhas preencher; o erro é texto, com role="alert" para ser anunciado quando aparece, e os campos apontam para ele; o anel de foco do tema voltou porque a classe que o zerava saiu; o botão do Google tem texto e o ícone ficou escondido da árvore de acessibilidade, já que repetiria a informação. O parágrafo de erro fica sempre na página, vazio quando não há erro, porque uma região de alerta inserida junto com o texto pode não ser anunciada por todos os leitores de tela.

Contraste: confira o tema, não o exemplo

O tema padrão da biblioteca costuma passar nos testes de contraste. O seu tema, depois que a marca da clínica entrou, é outra história. Abra as ferramentas do navegador, inspecione o texto auxiliar ("Esqueceu a senha?") e veja a razão de contraste que o inspetor mostra. Faça o mesmo com o contorno dos campos, que precisa ser distinguível do fundo. No painel da clínica, o cinza do texto auxiliar estava em 3,1:1 e subiu para 5,2:1 com um tom mais escuro, sem ninguém da equipe notar diferença estética.

O teste que pega quase tudo em cinco minutos

Recarregue a página e, sem tocar no mouse, aperte Tab. O foco deve ir para o e-mail, depois senha, depois "Entrar no painel", depois "Entrar com Google", nessa ordem, sempre visível. Envie com a senha errada: o erro deve aparecer em texto e ser lido pelo leitor de tela (NVDA no Windows e VoiceOver no Mac são gratuitos). Depois ative o leitor e passe pelos campos: cada um deve ser anunciado com o nome. Por fim, aumente o zoom do navegador para 200%: nada pode ficar cortado ou sobreposto.

Se você quer ir além do login, os mesmos cinco pontos valem para qualquer bloco copiado: tabela de dados, diálogo de confirmação, menu de usuário. O post sobre formulário de login usável no teclado aprofunda o lado da experiência do login em si.

Erros comuns

  • Placeholder no lugar do rótulo. O texto some quando a pessoa digita, e o campo fica sem nome para quem revisa ou usa leitor de tela.
  • Erro indicado só por cor. Quem não distingue vermelho, ou não vê a tela, não sabe que algo deu errado nem o quê.
  • Zerar o anel de foco para "ficar limpo". Quem navega por teclado fica cego na página; veja como manter o foco visível no tema escuro.
  • Botão só com ícone. O leitor anuncia "botão" sem nome; o mesmo vale para emoji, como explica o post sobre emoji que não substitui o rótulo.
  • Trocar o cinza do tema sem medir. O texto auxiliar fica ilegível em tela com brilho baixo e em quem tem visão reduzida.
  • Reordenar com CSS sem mudar o HTML. A tela mostra uma ordem e o Tab percorre outra, e a pessoa pula campos sem perceber.

Checklist

  • Todo campo tem rótulo visível ligado por htmlFor e id.
  • Mensagens de erro são texto, ligadas ao campo por aria-describedby.
  • Campos com erro têm aria-invalid.
  • O foco é visível em todos os elementos, nos temas claro e escuro.
  • Texto tem contraste de pelo menos 4,5:1 e contornos de pelo menos 3:1.
  • Botões com ícone têm texto visível ou nome acessível.
  • A ordem do Tab segue a ordem visual.
  • A página funciona com zoom de 200%.

A biblioteca encurtou o caminho até um login bonito, e isso é ótimo. O último trecho, que é o nome, o erro e o foco da sua instância, só você pode percorrer. Abra agora o seu formulário de login, digite uma senha errada e desligue mentalmente a cor da tela: o erro desse formulário existe fora do vermelho?

Avalie este artigo

Referência: https://ui.shadcn.com/blocks/authentication