Seção 6

Formulário

Labels associados, ordem de navegação, sem mudanças automáticas, identificação de erros, agrupamento com fieldset/legend, formatos de campos e confirmação de envio.

6.1

Fornecer alternativa em texto para botões de imagem de formulários

WCAG A

O elemento <input type="image"> deve ter atributo alt descrevendo a função do botão. Melhor prática: usar <button> com texto e ícone.

WCAG 1.1.1
Incorreto
<!-- ❌ Botão de imagem sem alternativa -->
<input type="image" src="btn-buscar.png">
<input type="image" src="btn-login.png" alt="">
Correto
<!-- ✓ Alt descritivo ou button com texto -->
<input type="image"
  src="btn-buscar.png"
  alt="Buscar">

<!-- Ainda melhor: button com texto + ícone -->
<button type="submit">
  <svg aria-hidden="true" focusable="false">
    <!-- ícone de lupa -->
  </svg>
  Buscar
</button>
Dica

Prefira <button type="submit"> com texto visível em vez de <input type="image">. Textos em botões são mais acessíveis, pesquisáveis e traduzíveis que imagens.

6.2

Associar etiquetas aos seus campos correspondentes

WCAG A

Todo campo de formulário deve ter um <label> associado via atributo for/id, ou via aria-label/aria-labelledby. Placeholder não substitui label.

WCAG 1.3.1WCAG 3.3.2
Incorreto
<!-- ❌ Campos sem label adequado -->
<p>Nome: <input type="text" name="nome"></p>
<input type="email" placeholder="Digite seu email">
<span>Data de nascimento</span>
<input type="date" name="nascimento">
Correto
<!-- ✓ Labels explicitamente associados -->
<div class="campo">
  <label for="nome">
    Nome completo
    <span aria-label="obrigatório">*</span>
  </label>
  <input type="text" id="nome" name="nome"
    required
    autocomplete="name">
</div>

<div class="campo">
  <label for="email">E-mail</label>
  <input type="email" id="email" name="email"
    autocomplete="email"
    aria-describedby="email-dica">
  <p id="email-dica" class="instrucao">
    Exemplo: nome@orgao.gov.br
  </p>
</div>
Dica

Nunca use placeholder como substituto para label — o placeholder desaparece quando o usuário digita. Leitores de tela anunciam o label quando o campo recebe foco.

6.3

Estabelecer uma ordem lógica de navegação

WCAG A

A ordem de foco do teclado (Tab) deve seguir uma sequência lógica que preserve o significado e a operabilidade do formulário.

WCAG 2.4.3
Incorreto
<!-- ❌ tabindex desordenado -->
<input id="sobrenome" tabindex="2" name="sobrenome">
<input id="nome" tabindex="1" name="nome">
<button tabindex="5" type="submit">Enviar</button>
<input id="email" tabindex="3" name="email">
Correto
<!-- ✓ Ordem natural do DOM ou tabindex sequencial -->
<form>
  <label for="nome">Nome</label>
  <input type="text" id="nome" name="nome">

  <label for="sobrenome">Sobrenome</label>
  <input type="text" id="sobrenome" name="sobrenome">

  <label for="email">E-mail</label>
  <input type="email" id="email" name="email">

  <button type="submit">Enviar</button>
</form>
<!-- Sem tabindex customizado: ordem segue o DOM -->
Dica

A melhor prática é NÃO usar tabindex positivo — a ordem natural do DOM é suficiente se o HTML estiver bem estruturado. Use tabindex="0" para tornar elementos não interativos focáveis, e tabindex="-1" para gerenciar foco via JS.

6.4

Não provocar alteração automática de contexto ao focar em um elemento

WCAG A

Simplesmente focar em um campo ou mudar o valor de um select não deve causar ações automáticas (submissão, navegação, abertura de janela) sem aviso prévio.

WCAG 3.2.1WCAG 3.2.2
Incorreto
<!-- ❌ Select que navega automaticamente -->
<label for="estado">Estado</label>
<select id="estado" name="estado"
  onchange="window.location = this.value">
  <option value="/mt">Mato Grosso</option>
  <option value="/sp">São Paulo</option>
</select>
Correto
<!-- ✓ Usuário controla quando a ação acontece -->
<label for="estado">Estado</label>
<select id="estado" name="estado">
  <option value="">Selecione um estado</option>
  <option value="MT">Mato Grosso</option>
  <option value="SP">São Paulo</option>
</select>
<button type="submit">Ir para o estado selecionado</button>
Dica

Selects com onchange automático são especialmente problemáticos para usuários de teclado — cada tecla pressionada dispara a mudança antes do usuário terminar de escolher.

6.5

Identificar e descrever erros e sugerir correção

WCAG A

Erros de validação devem identificar o campo com problema, descrever o erro em texto e, quando possível, sugerir a correção. Mensagens devem ser anunciadas por leitores de tela.

WCAG 3.3.1WCAG 3.3.3
Incorreto
<!-- ❌ Erro genérico apenas visual -->
<div class="erro" style="color:red">
  Dados inválidos.
</div>
<input type="email" class="campo-erro"
  style="border:2px solid red">
Correto
<!-- ✓ Erro específico, acessível e com sugestão -->
<div role="alert" aria-live="polite" id="erros-formulario">
  <h3>Encontramos problemas no formulário:</h3>
  <ul>
    <li>
      <a href="#email">E-mail</a>:
      Formato inválido — use nome@dominio.com.br
    </li>
  </ul>
</div>

