Web Accessibility (a11y)

· Padrão Frontend
Juan Kalleo
Juan Kalleo
Senior Fullstack
This page hasn't been translated to English yet — showing the original Portuguese content.

Práticas e padrões (como atributos ARIA) que garantem o uso do site por pessoas com deficiências visuais ou motoras — navegação por teclado sem mouse, leitores de tela anunciando o que está na tela, foco visível e previsível, contraste de cor suficiente.

No padrão frontend

Feedback de sucesso/erro e confirmação usam SweetAlert2, que já cuida de role/aria-live/foco sozinho — o código-fonte da lib instalada define role="alert"/aria-live="polite" pros toasts, role="dialog"/ aria-live="assertive" pros diálogos normais:

// node_modules/sweetalert2/dist/sweetalert2.js
popup.setAttribute('role', params.toast ? 'alert' : 'dialog');
popup.setAttribute('aria-live', params.toast ? 'polite' : 'assertive');

Todo campo de texto do padrão associa label/htmlFor corretamente via FormField (ver Formulários).

Regra do padrão pra <dialog> nativo

Quando um componente precisa de um <dialog> HTML nativo em vez de SweetAlert2 (caso do modal de recorte de imagem, shared/ui/sistema/image-cropper-modal.tsx), a regra do padrão é: abrir sempre via .showModal(), nunca só com o atributo open. Só showModal() coloca o elemento no top layer do navegador — o que dá foco preso, fechamento no Esc e role="dialog" implícito de graça. Um <dialog open> sem showModal() continua um elemento de fluxo comum: visualmente pode ficar idêntico (o DaisyUI estiliza via seletor CSS puro em cima do atributo open), mas sem nenhum dos comportamentos de acessibilidade que um diálogo modal precisa ter.

Implementação de referência:

const dialogRef = useRef<HTMLDialogElement>(null);

useLayoutEffect(() => {
  const dialog = dialogRef.current;
  if (!dialog || !imageSrc) return;
  if (!dialog.open) dialog.showModal();
  dialog.addEventListener("close", onCancelar);
  return () => dialog.removeEventListener("close", onCancelar);
}, [imageSrc]);

// ...
<dialog ref={dialogRef} className="modal" aria-labelledby="image-cropper-titulo">
  <div className="modal-box max-w-lg">
    <h3 id="image-cropper-titulo">Posicionar imagem</h3>

useLayoutEffect (não useEffect) garante que showModal() roda antes do primeiro paint. aria-labelledby dá nome acessível ao diálogo, e o listener de close sincroniza o Esc (que fecha o <dialog> sozinho, sem passar pelos botões React) de volta pro estado do formulário.

Fora do escopo desta página

Ainda não fazem parte do padrão documentado: contraste de cor mínimo sob tema customizado (cores de borda/texto configuráveis em Configuração institucional), navegação por teclado ponta a ponta em tabela com ordenação por coluna, e aria-sort no cabeçalho de tabela ordenável.