Por que formulários merecem atenção
Os formulários são onde os usuários pagam, se cadastram, pesquisam e enviam mensagens. Eles também são onde a frustração se concentra: um label ausente, um erro confuso ou um input que abre o teclado errado podem afastar um usuário em segundos.
A boa notícia é que o HTML já resolve a maior parte disso. Com labels adequados, os tipos de input corretos e restrições nativas, você consegue formulários acessíveis, amigáveis para dispositivos móveis e validados antes mesmo de escrever uma linha de JavaScript.
Estrutura do formulário
Um formulário envolve seus controles e declara onde e como enviá-los.
<!-- signup.html -->
<form action="/signup" method="post">
<label for="email">Email address</label>
<input id="email" name="email" type="email" autocomplete="email" required />
<label for="password">Password</label>
<input id="password" name="password" type="password" minlength="8" required />
<button type="submit">Create account</button>
</form>
- action é a URL que recebe os dados.
- method é
getoupost. - name em cada controle é a chave nos dados enviados.
- type=“submit” dispara o envio; um botão simples dentro de um formulário assume o comportamento de submit por padrão, portanto, use
type="button"para outras ações.
Labels
Todo controle precisa de um label. O label é anunciado por leitores de tela, serve como um alvo de clique maior e permanece visível após o campo ser preenchido.
<!-- label.html -->
<label for="phone">Phone number</label>
<input id="phone" name="phone" type="tel" autocomplete="tel" />
Você também pode envolver o controle dentro do label, o que os associa implicitamente:
<label>
Subscribe to the newsletter
<input type="checkbox" name="subscribe" />
</label>
Placeholders não são labels. Eles desaparecem assim que o usuário digita, geralmente possuem baixo contraste e não são anunciados de forma confiável. Use um label real e, se for útil, um hint.
Tipos de input
Escolher o type correto fornece aos usuários o teclado móvel adequado e habilita a validação nativa.
| Tipo | Use para | Benefício |
|---|---|---|
| Endereços de e-mail | Teclado de e-mail, verificação de formato | |
| tel | Números de telefone | Teclado numérico |
| number | Valores numéricos | Entrada numérica, min/max/step |
| url | Endereços web | Teclado de URL, verificação de formato |
| date | Datas | Seletor de data |
| password | Segredos | Entrada mascarada |
| search | Campos de busca | Teclado de busca, botão de limpar |
| file | Uploads | Seletor de arquivos e filtragem de tipo |
Use autocomplete com tokens padrão como email, given-name, postal-code e new-password para que os navegadores e gerenciadores de senhas possam ajudar.
Agrupando controles
Agrupe controles relacionados para que a relação entre eles fique clara para todos.
<!-- address.html -->
<fieldset>
<legend>Shipping address</legend>
<label for="street">Street</label>
<input id="street" name="street" autocomplete="address-line1" />
<label for="city">City</label>
<input id="city" name="city" autocomplete="address-level2" />
</fieldset>
fieldset e legend agrupam radio buttons e campos relacionados, que são anunciados pelos leitores de tela como um conjunto. Para grupos de radio buttons, use sempre um fieldset com um legend.
Validação nativa
O HTML oferece validação de restrições (constraint validation) por meio de atributos.
<!-- validate.html -->
<input type="email" name="email" required />
<input type="password" name="password" minlength="8" required />
<input type="number" name="age" min="18" max="120" step="1" />
<input type="text" name="handle" pattern="[a-z0-9_]{3,15}" />
<input type="url" name="website" />
O navegador bloqueia o envio e exibe uma mensagem quando uma restrição falha. Você pode estilizar esses estados com CSS:
/* styles.css */
input:invalid { border-color: #dc2626; }
input:valid { border-color: #16a34a; }
input:focus-visible { outline: 2px solid #2563eb; }
As mensagens nativas são funcionais, porém sucintas e não traduzidas. Para formulários importantes, forneça suas próprias mensagens claras utilizando a Constraint Validation API:
// validate.js
const email = document.querySelector("#email");
email.addEventListener("invalid", () => {
email.setCustomValidity("");
if (email.validity.valueMissing) {
email.setCustomValidity("Please enter your email address.");
} else if (email.validity.typeMismatch) {
email.setCustomValidity("That does not look like an email address.");
}
});
email.addEventListener("input", () => email.setCustomValidity(""));
Lembre-se de limpar a mensagem personalizada durante a digitação (input), caso contrário, o campo permanecerá inválido.
Erros acessíveis
A validação só é útil se os usuários entenderem o que deu errado.
- Exiba uma mensagem de texto clara ao lado do campo, não apenas uma borda vermelha.
- Associe a mensagem ao controle utilizando
aria-describedby. - Marque o campo com
aria-invalid="true". - Mova o foco para o primeiro campo inválido ao enviar o formulário.
- Resuma os erros no topo em formulários longos.
<!-- error.html -->
<label for="email">Email address</label>
<input id="email" name="email" type="email" required
aria-invalid="true" aria-describedby="email-error" />
<p id="email-error" class="error">Please enter a valid email address.</p>
Nunca dependa apenas da cor; combine-a com texto e um ícone.
Sempre valide no servidor
A validação no lado do cliente é um recurso de experiência do usuário, não um controle de segurança. Qualquer pessoa pode editar a página, desativar o JavaScript ou enviar uma requisição diretamente. O servidor deve validar tipos, comprimentos, formatos e permissões, e retornar erros claros que o cliente possa exibir. Consulte o guia de Web Security.
Melhores práticas
- Atribua a cada controle um label visível e associado.
- Use o tipo de input correto e tokens
autocomplete. - Agrupe controles relacionados com
fieldsetelegend. - Prefira constraints nativas e, em seguida, aprimore-as com a Constraint Validation API.
- Exiba mensagens de erro acessíveis e específicas próximas ao campo.
- Valide novamente no servidor em cada requisição.
- Mantenha os formulários curtos e solicite apenas o que for necessário.
Erros comuns
- Usar placeholders em vez de labels.
- Esquecer os atributos
name, fazendo com que os campos não sejam enviados. - Enviar dados sensíveis via GET.
- Confiar apenas na validação no client-side para segurança.
- Comunicar erros apenas através de cores.
- Resetar todo o formulário ao ocorrer um erro de validação, resultando na perda dos dados inseridos.
Próximos passos
Formulários combinam marcação, acessibilidade e validação. Reforce a marcação com Semantic HTML, torne-a inclusiva com o Guia de Acessibilidade e gerencie os envios com JavaScript. Depois, faça uma auditoria em um formulário do seu app utilizando o checklist acima.