<div class="campo-formulario">
  <label for="email">E-mail</label>
  <input type="email" id="email"
    aria-invalid="true"
    aria-describedby="erro-email"
    value="email-invalido">
  <p id="erro-email" class="mensagem-erro" role="alert">
    <strong>Erro:</strong> E-mail inválido.
    Informe no formato nome@orgao.gov.br
  </p>
</div>
Dica

Use role="alert" ou aria-live="polite" para que leitores de tela anunciem os erros automaticamente. O sumário de erros no topo do formulário, com links para cada campo, é a melhor prática.

6.6

Agrupar campos de formulário com fieldset e legend

WCAG A

Grupos de campos relacionados (como radio buttons, checkboxes ou endereço) devem ser agrupados em <fieldset> com <legend> descritiva para dar contexto a cada campo.

WCAG 1.3.1
Incorreto
<!-- ❌ Radio sem agrupamento -->
<p>Sexo</p>
<input type="radio" name="sexo" value="m" id="m">
<label for="m">Masculino</label>
<input type="radio" name="sexo" value="f" id="f">
<label for="f">Feminino</label>
Correto
<!-- ✓ Radio agrupados com fieldset/legend -->
<fieldset>
  <legend>Sexo</legend>
  <div class="opcao">
    <input type="radio" name="sexo" value="m" id="sexo-m">
    <label for="sexo-m">Masculino</label>
  </div>
  <div class="opcao">
    <input type="radio" name="sexo" value="f" id="sexo-f">
    <label for="sexo-f">Feminino</label>
  </div>
  <div class="opcao">
    <input type="radio" name="sexo" value="n" id="sexo-n">
    <label for="sexo-n">Prefiro não informar</label>
  </div>
</fieldset>
Dica

Fieldset é obrigatório para radio buttons e checkboxes. Para outros grupos relacionados (como campos de endereço), fieldset com legend também melhora a compreensão. A legend é anunciada junto com cada campo pelo leitor de tela.

6.7

Identificar o formato correto dos campos de entrada

WCAG A

Quando um campo exige um formato específico (data, CEP, telefone, CPF), o formato esperado deve ser indicado antes do campo, não apenas no placeholder.

WCAG 3.3.2
Incorreto
<!-- ❌ Formato só no placeholder (some ao digitar) -->
<label for="cep">CEP</label>
<input type="text" id="cep" name="cep"
  placeholder="00000-000">
Correto
<!-- ✓ Formato e instrução visíveis -->
<div class="campo">
  <label for="cep">CEP</label>
  <input type="text" id="cep" name="cep"
    inputmode="numeric"
    pattern="[0-9]{5}-?[0-9]{3}"
    maxlength="9"
    autocomplete="postal-code"
    aria-describedby="cep-formato">
  <p id="cep-formato" class="instrucao-campo">
    Formato: 00000-000 (com ou sem hífen)
  </p>
</div>
Dica

Use tipos de input adequados (type="tel", "email", "date", "number") — o teclado mobile adapta automaticamente. O atributo inputmode refinado (numeric, decimal, tel) controla o teclado sem restringir a validação.

6.8

Possibilitar que o usuário revise e confirme dados antes do envio

WCAG AA

Formulários com consequências legais, financeiras ou irreversíveis devem permitir revisão e confirmação dos dados antes da submissão definitiva.

WCAG 3.3.4
Incorreto
<!-- ❌ Botão que envia imediatamente sem confirmação -->
<button type="submit"
  onclick="return confirm('Confirmar?')">
  Protocolar requerimento
</button>
Correto
<!-- ✓ Tela de confirmação com resumo dos dados -->
<section aria-labelledby="confirmar-titulo">
  <h2 id="confirmar-titulo">
    Confirme os dados do requerimento
  </h2>
  <dl>
    <dt>Tipo de requerimento:</dt>
    <dd>Solicitação de certidão negativa</dd>
    <dt>CPF/CNPJ:</dt>
    <dd>***.***.***-**</dd>
    <dt>Endereço de entrega:</dt>
    <dd>Rua das Flores, 123 — Cuiabá/MT</dd>
  </dl>
  <div class="acoes-confirmacao">
    <button type="button" onclick="voltarEditar()">
      Editar dados
    </button>
    <button type="submit" form="form-requerimento">
      Confirmar e protocolar
    </button>
  </div>
</section>
Dica

Use um fluxo de múltiplos passos (wizard) para formulários complexos. A tela de confirmação deve mostrar todos os dados que serão enviados e ter botão para editar. Após envio, ofereça número de protocolo e confirmação por email.

6.9

Identificar claramente o botão de envio do formulário

WCAG AA

O botão de envio deve ter texto claro que descreve a ação. Evite textos genéricos como "OK", "Enviar" isolado ou símbolos sem texto visível.

WCAG 2.4.6
Incorreto
<!-- ❌ Botão ambíguo ou só ícone -->
<input type="submit" value="OK">
<button type="submit">
  <img src="seta.png" alt="">
</button>
<button type="submit">>></button>
Correto
<!-- ✓ Botão com texto descritivo da ação -->
<!-- Formulário de contato -->
<button type="submit">
  Enviar mensagem de contato
</button>

<!-- Formulário de login -->
<button type="submit">
  Entrar no Portal
</button>

<!-- Com ícone complementar -->
<button type="submit">
  <svg aria-hidden="true" focusable="false">
    <!-- ícone de envelope -->
  </svg>
  Enviar solicitação
</button>
Dica

O texto do botão deve descrever o resultado da ação, não apenas o gesto. "Enviar formulário de inscrição" é melhor que "Enviar". Em formulários multi-passo, use "Próximo passo" e "Finalizar inscrição" em vez de "Avançar" genérico.