Tips & Tricks (Atualizado: 02/06/2026)

Acessibilidade com Claude Code: HTML semântico, ARIA, axe e revisão manual

Fluxo prático para melhorar acessibilidade com Claude Code: HTML, teclado, formulários, foco, axe e leitor de tela.

Acessibilidade com Claude Code: HTML semântico, ARIA, axe e revisão manual

Acessibilidade não é rodar uma ferramenta no fim do release. Ela começa com HTML semântico, passa por navegação por teclado, gerenciamento de foco, erros de formulário, contraste de cor e termina com testes automatizados e verificação manual com leitor de tela.

Claude Code acelera esse fluxo, mas somente quando o pedido é específico. Se você escrever “deixe acessível”, ele pode adicionar ARIA em um div sem implementar Enter ou Space, criar um modal bonito que deixa o foco escapar para o fundo ou exibir erros que o leitor de tela não anuncia.

As referências aqui são oficiais: W3C WCAG 2.2 como alvo prático, WAI-ARIA Authoring Practices Guide para padrões de widgets, MDN ARIA para a regra de HTML semântico primeiro, documentação axe-core da Deque para automação e documentação oficial do Claude Code para a ferramenta.

Defina o alvo antes do patch

“Melhorar acessibilidade” é amplo demais. Um alvo bom para Claude Code é: WCAG 2.2 AA como referência, HTML semântico antes de ARIA, fluxo principal operável por teclado, foco visível, erros compreensíveis e evidência com teste automático e revisão manual.

ÁreaLinha mínimaFalha comum
HTML semânticoUsar button, a, form, label, main, nav corretamentediv clicável substitui controle nativo
TecladoTab, Shift+Tab, Enter, Space e Escape cobrem o fluxoModal fecha só com mouse
FocoFoco entra na UI nova e volta ao gatilho ao fecharFoco some atrás do diálogo
ARIAUsar apenas quando HTML não expressa o estadoaria-label mascara falta de label visível
CorTexto, controles, erros e foco têm contrasteErro depende só da cor vermelha
FormuláriosLabels, ajuda, estado inválido e erros ligados ao inputErro aparece, mas não é anunciado
Testesaxe mais teclado e leitor de telaZero violações automáticas vira falsa aprovação total

MDN recomenda usar elementos HTML nativos quando eles já têm a semântica e o comportamento necessários. Portanto, peça ao Claude Code para corrigir a estrutura antes de adicionar estados ARIA como aria-invalid, aria-expanded ou aria-modal.

Prompt seguro para Claude Code

Um prompt bom parece uma checklist de revisão. Ele define escopo, protege texto comercial e pede prova.

claude <<'PROMPT'
Scope:
- Review only src/components/CheckoutForm.tsx and its tests.
- Do not change pricing copy, analytics events, or unrelated styles.

Accessibility target:
- Use WCAG 2.2 AA as the practical target.
- Prefer semantic HTML before ARIA.
- Add ARIA only when native HTML cannot express the state.

Check these items:
- Labels, descriptions, required state, and validation errors.
- Keyboard operation with Tab, Shift+Tab, Enter, Space, and Escape.
- Focus order, visible focus, and focus return after closing UI.
- Color contrast and non-color error indicators.
- Automated axe check plus manual screen-reader notes.

Output:
- Findings first, with file and line references.
- Minimal patch.
- Commands to verify.
- Any remaining risk.
PROMPT

Findings first evita patch sem diagnóstico. Claude Code aponta o problema, depois gera a menor mudança possível. Isso protege páginas com preço, CTA, Gumroad e analytics. O mesmo hábito de escopo pequeno aparece em dicas de produtividade com Claude Code.

Caso 1: CTA de produto ou artigo

CTA é caminho de receita. Se a ação é navegação, use link real em vez de cartão inteiro com onclick.

<div class="hero-card" onclick="location.href='/en/products'">
  <div class="title">Claude Code Templates</div>
  <div class="button">Buy now</div>
</div>

