HTML: Formulários — Estrutura, Elementos, Atributos e Validação

Não existe aplicação web sem formulário. Login, cadastro, contato, busca, checkout: tudo que envolve o usuário enviar informações para um site passa por um <form>. Por isso, saber estruturar formulários HTML corretamente é uma das habilidades mais importantes para quem está começando no desenvolvimento web.
Neste tutorial, você vai aprender a montar um formulário do zero: o elemento <form> e seus atributos, a ligação entre <label> e campo, os principais tipos de <input>, <select>, <textarea>, botões, validação nativa do HTML5 e as boas práticas de acessibilidade que fazem diferença para qualquer pessoa preencher o formulário sem frustração.
📌 Pré-requisito: conhecer a estrutura básica de um documento HTML. Se ainda não conhece, comece por HTML: estrutura essencial — tutorial passo a passo.
O que é um formulário HTML?
Um formulário HTML é um conjunto de controles interativos (campos de texto, caixas de seleção, listas, botões) agrupados pelo elemento <form>. Quando o usuário envia o formulário, o navegador reúne os valores de cada campo em pares nome=valor e os envia para o endereço definido no próprio formulário.
Segundo a MDN (em inglês), o elemento <form> representa uma seção do documento que contém controles interativos para enviar informações. Três peças trabalham juntas em todo formulário:
| Peça | Papel |
|---|---|
<form> | Define para onde e como os dados serão enviados |
Controles (<input>, <select>, <textarea>…) | Capturam os dados do usuário |
<label> | Descreve cada controle para pessoas e tecnologias assistivas |
💡 Dica: só são enviados os campos que têm o atributo
name. Um<input>semnameaparece na tela, mas não vai para o servidor.
Estrutura básica: o elemento <form>
Todo formulário começa com a tag <form>, e todos os campos relacionados ficam dentro dela:
<form action="/contato" method="post">
<label for="nome">Nome:</label>
<input type="text" id="nome" name="nome">
<button type="submit">Enviar</button>
</form>
Ao clicar em Enviar, o navegador faz uma requisição POST para /contato com o corpo nome=Valor+digitado. Quem recebe e processa esses dados é o back-end (PHP, Node.js, Python, uma API etc.). O HTML apenas coleta e envia.
Principais atributos do <form>
| Atributo | O que faz | Exemplo |
|---|---|---|
action | URL que receberá os dados. Se omitido, envia para a própria página | action="/contato" |
method | Método HTTP: get (padrão) ou post | method="post" |
enctype | Codificação do corpo no envio via post | enctype="multipart/form-data" |
autocomplete | Permite (on, padrão) ou não (off) o preenchimento automático | autocomplete="on" |
novalidate | Desativa a validação nativa do navegador ao enviar | <form novalidate> |
target | Onde exibir a resposta: _self (padrão), _blank, _parent, _top ou o name de um <iframe> | target="_blank" |
name | Nome do formulário, útil para scripts | name="cadastro" |
GET ou POST?
get— os dados vão na URL (/busca?q=html). Use para consultas que podem ser compartilhadas, salvas nos favoritos ou repetidas sem efeito colateral, como uma busca ou um filtro.post— os dados vão no corpo da requisição. Use quando o envio cria ou altera algo (cadastro, login, compra) ou quando os dados são grandes ou sensíveis.
⚠️ Atenção:
postnão criptografa nada; ele apenas tira os dados da URL. O que protege senhas e dados pessoais no caminho até o servidor é o HTTPS.
Valores de enctype
application/x-www-form-urlencoded(padrão): os paresnome=valorsão codificados como na URL. Atende à maioria dos formulários de texto.multipart/form-data: obrigatório para upload de arquivos com<input type="file">. Sem ele, o servidor recebe só o nome do arquivo.text/plain: envia os dados quase sem codificação. Serve apenas para depuração, porque o servidor não consegue interpretá-lo de forma confiável.
Rótulos com <label>
O <label> é o texto que identifica um campo. Ele é essencial por três motivos:
- Acessibilidade: leitores de tela anunciam o rótulo quando o campo recebe foco.
- Usabilidade: clicar no rótulo coloca o foco no campo (ou marca o checkbox/radio), aumentando a área clicável, o que ajuda muito no celular.
- Clareza: o usuário sempre sabe o que precisa digitar, mesmo depois de começar a preencher.
Há duas formas de associar um rótulo a um campo:
<!-- Associação explícita: o "for" do label aponta para o "id" do campo -->
<label for="cidade">Cidade:</label>
<input type="text" id="cidade" name="cidade">
<!-- Associação implícita: o campo fica dentro do label -->
<label>
Estado:
<input type="text" name="estado">
</label>
A associação explícita (for + id) é a mais usada e a mais compatível. Lembre que o id deve ser único na página.
⚠️ Atenção:
placeholdernão substitui o<label>. O texto de exemplo some assim que o usuário digita, costuma ter baixo contraste e nem sempre é anunciado por leitores de tela. Useplaceholderapenas como dica complementar.
Agrupando campos com <fieldset> e <legend>
<fieldset> e <legend> são elementos (não atributos) que organizam o formulário em seções:
<fieldset>agrupa campos relacionados. Por padrão, o navegador desenha uma borda em volta do grupo.<legend>deve ser o primeiro filho do<fieldset>e define o título do grupo. Leitores de tela anunciam a legenda junto com os campos do grupo.
São especialmente importantes em grupos de radio e checkbox, onde a pergunta (“Forma de contato preferida”) precisa ficar associada a todas as opções.
<fieldset>
<legend>Dados pessoais</legend>
<label for="nome-completo">Nome completo:</label>
<input type="text" id="nome-completo" name="nome" autocomplete="name">
<label for="nascimento">Data de nascimento:</label>
<input type="date" id="nascimento" name="nascimento" autocomplete="bday">
</fieldset>
Resultado renderizado
💡 Dica: o atributo
disabledem um<fieldset>desativa todos os campos dentro dele de uma só vez.
O elemento <input> e seus atributos
O <input> é o controle mais versátil do HTML: o mesmo elemento vira campo de texto, e-mail, data, checkbox ou upload de arquivo, dependendo do valor do atributo type. Se type for omitido ou tiver um valor desconhecido, o navegador usa text.
<input> é um elemento vazio: não tem tag de fechamento.
Atributos comuns
| Atributo | Função |
|---|---|
type | Tipo do controle (text, email, date, checkbox…) |
name | Nome do campo no envio. Sem ele, o valor não é enviado |
id | Identificador único, usado pelo for do <label> |
value | Valor inicial do campo (ou o valor enviado, em radio e checkbox) |
placeholder | Texto de exemplo exibido enquanto o campo está vazio |
required | Torna o preenchimento obrigatório |
autocomplete | Indica ao navegador qual dado sugerir (name, email, tel…) |
autofocus | Coloca o foco no campo ao carregar a página. Use com moderação |
disabled | Desativa o campo. Campos desativados não são enviados |
readonly | Impede a edição, mas o valor é enviado |
📌 Importante: a diferença entre
disabledereadonlyconfunde muita gente. Se o valor precisa chegar ao servidor, mas o usuário não pode alterá-lo, usereadonly.
Campos de texto
Texto de uma linha: type="text"
É o campo padrão para nomes, endereços e textos curtos. Quebras de linha digitadas ou coladas são removidas do valor.
<label for="nome">Nome:</label>
<input type="text" id="nome" name="nome" maxlength="50"
placeholder="Ex.: Maria Silva" autocomplete="name" required>
Resultado renderizado
minlength e maxlength definem o número mínimo e máximo de caracteres.
Senha: type="password"
Oculta os caracteres digitados. Use o autocomplete certo para que gerenciadores de senha funcionem:
autocomplete="current-password"em telas de login;autocomplete="new-password"em telas de cadastro ou troca de senha.
<label for="senha">Senha:</label>
<input type="password" id="senha" name="senha" minlength="8"
autocomplete="current-password" required>
Resultado renderizado
⚠️ Atenção: evite
maxlengthbaixo em senhas (como 20). Senhas longas são mais seguras, e cortar o que o usuário digita só gera erro de login.
E-mail: type="email"
O navegador verifica se o valor tem formato de e-mail (usuario@dominio) antes de enviar. No celular, o teclado exibe o @ em destaque. Com o atributo multiple, o campo aceita vários e-mails separados por vírgula.
<label for="email">E-mail:</label>
<input type="email" id="email" name="email"
placeholder="voce@exemplo.com" autocomplete="email" required>
Resultado renderizado
Telefone: type="tel"
Exibe o teclado numérico no celular, mas não valida o formato sozinho, porque telefones variam muito entre países. Para exigir um padrão, use pattern:
<label for="telefone">Celular:</label>
<input type="tel" id="telefone" name="telefone"
placeholder="(11) 91234-5678" autocomplete="tel"
pattern="\(\d{2}\)\s?\d{4,5}-\d{4}">
<small>Formato: (DDD) número, ex.: (11) 91234-5678</small>
Resultado renderizado
URL: type="url"
Valida se o valor é uma URL absoluta, com protocolo (https://). Um valor como exemplo.com, sem protocolo, é recusado.
<label for="site">Site pessoal:</label>
<input type="url" id="site" name="site" placeholder="https://exemplo.com">
Resultado renderizado
Busca: type="search"
Funciona como um campo de texto, mas o navegador pode exibir um botão para limpar o conteúdo, e no celular a tecla Enter vira “Buscar”. Formulários de busca normalmente usam method="get", para que o resultado tenha uma URL compartilhável. O elemento semântico <search> pode envolver o formulário para indicar que ele é uma área de busca.
<search>
<form action="/busca" method="get">
<label for="q">Pesquisar no site:</label>
<input type="search" id="q" name="q" placeholder="Ex.: tabelas HTML">
<button type="submit">Buscar</button>
</form>
</search>
Resultado renderizado
Números, intervalos e datas
Número: type="number"
Aceita apenas valores numéricos e exibe setas para aumentar e diminuir. Os atributos min, max e step controlam o intervalo e o incremento.
<label for="quantidade">Quantidade:</label>
<input type="number" id="quantidade" name="quantidade" min="1" max="99" value="1">
<label for="preco">Preço (R$):</label>
<input type="number" id="preco" name="preco" min="0" step="0.01">
Resultado renderizado
📌 Importante: o
steppadrão é1, ou seja, números decimais são recusados. Para aceitar centavos, usestep="0.01"; para qualquer casa decimal,step="any".
💡 Dica: não use
type="number"para CPF, CEP, cartão ou telefone. Esses dados são códigos, não quantidades: zeros à esquerda somem e as setas não fazem sentido. Prefiratype="text"cominputmode="numeric", que exibe o teclado numérico no celular.
<label for="cep">CEP:</label>
<input type="text" id="cep" name="cep" inputmode="numeric"
pattern="\d{5}-?\d{3}" autocomplete="postal-code" placeholder="00000-000">
Controle deslizante: type="range"
Um controle deslizante para escolher um valor em um intervalo quando a precisão não é essencial (volume, nível de satisfação). Sem min e max, o intervalo padrão vai de 0 a 100.
<label for="satisfacao">Satisfação (0 a 10):</label>
<input type="range" id="satisfacao" name="satisfacao" min="0" max="10" step="1" value="5">
Resultado renderizado
Data e hora: date, time e datetime-local
Esses tipos abrem o seletor nativo de data e/ou hora do navegador. A tela mostra a data no formato do idioma do usuário (ex.: 26/09/2026), mas o valor enviado é sempre no padrão ISO:
| Tipo | Captura | Valor enviado |
|---|---|---|
date | Dia, mês e ano | 2026-09-26 |
time | Hora e minuto | 14:30 |
datetime-local | Data e hora, sem fuso horário | 2026-09-26T14:30 |
month | Mês e ano | 2026-09 |
<label for="data-entrega">Data de entrega:</label>
<input type="date" id="data-entrega" name="data_entrega" min="2026-01-01" max="2026-12-31">
<label for="horario">Horário da reunião:</label>
<input type="datetime-local" id="horario" name="horario">
Resultado renderizado
Opções de escolha: radio e checkbox
Botões de rádio: type="radio"
Permitem escolher apenas uma opção de um grupo. O que forma o grupo é o mesmo name em todos os botões. Cada botão precisa de um value, que é o que será enviado. checked define a opção marcada inicialmente.
<fieldset>
<legend>Forma de contato preferida</legend>
<input type="radio" id="contato-email" name="contato" value="email" checked>
<label for="contato-email">E-mail</label>
<input type="radio" id="contato-telefone" name="contato" value="telefone">
<label for="contato-telefone">Telefone</label>
<input type="radio" id="contato-whatsapp" name="contato" value="whatsapp">
<label for="contato-whatsapp">WhatsApp</label>
</fieldset>
Resultado renderizado
Com required em pelo menos um dos botões, o grupo inteiro se torna obrigatório.
Caixas de seleção: type="checkbox"
Permitem marcar zero, uma ou várias opções. Cada checkbox marcado envia o seu name=value.
<fieldset>
<legend>Assuntos de interesse</legend>
<input type="checkbox" id="int-html" name="interesses" value="html">
<label for="int-html">HTML</label>
<input type="checkbox" id="int-css" name="interesses" value="css">
<label for="int-css">CSS</label>
<input type="checkbox" id="int-js" name="interesses" value="javascript">
<label for="int-js">JavaScript</label>
</fieldset>
<input type="checkbox" id="termos" name="termos" value="aceito" required>
<label for="termos">Li e aceito os termos de uso</label>
Resultado renderizado
📌 Importante: checkbox desmarcado não é enviado: o campo simplesmente não aparece nos dados. Se nenhum
valuefor definido, um checkbox marcado enviaon. Em PHP, para receber várias opções com o mesmo nome como lista, usename="interesses[]".
Upload de arquivos: type="file"
Permite ao usuário selecionar arquivos do dispositivo. O atributo accept sugere os tipos aceitos (por extensão ou tipo MIME), e multiple permite selecionar mais de um arquivo.
<form action="/upload" method="post" enctype="multipart/form-data">
<label for="curriculo">Currículo (PDF ou Word):</label>
<input type="file" id="curriculo" name="curriculo"
accept=".pdf,.doc,.docx,application/pdf">
<label for="fotos">Fotos:</label>
<input type="file" id="fotos" name="fotos" accept="image/*" multiple>
<button type="submit">Enviar arquivos</button>
</form>
Resultado renderizado
⚠️ Atenção: o envio de arquivos exige
method="post"eenctype="multipart/form-data". E oaccepté só um filtro de conveniência: o usuário pode contorná-lo, então o servidor sempre deve validar tipo e tamanho do arquivo.
Outros tipos úteis de <input>
Cor: type="color"
Abre o seletor de cores do sistema. O valor é sempre hexadecimal com 6 dígitos, em minúsculas (ex.: #ff0000).
<label for="cor">Cor favorita:</label>
<input type="color" id="cor" name="cor" value="#2563eb">
Resultado renderizado
Campo oculto: type="hidden"
Envia um valor junto com o formulário sem exibi-lo, como a origem do cadastro ou o ID de um produto.
<input type="hidden" name="origem" value="newsletter-rodape">
⚠️ Atenção: “oculto” não significa “protegido”. Qualquer pessoa pode ver e alterar o valor pelas ferramentas do navegador. Nunca confie nele para preços, permissões ou dados sensíveis.
Sugestões com <datalist>
O <datalist> oferece uma lista de sugestões para um campo de texto, sem impedir que o usuário digite outro valor. O list do <input> aponta para o id do <datalist>.
<label for="linguagem">Linguagem favorita:</label>
<input type="text" id="linguagem" name="linguagem" list="linguagens">
<datalist id="linguagens">
<option value="HTML">
<option value="CSS">
<option value="JavaScript">
<option value="TypeScript">
</datalist>
Resultado renderizado
Listas de seleção com <select>
O <select> cria uma lista suspensa. Cada opção é um <option>, e o value da opção escolhida é o que vai para o servidor. O texto entre as tags é só o que o usuário vê.
<label for="escolaridade">Escolaridade:</label>
<select id="escolaridade" name="escolaridade" required>
<option value="">Selecione…</option>
<option value="medio">Ensino Médio</option>
<option value="superior" selected>Ensino Superior</option>
<option value="pos">Pós-graduação</option>
</select>
Resultado renderizado
selecteddefine a opção escolhida inicialmente.- Com
required, uma primeira opção comvalue=""funciona como “nenhuma escolha”: o formulário não é enviado enquanto o usuário não trocar de opção.
Agrupando opções com <optgroup>
Em listas longas, <optgroup> cria grupos com título não selecionável:
<label for="estado">Estado:</label>
<select id="estado" name="estado">
<optgroup label="Sudeste">
<option value="SP">São Paulo</option>
<option value="RJ">Rio de Janeiro</option>
</optgroup>
<optgroup label="Sul">
<option value="PR">Paraná</option>
<option value="RS">Rio Grande do Sul</option>
</optgroup>
</select>
Resultado renderizado
Seleção múltipla: multiple e size
O atributo multiple permite escolher várias opções, e size define quantas ficam visíveis ao mesmo tempo. No computador, o usuário segura Ctrl (Windows/Linux) ou Command (Mac) para marcar mais de uma.
<label for="tecnologias">Tecnologias que você usa:</label>
<select id="tecnologias" name="tecnologias" size="4" multiple>
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="js">JavaScript</option>
<option value="react">React</option>
<option value="astro">Astro</option>
</select>
Resultado renderizado
💡 Dica: muitos usuários não sabem usar Ctrl/Command para selecionar várias opções. Para poucas opções, um grupo de checkboxes costuma ser mais claro.
Texto longo com <textarea>
Para mensagens, comentários e descrições, use <textarea>, que aceita várias linhas. Diferente do <input>, ele tem tag de fechamento, e o valor inicial fica entre as tags (não existe atributo value).
rowsdefine a altura visível, em linhas;colsdefine a largura visível, em caracteres (na prática, a largura costuma ser controlada via CSS);minlengthemaxlengthlimitam o tamanho do texto.
<label for="mensagem">Mensagem:</label>
<textarea id="mensagem" name="mensagem" rows="5" cols="40"
maxlength="1000" placeholder="Escreva sua mensagem…" required></textarea>
Resultado renderizado
⚠️ Atenção: qualquer espaço ou quebra de linha entre
<textarea>e</textarea>vira conteúdo do campo, e oplaceholderdeixa de aparecer. Escreva as duas tags coladas quando o campo começar vazio.
Botões: <button> e <input type="submit">
Há duas formas de criar botões em formulários:
<input type="submit" value="Enviar">
<button type="submit">Enviar</button>
O <button> é mais flexível: aceita conteúdo HTML dentro (ícones, <strong>, <span>), enquanto o <input> aceita apenas texto no value. Em código novo, prefira <button>.
O comportamento do botão depende do type:
type | Comportamento |
|---|---|
submit | Envia o formulário. É o padrão de um <button> dentro de <form> |
reset | Restaura todos os campos aos valores iniciais |
button | Não faz nada sozinho; usado para acionar JavaScript |
<button type="submit">Enviar cadastro</button>
<button type="reset">Limpar</button>
<button type="button">Pré-visualizar</button>
Resultado renderizado
📌 Importante: como
submité o padrão, um<button>semtypedentro de um formulário envia o formulário ao ser clicado. Sempre declaretype="button"em botões que só executam scripts.
💡 Dica: botões de reset raramente ajudam: um clique acidental apaga tudo o que o usuário digitou. Só inclua esse botão se houver um motivo claro.
Um botão de envio também pode sobrescrever os atributos do formulário com formaction, formmethod e formnovalidate. Isso é útil, por exemplo, em um botão “Salvar rascunho” que envia para outro endereço sem validar os campos:
<button type="submit">Publicar</button>
<button type="submit" formaction="/rascunho" formnovalidate>Salvar rascunho</button>
Validação nativa do HTML5
Antes de enviar, o navegador verifica as regras declaradas nos campos. Se algo estiver errado, ele bloqueia o envio, coloca o foco no primeiro campo inválido e exibe uma mensagem, sem nenhuma linha de JavaScript.
| Atributo | Regra | Exemplo |
|---|---|---|
required | Campo obrigatório | <input required> |
type | Formato do tipo (email, url, number…) | <input type="email"> |
minlength / maxlength | Tamanho mínimo e máximo do texto | minlength="3" |
min / max | Menor e maior valor (números e datas) | min="18" |
step | Incremento válido | step="0.01" |
pattern | Expressão regular que o valor inteiro deve atender | pattern="[A-Z]{3}" |
Teste abaixo: clique em Validar com os campos vazios ou com um e-mail inválido.
<form>
<label for="usuario">Usuário (3 a 15 letras minúsculas):</label>
<input type="text" id="usuario" name="usuario"
pattern="[a-z]{3,15}" required
title="Use de 3 a 15 letras minúsculas, sem espaços">
<label for="email-validacao">E-mail:</label>
<input type="email" id="email-validacao" name="email" required>
<label for="idade">Idade:</label>
<input type="number" id="idade" name="idade" min="18" max="120" required>
<button type="submit">Validar</button>
</form>
Resultado renderizado
Alguns detalhes que evitam dor de cabeça:
- O
patternprecisa corresponder ao valor inteiro, não a um trecho:[a-z]{3,15}recusaAnapor causa da letra maiúscula. - O texto do atributo
titleaparece junto da mensagem de erro em vários navegadores. Use-o para explicar o formato esperado. patterneminlengthsó são verificados quando o campo tem algum valor. Para impedir campo vazio, combine comrequired.- No CSS, as pseudoclasses
:invalid,:valide:user-invalidpermitem destacar campos com erro. A:user-invalidsó é aplicada depois que o usuário interage com o campo, o que evita mostrar erro em um formulário que acabou de abrir.
⚠️ Atenção: a validação do navegador melhora a experiência, mas não é segurança. Qualquer pessoa pode desativá-la ou enviar dados direto para o servidor. Sempre valide novamente no back-end.
Para mensagens personalizadas e regras mais complexas (confirmar senha, CPF válido), use JavaScript. Veja como no nosso guia básico de validação de formulários com JavaScript.
Acessibilidade e boas práticas
Um formulário acessível é mais fácil de usar para todo mundo, não só para quem usa tecnologias assistivas. As recomendações a seguir seguem o tutorial de formulários do W3C WAI:
- Todo campo tem um
<label>visível associado porfor/id. Nada de usar sóplaceholder. - Agrupe radio e checkbox com
<fieldset>e<legend>para que a pergunta seja anunciada junto com as opções. - Use o
typecorreto. Ele define o teclado exibido no celular e a validação aplicada. Para códigos numéricos, useinputmode="numeric". - Use
autocompletecom valores específicos (name,email,tel,postal-code,street-address,new-password,one-time-code…). Isso reduz a digitação e atende ao critério 1.3.5 da WCAG. - Indique campos obrigatórios no texto, não só com cor. Um asterisco explicado (”* campos obrigatórios”) ou a palavra “(obrigatório)” no rótulo resolvem.
- Associe instruções e mensagens de erro ao campo com
aria-describedby, para que o leitor de tela leia a dica junto com o rótulo:
<label for="cpf">CPF (obrigatório):</label>
<input type="text" id="cpf" name="cpf" inputmode="numeric"
aria-describedby="cpf-ajuda" required>
<small id="cpf-ajuda">Somente números, 11 dígitos.</small>
- Peça só o necessário. Cada campo a mais reduz a taxa de conclusão. Se um dado é opcional, pergunte-se se ele precisa estar ali.
- Mantenha a ordem lógica. A ordem do HTML define a navegação com a tecla Tab. Evite
tabindexpositivo. - Use HTTPS em qualquer página com formulário, principalmente em login e pagamento.
💡 Dica: teste o formulário somente com o teclado (Tab, Shift+Tab, Espaço e Enter). Se não der para preencher e enviar tudo assim, há um problema de acessibilidade.
Exemplo completo: formulário de cadastro
Juntando tudo, veja um formulário de cadastro semântico, acessível e com validação nativa:
<form action="/cadastro" method="post">
<p>Campos marcados com * são obrigatórios.</p>
<fieldset>
<legend>Dados pessoais</legend>
<label for="cad-nome">Nome completo *</label>
<input type="text" id="cad-nome" name="nome" autocomplete="name" required>
<label for="cad-email">E-mail *</label>
<input type="email" id="cad-email" name="email" autocomplete="email" required>
<label for="cad-telefone">Celular</label>
<input type="tel" id="cad-telefone" name="telefone" autocomplete="tel"
placeholder="(11) 91234-5678" pattern="\(\d{2}\)\s?\d{4,5}-\d{4}"
aria-describedby="cad-telefone-ajuda">
<small id="cad-telefone-ajuda">Formato: (DDD) número</small>
<label for="cad-nascimento">Data de nascimento</label>
<input type="date" id="cad-nascimento" name="nascimento" autocomplete="bday">
</fieldset>
<fieldset>
<legend>Acesso</legend>
<label for="cad-senha">Senha *</label>
<input type="password" id="cad-senha" name="senha" minlength="8"
autocomplete="new-password" aria-describedby="cad-senha-ajuda" required>
<small id="cad-senha-ajuda">Mínimo de 8 caracteres.</small>
</fieldset>
<fieldset>
<legend>Preferências</legend>
<label for="cad-area">Área de interesse *</label>
<select id="cad-area" name="area" required>
<option value="">Selecione…</option>
<option value="frontend">Front-end</option>
<option value="backend">Back-end</option>
<option value="fullstack">Full stack</option>
</select>
<fieldset>
<legend>Como prefere receber novidades?</legend>
<input type="radio" id="cad-news-email" name="newsletter" value="email" checked>
<label for="cad-news-email">E-mail</label>
<input type="radio" id="cad-news-nao" name="newsletter" value="nao">
<label for="cad-news-nao">Não quero receber</label>
</fieldset>
<label for="cad-sobre">Conte um pouco sobre você</label>
<textarea id="cad-sobre" name="sobre" rows="4" maxlength="500"></textarea>
</fieldset>
<input type="hidden" name="origem" value="blog">
<input type="checkbox" id="cad-termos" name="termos" value="aceito" required>
<label for="cad-termos">Li e aceito os termos de uso *</label>
<button type="submit">Criar conta</button>
</form>
Resultado renderizado (os dados não são enviados neste exemplo)
Repare nas decisões aplicadas: cada grupo tem <fieldset> e <legend>, todo campo tem <label>, o autocomplete indica exatamente qual dado o navegador deve sugerir, as instruções estão ligadas aos campos por aria-describedby e o botão usa <button type="submit">. Para deixar o layout com um campo por linha, o próximo passo é o CSS.
Próximos passos
Com a estrutura HTML pronta, você pode evoluir o formulário em três frentes:
- Estilo: CSS: como estilizar formulários e seus atributos
- Validação avançada: Validação de formulários com JavaScript — o guia básico
- Framework: CSS: estilização e validação de formulários com Bootstrap 5
Também vale conhecer o guia de HTML5 semântico e o tutorial de tabelas HTML, muito usadas para exibir os dados que os formulários coletam.
Conclusão
Formulários HTML parecem simples, mas cada detalhe muda o resultado: o name define o que é enviado, o type define o teclado e a validação, o <label> torna o campo acessível e o method e o enctype determinam como os dados chegam ao servidor.
Resumindo o que você aprendeu:
<form>comaction,methodeenctypedefine o destino e o formato do envio;<label>,<fieldset>e<legend>dão significado e acessibilidade aos campos;- os tipos de
<input>, junto com<select>,<textarea>e<datalist>, cobrem praticamente qualquer dado; <button>comtypeexplícito evita envios acidentais;- a validação nativa (
required,pattern,min,max…) melhora a experiência, mas não dispensa a validação no servidor.
Ficou com alguma dúvida ou quer sugerir um tema? Fale com a gente pela página de contato.
Referências
- MDN Web Docs —
<form>: the Form element (em inglês) - MDN Web Docs —
<input>: o elemento de entrada - MDN Web Docs — HTML attribute:
autocomplete(em inglês) - MDN Web Docs — Validação de formulários no lado do cliente
- WHATWG — HTML Living Standard: Forms
- W3C WAI — Forms Tutorial
- W3C — WCAG 2.2: Understanding SC 1.3.5 Identify Input Purpose
Comentários
Seja o primeiro a comentar!
Deixe seu comentário