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

Ilustração de um formulário HTML com campos de texto, seleção e botão de envio

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çaPapel
<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> sem name aparece 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>

AtributoO que fazExemplo
actionURL que receberá os dados. Se omitido, envia para a própria páginaaction="/contato"
methodMétodo HTTP: get (padrão) ou postmethod="post"
enctypeCodificação do corpo no envio via postenctype="multipart/form-data"
autocompletePermite (on, padrão) ou não (off) o preenchimento automáticoautocomplete="on"
novalidateDesativa a validação nativa do navegador ao enviar<form novalidate>
targetOnde exibir a resposta: _self (padrão), _blank, _parent, _top ou o name de um <iframe>target="_blank"
nameNome do formulário, útil para scriptsname="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: post nã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 pares nome=valor sã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:

  1. Acessibilidade: leitores de tela anunciam o rótulo quando o campo recebe foco.
  2. 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.
  3. 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: placeholder nã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. Use placeholder apenas 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

Dados pessoais

💡 Dica: o atributo disabled em 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

AtributoFunção
typeTipo do controle (text, email, date, checkbox…)
nameNome do campo no envio. Sem ele, o valor não é enviado
idIdentificador único, usado pelo for do <label>
valueValor inicial do campo (ou o valor enviado, em radio e checkbox)
placeholderTexto de exemplo exibido enquanto o campo está vazio
requiredTorna o preenchimento obrigatório
autocompleteIndica ao navegador qual dado sugerir (name, email, tel…)
autofocusColoca o foco no campo ao carregar a página. Use com moderação
disabledDesativa o campo. Campos desativados não são enviados
readonlyImpede a edição, mas o valor é enviado

📌 Importante: a diferença entre disabled e readonly confunde muita gente. Se o valor precisa chegar ao servidor, mas o usuário não pode alterá-lo, use readonly.


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 maxlength baixo 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

Formato: (DDD) número, ex.: (11) 91234-5678

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 step padrão é 1, ou seja, números decimais são recusados. Para aceitar centavos, use step="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. Prefira type="text" com inputmode="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:

TipoCapturaValor enviado
dateDia, mês e ano2026-09-26
timeHora e minuto14:30
datetime-localData e hora, sem fuso horário2026-09-26T14:30
monthMês e ano2026-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

Forma de contato preferida

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

Assuntos de interesse

📌 Importante: checkbox desmarcado não é enviado: o campo simplesmente não aparece nos dados. Se nenhum value for definido, um checkbox marcado envia on. Em PHP, para receber várias opções com o mesmo nome como lista, use name="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" e enctype="multipart/form-data". E o accept é 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

  • selected define a opção escolhida inicialmente.
  • Com required, uma primeira opção com value="" 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).

  • rows define a altura visível, em linhas;
  • cols define a largura visível, em caracteres (na prática, a largura costuma ser controlada via CSS);
  • minlength e maxlength limitam 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 o placeholder deixa 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:

typeComportamento
submitEnvia o formulário. É o padrão de um <button> dentro de <form>
resetRestaura todos os campos aos valores iniciais
buttonNã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> sem type dentro de um formulário envia o formulário ao ser clicado. Sempre declare type="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.

AtributoRegraExemplo
requiredCampo obrigatório<input required>
typeFormato do tipo (email, url, number…)<input type="email">
minlength / maxlengthTamanho mínimo e máximo do textominlength="3"
min / maxMenor e maior valor (números e datas)min="18"
stepIncremento válidostep="0.01"
patternExpressão regular que o valor inteiro deve atenderpattern="[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 pattern precisa corresponder ao valor inteiro, não a um trecho: [a-z]{3,15} recusa Ana por causa da letra maiúscula.
  • O texto do atributo title aparece junto da mensagem de erro em vários navegadores. Use-o para explicar o formato esperado.
  • pattern e minlength só são verificados quando o campo tem algum valor. Para impedir campo vazio, combine com required.
  • No CSS, as pseudoclasses :invalid, :valid e :user-invalid permitem destacar campos com erro. A :user-invalid só é 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:

  1. Todo campo tem um <label> visível associado por for/id. Nada de usar só placeholder.
  2. Agrupe radio e checkbox com <fieldset> e <legend> para que a pergunta seja anunciada junto com as opções.
  3. Use o type correto. Ele define o teclado exibido no celular e a validação aplicada. Para códigos numéricos, use inputmode="numeric".
  4. Use autocomplete com 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.
  5. 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.
  6. 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>
  1. 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.
  2. Mantenha a ordem lógica. A ordem do HTML define a navegação com a tecla Tab. Evite tabindex positivo.
  3. 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)

Campos marcados com * são obrigatórios.

Dados pessoais
Formato: (DDD) número
Acesso
Mínimo de 8 caracteres.
Preferências
Como prefere receber novidades?


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:

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> com action, method e enctype define 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> com type explí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

Comentários

Seja o primeiro a comentar!

Deixe seu comentário