Uma base melhor separa título, descrição e destino.

<section aria-labelledby="templates-heading" class="product-cta">
  <h2 id="templates-heading">Reduza revisões com templates Claude Code</h2>
  <p>
    Copie prompts reutilizáveis para implementação, revisão,
    debugging e documentação.
  </p>
  <a class="primary-link" href="/en/products">
    Ver recursos de produto
  </a>
</section>

Ao pedir a mudança, diga para preservar URLs, atributos de tracking e texto de conversão. Um patch acessível que quebra o funil não é bom para produção.

Caso 2: Formulário de contato ou consultoria

Formulários conectam acessibilidade e conversão. Se o usuário não entende o campo, o formato esperado ou o erro, ele não envia a solicitação.

import { FormEvent, useState } from "react";

type Errors = {
  name?: string;
  email?: string;
};

export function ConsultationForm() {
  const [errors, setErrors] = useState<Errors>({});

  function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const data = new FormData(event.currentTarget);
    const nextErrors: Errors = {};

    if (!String(data.get("name") || "").trim()) {
      nextErrors.name = "Informe seu nome.";
    }

    if (!String(data.get("email") || "").includes("@")) {
      nextErrors.email = "Informe um e-mail válido.";
    }

    setErrors(nextErrors);
  }

  return (
    <form aria-labelledby="consultation-title" onSubmit={handleSubmit} noValidate>
      <h2 id="consultation-title">Solicitação de consultoria</h2>

      <div className="field">
        <label htmlFor="name">Nome</label>
        <input
          id="name"
          name="name"
          autoComplete="name"
          aria-invalid={errors.name ? "true" : "false"}
          aria-describedby={errors.name ? "name-error" : undefined}
        />
        {errors.name && (
          <p id="name-error" role="alert">
            {errors.name}
          </p>
        )}
      </div>

      <div className="field">
        <label htmlFor="email">E-mail</label>
        <p id="email-help">Use um endereço em que possamos responder.</p>
        <input
          id="email"
          name="email"
          type="email"
          autoComplete="email"
          aria-invalid={errors.email ? "true" : "false"}
          aria-describedby={
            errors.email ? "email-help email-error" : "email-help"
          }
        />
        {errors.email && (
          <p id="email-error" role="alert">
            {errors.email}
          </p>
        )}
      </div>

      <button type="submit">Enviar solicitação</button>
    </form>
  );
}

A falha comum é exibir erro sem conectar ao input. Outra é referenciar sempre um ID de erro que ainda não existe. Ajuda fixa fica sempre ligada; erro só quando renderizado.

Caso 3: Modal, command palette e menu

Modal não é só overlay. O padrão Dialog Modal do WAI-ARIA exige foco dentro, Tab circulando internamente, Escape para fechar e retorno ao botão que abriu.

import { ReactNode, useEffect, useRef } from "react";

type ModalProps = {
  open: boolean;
  title: string;
  onClose: () => void;
  children: ReactNode;
};

const focusableSelector = [
  "a[href]",
  "button:not([disabled])",
  "input:not([disabled])",
  "select:not([disabled])",
  "textarea:not([disabled])",
  '[tabindex]:not([tabindex="-1"])',
].join(",");

export function AccessibleModal(props: ModalProps) {
  const { open, title, onClose, children } = props;
  const dialogRef = useRef<HTMLDivElement>(null);
  const previousFocusRef = useRef<HTMLElement | null>(null);

  useEffect(() => {
    if (!open) return;

    previousFocusRef.current = document.activeElement as HTMLElement;
    const focusable = dialogRef.current?.querySelectorAll<HTMLElement>(
      focusableSelector
    );
    focusable?.[0]?.focus();

    function onKeyDown(event: KeyboardEvent) {
      if (event.key === "Escape") onClose();
      if (event.key !== "Tab" || !dialogRef.current) return;

      const items = [...dialogRef.current.querySelectorAll<HTMLElement>(
        focusableSelector
      )];
      const first = items[0];
      const last = items[items.length - 1];

      if (event.shiftKey && document.activeElement === first) {
        event.preventDefault();
        last?.focus();
      } else if (!event.shiftKey && document.activeElement === last) {
        event.preventDefault();
        first?.focus();
      }
    }

    document.addEventListener("keydown", onKeyDown);
    return () => {
      document.removeEventListener("keydown", onKeyDown);
      previousFocusRef.current?.focus();
    };
  }, [open, onClose]);

  if (!open) return null;

  return (
    <div className="modal-backdrop">
      <div
        ref={dialogRef}
        role="dialog"
        aria-modal="true"
        aria-labelledby="modal-title"
        className="modal-panel"
      >
        <h2 id="modal-title" tabIndex={-1}>
          {title}
        </h2>
        {children}
        <button type="button" onClick={onClose}>
          Fechar
        </button>
      </div>
    </div>
  );
}

Para dropdowns, confirme se o padrão ARIA é mesmo necessário. O Menu Button Pattern serve para menus de comando. Navegação comum costuma funcionar melhor com nav e links.

Cor, foco e mobile

Regressões aparecem quando o design remove outline, usa cinza claro ou mostra erro só pela cor. Mantenha foco visível e indicador não baseado em cor.

.primary-link {
  background: #0f766e;
  border-radius: 6px;
  color: #ffffff;
  display: inline-flex;
  font-weight: 700;
  min-height: 44px;
  padding: 0.75rem 1rem;
}

.primary-link:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 3px solid #f59e0b;
  outline-offset: 3px;
}

.field [role="alert"] {
  border-left: 4px solid #b91c1c;
  color: #7f1d1d;
  margin-top: 0.5rem;
  padding-left: 0.75rem;
}

Cheque também mobile: botão de fechar pequeno, foco escondido sob header fixo e CTA estreito são falhas comuns.

axe e revisão manual

axe encontra problemas estruturais rapidamente, mas não prova que o texto faz sentido ou que a ordem de leitura ajuda no fluxo real.

npm install -D @axe-core/playwright @playwright/test
npx playwright install --with-deps chromium
import AxeBuilder from "@axe-core/playwright";
import { expect, test } from "@playwright/test";

test("consultation form has no serious accessibility issues", async ({ page }) => {
  await page.goto("/contact");

  const results = await new AxeBuilder({ page })
    .include("main")
    .withTags(["wcag2a", "wcag2aa", "wcag22aa"])
    .analyze();

  expect(results.violations).toEqual([]);
});

Na revisão manual, comece por CTA de produto, formulário, checkout, modal, navegação e recuperação de erro. NVDA no Windows e VoiceOver no macOS são pontos de partida realistas.

Falhas concretas para procurar

  • role="button" sem Enter e Space.
  • Botão só com ícone e sem nome acessível.
  • alt="image" ou texto alternativo inútil.
  • aria-hidden="true" escondendo modal ou live region.
  • Erro visível sem aria-describedby.
  • Foco removido sem substituto.
  • Modal fecha sem devolver foco ao gatilho.
  • Teste automático cobre só página marketing, não o formulário real.

Use essa lista como prompt de revisão depois do patch.

CTA e resultado verificado

Para transformar isso em rotina, veja os produtos Claude Code. Para prompts repetíveis de review e debugging, use os 50 templates de prompts Claude Code. Para equipe com permissões, hooks e recibos de verificação, consultoria é o próximo passo.

Nesta atualização, testei três falhas reais: CTA em div clicável, formulário com erro não anunciado e modal sem retorno de foco. O melhor fluxo foi pedir findings primeiro, patch mínimo depois e checar teclado mais VoiceOver. axe ajudou na estrutura, mas a clareza do fluxo exigiu revisão humana.

#Claude Code #accessibility #WCAG #a11y #React
Grátis

PDF grátis: cheatsheet do Claude Code

Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.

Cuidamos dos seus dados e não enviamos spam.

Masa

Sobre o autor

Masa

Engenheiro focado em workflows práticos com Claude Code.