Aprenda a criar formulários funcionais e acessíveis usando inputs, labels, validação básica e as melhores práticas de experiência do usuário.
O elemento <form>
é o container fundamental que agrupa todos os campos
de entrada e define como os dados serão processados. Ele atua como um "envelope" que
organiza as informações do usuário e estabelece a comunicação com o servidor.
Por que é importante? Sem o form, os dados ficam soltos na página. Com ele, você cria um sistema organizado de coleta e envio de informações.
Conceito: Todo formulário precisa de três elementos essenciais: um container (form), campos de entrada (input) e rótulos (label). Vamos ver o exemplo mais simples possível:
<form action="/processar" method="post">
<!-- Label + Input = Dupla Perfeita -->
<label for="nome">Nome:</label>
<input type="text" id="nome" name="nome" required>
<label for="email">E-mail:</label>
<input type="email" id="email" name="email" required>
<!-- Botão para enviar os dados -->
<button type="submit">Enviar</button>
</form>
Entenda cada atributo: Cada atributo do form tem um propósito específico. Aqui você aprende quando e como usar cada um deles:
<form
action="/submit-contact" <!-- 🎯 Para onde enviar -->
method="post" <!-- 📦 Como enviar (GET/POST) -->
enctype="multipart/form-data" <!-- 📎 Para arquivos -->
autocomplete="on" <!-- 🤖 Preenchimento automático -->
novalidate="false" <!-- ✅ Habilitar validação -->
target="_self"> <!-- 🪟 Onde abrir resposta -->
<!-- Campo oculto: dados extras que o usuário não vê -->
<input type="hidden" name="form_id" value="contact_form_2024">
<!-- Seus campos visíveis vêm aqui -->
</form>
A Arte da Organização: Um formulário bem estruturado é como uma conversa organizada.
Use fieldset
e legend
para criar "capítulos" no seu formulário,
agrupando campos relacionados. Isso melhora tanto a experiência do usuário quanto a acessibilidade.
<form action="/contato" method="post" class="contact-form">
<!-- 📋 SEÇÃO 1: Dados Pessoais -->
<fieldset>
<legend>Informações Pessoais</legend>
<div class="form-group">
<label for="primeiro-nome">Primeiro Nome</label>
<input type="text"
id="primeiro-nome"
name="primeiro_nome"
required
autocomplete="given-name"
placeholder="Ex: João">
</div>
<div class="form-group">
<label for="sobrenome">Sobrenome</label>
<input type="text"
id="sobrenome"
name="sobrenome"
required
autocomplete="family-name"
placeholder="Ex: Silva">
</div>
<div class="form-group">
<label for="email-contato">E-mail</label>
<input type="email"
id="email-contato"
name="email"
required
autocomplete="email"
placeholder="seuemail@exemplo.com">
</div>
</fieldset>
<!-- 💬 SEÇÃO 2: Mensagem -->
<fieldset>
<legend>Mensagem</legend>
<div class="form-group">
<label for="assunto">Assunto</label>
<select id="assunto" name="assunto" required>
<option value="">Selecione um assunto</option>
<option value="suporte">Suporte Técnico</option>
<option value="vendas">Vendas</option>
<option value="geral">Informações Gerais</option>
</select>
</div>
<div class="form-group">
<label for="mensagem">Sua Mensagem</label>
<textarea id="mensagem"
name="mensagem"
rows="5"
required
placeholder="Digite sua mensagem aqui..."></textarea>
</div>
</fieldset>
<!-- 🎬 SEÇÃO 3: Ações -->
<div class="form-actions">
<button type="reset">Limpar</button>
<button type="submit">Enviar Mensagem</button>
</div>
</form>
Crie um formulário de cadastro de usuário com:
Cada tipo de input foi criado para um propósito específico. Usar o tipo correto oferece:
Textos são a base: A maioria dos dados que coletamos são textuais. Vamos dominar cada variação e quando usar cada uma:
<!-- 📝 Texto simples - O mais versátil -->
<label for="nome-usuario">Nome de Usuário</label>
<input type="text"
id="nome-usuario"
name="username"
maxlength="20" <!-- Máximo 20 caracteres -->
pattern="[a-zA-Z0-9]+" <!-- Apenas letras e números -->
title="Apenas letras e números" <!-- Tooltip de ajuda -->
placeholder="Digite seu username"
required>
<!-- 🔐 Senha - Segurança em primeiro lugar -->
<label for="senha">Senha</label>
<input type="password"
id="senha"
name="password"
minlength="8" <!-- Mínimo 8 caracteres -->
pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]"
title="Mínimo 8 caracteres, incluindo maiúscula, minúscula, número e símbolo"
required>
<!-- 📧 E-mail - Validação inteligente -->
<label for="email-comercial">E-mail Comercial</label>
<input type="email"
id="email-comercial"
name="business_email"
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$" <!-- Pattern extra -->
placeholder="contato@empresa.com"
autocomplete="work email" <!-- Autocomplete específico -->
required>
<!-- 🌐 URL - Para links e websites -->
<label for="website">Website da Empresa</label>
<input type="url"
id="website"
name="company_website"
placeholder="https://exemplo.com"
pattern="https://.*" <!-- Força HTTPS -->
title="URL deve começar com https://">
<!-- 📞 Telefone - Interface otimizada -->
<label for="telefone">Telefone</label>
<input type="tel"
id="telefone"
name="phone"
pattern="\([0-9]{2}\) [0-9]{4,5}-[0-9]{4}" <!-- Formato brasileiro -->
placeholder="(11) 99999-9999"
autocomplete="tel">
Números e datas merecem tratamento especial: Estes tipos oferecem controles visuais (spinner, calendário) e validação automática de ranges. No mobile, abrem teclados otimizados para facilitar a entrada de dados.
<!-- 🔢 Número inteiro - Idades, quantidades -->
<label for="idade">Idade</label>
<input type="number"
id="idade"
name="age"
min="18" <!-- Valor mínimo aceito -->
max="120" <!-- Valor máximo aceito -->
step="1" <!-- Incremento (inteiros) -->
placeholder="Ex: 25"
required>
<!-- 💰 Valor monetário - Com casas decimais -->
<label for="orcamento">Orçamento (R$)</label>
<input type="number"
id="orcamento"
name="budget"
min="0"
max="1000000"
step="0.01" <!-- Permite centavos -->
placeholder="0.00">
<!-- 🎚️ Range/Slider - Experiência visual -->
<label for="experiencia">Nível de Experiência (1-10)</label>
<input type="range"
id="experiencia"
name="experience_level"
min="1"
max="10"
value="5" <!-- Valor inicial -->
step="1"
oninput="this.nextElementSibling.value=this.value">
<output>5</output> <!-- Mostra valor atual -->
<!-- 📅 Data - Seletor nativo -->
<label for="nascimento">Data de Nascimento</label>
<input type="date"
id="nascimento"
name="birth_date"
min="1900-01-01" <!-- Data mínima -->
max="2006-12-31" <!-- Data máxima -->
autocomplete="bday" <!-- Autocomplete para aniversário -->
required>
<!-- ⏰ Hora - Para agendamentos -->
<label for="horario-reuniao">Horário da Reunião</label>
<input type="time"
id="horario-reuniao"
name="meeting_time"
min="08:00" <!-- Horário comercial -->
max="18:00"
step="900"> <!-- 900s = 15 minutos -->
<!-- 📅⏰ Data e hora juntas -->
<label for="evento">Data e Hora do Evento</label>
<input type="datetime-local"
id="evento"
name="event_datetime"
min="2024-01-01T08:00"
max="2025-12-31T22:00">
Quando o usuário precisa escolher: Nem tudo é texto livre. Muitas vezes precisamos que o usuário escolha entre opções predefinidas. Cada tipo de seleção serve para situações específicas:
<!-- ☑️ Checkbox único - Sim/Não simples -->
<div class="form-group">
<input type="checkbox"
id="newsletter"
name="subscribe_newsletter"
value="yes">
<label for="newsletter">
Quero receber newsletter com novidades
</label>
</div>
<!-- ☑️☑️☑️ Múltiplos checkboxes - Várias escolhas -->
<fieldset>
<legend>Interesses (selecione todos que se aplicam)</legend>
<div class="checkbox-group">
<input type="checkbox" id="tech" name="interests[]" value="tecnologia">
<label for="tech">Tecnologia</label>
</div>
<div class="checkbox-group">
<input type="checkbox" id="design" name="interests[]" value="design">
<label for="design">Design</label>
</div>
<div class="checkbox-group">
<input type="checkbox" id="marketing" name="interests[]" value="marketing">
<label for="marketing">Marketing</label>
</div>
</fieldset>
<!-- 🔘 Radio buttons - Uma escolha apenas -->
<fieldset>
<legend>Tamanho da Empresa</legend>
<div class="radio-group">
<input type="radio" id="pequena" name="company_size" value="pequena" required>
<label for="pequena">Pequena (1-10 funcionários)</label>
</div>
<div class="radio-group">
<input type="radio" id="media" name="company_size" value="media">
<label for="media">Média (11-100 funcionários)</label>
</div>
<div class="radio-group">
<input type="radio" id="grande" name="company_size" value="grande">
<label for="grande">Grande (100+ funcionários)</label>
</div>
</fieldset>
<!-- 📋 Select dropdown - Lista suspensa -->
<label for="pais">País</label>
<select id="pais" name="country" required>
<option value="">Selecione seu país</option>
<optgroup label="América do Sul"> <!-- Agrupa opções -->
<option value="BR">Brasil</option>
<option value="AR">Argentina</option>
<option value="CL">Chile</option>
</optgroup>
<optgroup label="América do Norte">
<option value="US">Estados Unidos</option>
<option value="CA">Canadá</option>
</optgroup>
</select>
<!-- 📋📋 Select múltiplo - Múltiplas escolhas em lista -->
<label for="habilidades">Habilidades Técnicas</label>
<select id="habilidades" name="skills[]" multiple size="5">
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="javascript">JavaScript</option>
<option value="python">Python</option>
<option value="react">React</option>
<option value="node">Node.js</option>
</select>
Crie um formulário de pesquisa de satisfação com:
Antes do HTML5, toda validação precisava ser programada em JavaScript. Agora o navegador faz o trabalho pesado para você:
Cada atributo é uma regra: Combine diferentes atributos para criar validações poderosas. O navegador vai verificar automaticamente se os dados atendem todos os critérios.
<form>
<!-- ✅ Campo obrigatório com múltiplas validações -->
<label for="nome-completo">Nome Completo</label>
<input type="text"
id="nome-completo"
name="full_name"
required <!-- ❗ Não pode ficar vazio -->
minlength="3" <!-- 📏 Mínimo 3 caracteres -->
maxlength="100" <!-- 📏 Máximo 100 caracteres -->
pattern="[A-Za-zÀ-ÿ\s]+" <!-- 🔤 Apenas letras e espaços -->
title="Apenas letras e espaços são permitidos"> <!-- 💬 Tooltip de ajuda -->
<!-- 📋 CPF com pattern customizado -->
<label for="cpf">CPF</label>
<input type="text"
id="cpf"
name="cpf"
pattern="\d{3}\.\d{3}\.\d{3}-\d{2}" <!-- 🔢 Formato específico -->
placeholder="123.456.789-00"
title="Formato: 123.456.789-00" <!-- 📝 Exemplo no tooltip -->
required>
<!-- 📧 Confirmação de e-mail -->
<label for="email-principal">E-mail</label>
<input type="email"
id="email-principal"
name="email"
required>
<label for="confirma-email">Confirme o E-mail</label>
<input type="email"
id="confirma-email"
name="email_confirmation"
required
oninput="checkEmailMatch()"> <!-- 🔄 Verificação em tempo real -->
<!-- 📎 Campo de arquivo com restrições -->
<label for="curriculo">Currículo (PDF)</label>
<input type="file"
id="curriculo"
name="resume"
accept=".pdf" <!-- 📋 Apenas PDFs -->
max="5242880"> <!-- 💾 5MB máximo -->
<button type="submit">Cadastrar</button>
</form>
Patterns são seus aliados: Expressões regulares podem parecer complexas, mas são extremamente poderosas. Aqui estão os patterns mais úteis para formulários brasileiros:
<!-- 📮 CEP brasileiro - Flexível -->
<label for="cep">CEP</label>
<input type="text"
id="cep"
name="postal_code"
pattern="\d{5}-?\d{3}" <!-- Com ou sem hífen -->
placeholder="12345-678"
title="Formato: 12345-678">
<!-- 📞 Telefone brasileiro - Celular ou fixo -->
<label for="celular">Celular</label>
<input type="tel"
id="celular"
name="mobile"
pattern="\(\d{2}\)\s\d{4,5}-\d{4}" <!-- 4 ou 5 dígitos no meio -->
placeholder="(11) 99999-9999"
title="Formato: (11) 99999-9999">
<!-- 🔢 Apenas números - Códigos, IDs -->
<label for="codigo">Código Numérico</label>
<input type="text"
id="codigo"
name="code"
pattern="\d+" <!-- Um ou mais dígitos -->
title="Apenas números">
<!-- 🚫 Sem caracteres especiais - Usernames -->
<label for="usuario">Nome de Usuário</label>
<input type="text"
id="usuario"
name="username"
pattern="[a-zA-Z0-9_]+" <!-- Letras, números, underscore -->
title="Apenas letras, números e underscore">
<!-- 📅 Data no formato brasileiro -->
<label for="data-br">Data (DD/MM/AAAA)</label>
<input type="text"
id="data-br"
name="date_br"
pattern="\d{2}/\d{2}/\d{4}" <!-- DD/MM/AAAA -->
placeholder="31/12/2024"
title="Formato: DD/MM/AAAA">
<form>
<label for="senha-forte">Senha Segura</label>
<input type="password"
id="senha-forte"
name="secure_password"
pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}"
title="Mínimo 8 caracteres com: maiúscula, minúscula, número e símbolo"
required>
<label for="idade-minima">Idade</label>
<input type="number"
id="idade-minima"
name="age"
min="18"
max="100"
title="Idade deve estar entre 18 e 100 anos"
required>
<button type="submit">Validar</button>
</form>
<script>
// Mensagens customizadas em português
document.addEventListener('DOMContentLoaded', function() {
const inputs = document.querySelectorAll('input');
inputs.forEach(input => {
input.addEventListener('invalid', function(e) {
const validity = e.target.validity;
if (validity.valueMissing) {
e.target.setCustomValidity('Este campo é obrigatório.');
} else if (validity.patternMismatch) {
e.target.setCustomValidity('Formato inválido. Verifique o exemplo.');
} else if (validity.tooShort) {
e.target.setCustomValidity(`Mínimo ${e.target.minLength} caracteres.`);
} else if (validity.tooLong) {
e.target.setCustomValidity(`Máximo ${e.target.maxLength} caracteres.`);
} else if (validity.rangeUnderflow) {
e.target.setCustomValidity(`Valor mínimo: ${e.target.min}`);
} else if (validity.rangeOverflow) {
e.target.setCustomValidity(`Valor máximo: ${e.target.max}`);
} else {
e.target.setCustomValidity('');
}
});
input.addEventListener('input', function(e) {
e.target.setCustomValidity('');
});
});
});
</script>
Acessibilidade não é apenas sobre pessoas com deficiência - é sobre criar experiências melhores para todos. Um formulário acessível é:
Labels são a fundação da acessibilidade: Eles dizem aos leitores de tela e outros dispositivos o que cada campo representa. Sem labels adequados, seu formulário é praticamente impossível de usar para pessoas com deficiência visual.
<!-- ✅ Associação explícita (RECOMENDADO) -->
<label for="email-usuario">E-mail:</label>
<input type="email" id="email-usuario" name="user_email">
<!-- 🔗 A conexão for="email-usuario" + id="email-usuario" -->
<!-- ✅ Associação implícita (também funciona) -->
<label>
Nome:
<input type="text" name="user_name">
</label>
<!-- 🔗 Input dentro do label se conecta automaticamente -->
<!-- 🏷️ Para elementos não-label -->
<span id="descricao-senha">Senha deve ter pelo menos 8 caracteres</span>
<input type="password"
name="password"
aria-describedby="descricao-senha" <!-- 📝 Conecta à descrição -->
aria-labelledby="titulo-senha"> <!-- 🏷️ Conecta ao título -->
<!-- 👥 Labels para grupos de campos -->
<fieldset>
<legend>Preferências de Contato</legend> <!-- 🎭 Título do grupo -->
<label><input type="radio" name="contact" value="email"> E-mail</label>
<label><input type="radio" name="contact" value="phone"> Telefone</label>
<label><input type="radio" name="contact" value="sms"> SMS</label>
</fieldset>
ARIA é a linguagem dos leitores de tela: Quando HTML semântico não é suficiente, ARIA (Accessible Rich Internet Applications) oferece atributos extras para comunicar melhor com tecnologias assistivas.
<form role="form" aria-label="Formulário de cadastro"> <!-- 🎭 Identifica o propósito -->
<!-- 📝 Campo com descrição adicional -->
<label for="username">Nome de Usuário</label>
<input type="text"
id="username"
name="username"
aria-describedby="username-help username-error" <!-- 📋 IDs das descrições -->
aria-required="true" <!-- ❗ Obrigatório (para ARIA) -->
aria-invalid="false"> <!-- ✅ Estado de validação -->
<div id="username-help" class="help-text">
Deve ter entre 3-20 caracteres, apenas letras e números
</div>
<div id="username-error" class="error-message" aria-live="polite"></div>
<!-- 📢 aria-live="polite" anuncia mudanças sem interromper -->
<!-- 👥 Grupo de campos relacionados -->
<fieldset role="group" aria-labelledby="endereco-titulo">
<legend id="endereco-titulo">Endereço de Entrega</legend>
<label for="rua">Rua</label>
<input type="text" id="rua" name="street" aria-required="true">
<label for="numero">Número</label>
<input type="text" id="numero" name="number" aria-required="true">
<label for="cep-endereco">CEP</label>
<input type="text" id="cep-endereco" name="zipcode"
pattern="\d{5}-\d{3}"
aria-describedby="cep-format">
<div id="cep-format">Formato: 12345-678</div>
</fieldset>
<!-- 📊 Status do formulário -->
<div id="form-status"
aria-live="assertive" <!-- 🚨 Anuncia imediatamente -->
aria-atomic="true"> <!-- 📢 Lê conteúdo completo -->
</div>
<button type="submit" aria-describedby="submit-help">
Finalizar Cadastro
</button>
<div id="submit-help">
Clique para enviar os dados ou pressione Enter
</div>
</form>
<form novalidate>
<div class="form-group">
<label for="email-required">
E-mail <span class="required" aria-label="obrigatório">*</span>
</label>
<input type="email"
id="email-required"
name="email"
aria-required="true"
aria-invalid="false"
aria-describedby="email-error">
<div id="email-error"
class="error-message"
role="alert"
aria-live="assertive"
style="display: none;">
</div>
</div>
<div class="form-group">
<label for="phone-optional">Telefone (opcional)</label>
<input type="tel"
id="phone-optional"
name="phone"
aria-describedby="phone-format">
<div id="phone-format" class="help-text">
Formato: (11) 99999-9999
</div>
</div>
<button type="submit">Enviar</button>
</form>
<style>
.required {
color: #d32f2f;
font-weight: bold;
}
.error-message {
color: #d32f2f;
font-size: 0.875em;
margin-top: 4px;
}
.help-text {
color: #666;
font-size: 0.875em;
margin-top: 4px;
}
.form-group {
margin-bottom: 1rem;
}
input:invalid {
border-color: #d32f2f;
box-shadow: 0 0 0 2px rgba(211, 47, 47, 0.2);
}
input:valid {
border-color: #4caf50;
}
</style>
Crie um formulário de checkout e-commerce acessível:
Um formulário bem projetado pode aumentar significativamente suas conversões. A diferença entre um usuário que abandona e um que completa o cadastro está nos detalhes da experiência:
Mobile-first não é opção, é obrigação: Seus formulários precisam funcionar perfeitamente em qualquer dispositivo. Um layout responsivo bem feito adapta não só o tamanho, mas toda a experiência de interação.
/* 📐 Layout base do formulário */
.form-container {
max-width: 600px; /* 📏 Largura máxima legível */
margin: 0 auto; /* 🎯 Centraliza na tela */
padding: 20px; /* 📦 Espaço interno */
}
.form-group {
margin-bottom: 1.5rem; /* 📏 Espaço entre campos */
}
.form-row {
display: flex; /* 🔄 Layout flexível */
gap: 1rem; /* 📏 Espaço entre colunas */
}
.form-col {
flex: 1; /* 📐 Colunas de tamanho igual */
}
/* 🏷️ Labels e inputs - Estilo profissional */
label {
display: block; /* 📐 Cada label em sua linha */
margin-bottom: 0.5rem; /* 📏 Espaço até o input */
font-weight: 600; /* ✨ Destaque visual */
color: #333; /* 🎨 Cor neutra */
}
input, select, textarea {
width: 100%; /* 📐 Largura total disponível */
padding: 12px 16px; /* 📦 Espaço interno confortável */
border: 2px solid #e1e5e9; /* 🖼️ Borda sutil */
border-radius: 8px; /* 🎨 Cantos arredondados */
font-size: 16px; /* 📱 Evita zoom no iOS */
transition: border-color 0.3s ease; /* ✨ Animação suave */
}
input:focus, select:focus, textarea:focus {
outline: none; /* 🚫 Remove outline padrão */
border-color: #007bff; /* 🎨 Azul de foco */
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1); /* ✨ Halo de foco */
}
/* 🎨 Estados de validação - Feedback visual */
input:valid {
border-color: #28a745; /* 🟢 Verde para válido */
}
input:invalid:not(:focus):not(:placeholder-shown) {
border-color: #dc3545; /* 🔴 Vermelho para inválido */
}
/* 📱 Responsive - Adaptação para mobile */
@media (max-width: 768px) {
.form-row {
flex-direction: column; /* 📐 Empilha em mobile */
gap: 0; /* 📏 Remove gap desnecessário */
}
input, select, textarea {
font-size: 16px; /* 📱 Previne zoom automático no iOS */
}
}
/* 🎭 Fieldsets - Agrupamento visual */
fieldset {
border: 2px solid #e1e5e9; /* 🖼️ Borda do grupo */
border-radius: 8px; /* 🎨 Cantos arredondados */
padding: 20px; /* 📦 Espaço interno */
margin-bottom: 2rem; /* 📏 Espaço entre grupos */
}
legend {
padding: 0 10px; /* 📦 Espaço nas laterais */
font-weight: bold; /* ✨ Destaque no título */
color: #007bff; /* 🎨 Cor de destaque */
}
<!-- Radio buttons customizados -->
<fieldset class="radio-fieldset">
<legend>Escolha um plano</legend>
<label class="radio-card">
<input type="radio" name="plan" value="basic">
<div class="radio-card-content">
<h4>Plano Básico</h4>
<p>Ideal para iniciantes</p>
<strong>R$ 19/mês</strong>
</div>
</label>
<label class="radio-card">
<input type="radio" name="plan" value="premium">
<div class="radio-card-content">
<h4>Plano Premium</h4>
<p>Para usuários avançados</p>
<strong>R$ 49/mês</strong>
</div>
</label>
</fieldset>
<!-- Checkbox customizado -->
<label class="checkbox-custom">
<input type="checkbox" name="terms">
<span class="checkmark"></span>
Li e aceito os <a href="/termos">termos de uso</a>
</label>
<!-- Upload de arquivo aprimorado -->
<div class="file-upload">
<label for="file-input" class="file-label">
<span class="file-icon">📁</span>
<span class="file-text">Clique para selecionar arquivo</span>
<span class="file-subtext">PDF, máximo 5MB</span>
</label>
<input type="file"
id="file-input"
name="document"
accept=".pdf"
style="display: none;">
<div class="file-status"></div>
</div>
/* Radio cards */
.radio-card {
display: block;
position: relative;
padding: 20px;
border: 2px solid #e1e5e9;
border-radius: 12px;
margin-bottom: 1rem;
cursor: pointer;
transition: all 0.3s ease;
}
.radio-card:hover {
border-color: #007bff;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 123, 255, 0.15);
}
.radio-card input[type="radio"] {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.radio-card input[type="radio"]:checked + .radio-card-content {
border-left: 4px solid #007bff;
padding-left: 16px;
}
.radio-card input[type="radio"]:checked ~ .radio-card {
border-color: #007bff;
background-color: #f8f9ff;
}
/* Checkbox customizado */
.checkbox-custom {
display: flex;
align-items: center;
cursor: pointer;
font-size: 14px;
line-height: 1.5;
}
.checkbox-custom input[type="checkbox"] {
opacity: 0;
position: absolute;
width: 0;
height: 0;
}
.checkmark {
width: 20px;
height: 20px;
border: 2px solid #e1e5e9;
border-radius: 4px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.checkbox-custom input[type="checkbox"]:checked + .checkmark {
background-color: #007bff;
border-color: #007bff;
}
.checkbox-custom input[type="checkbox"]:checked + .checkmark::after {
content: "✓";
color: white;
font-weight: bold;
font-size: 12px;
}
/* Upload de arquivo */
.file-upload {
position: relative;
}
.file-label {
display: flex;
flex-direction: column;
align-items: center;
padding: 40px 20px;
border: 2px dashed #e1e5e9;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
text-align: center;
}
.file-label:hover {
border-color: #007bff;
background-color: #f8f9ff;
}
.file-icon {
font-size: 48px;
margin-bottom: 10px;
}
.file-text {
font-weight: 600;
color: #333;
margin-bottom: 4px;
}
.file-subtext {
font-size: 14px;
color: #666;
}
/* Botões */
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-block;
text-align: center;
}
.btn-primary {
background-color: #007bff;
color: white;
}
.btn-primary:hover {
background-color: #0056b3;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 123, 255, 0.3);
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-outline {
background-color: transparent;
border: 2px solid #007bff;
color: #007bff;
}
.btn-outline:hover {
background-color: #007bff;
color: white;
}
Crie um formulário de inscrição em evento com design completo:
Parabéns! Você completou uma jornada completa pelos formulários HTML. Agora você tem conhecimento sólido para criar formulários profissionais, acessíveis e com excelente experiência do usuário.
Form, fieldset, legend e organização semântica. Você aprendeu a criar formulários bem estruturados que são fáceis de usar e manter.
Domínio completo de todos os tipos: text, email, number, date, radio, checkbox, select e file. Cada tipo para sua situação específica.
Validação nativa poderosa com required, pattern, min/max e mensagens customizadas. Sem necessidade de JavaScript básico.
Formulários inclusivos com labels corretos, ARIA, indicação de erros e navegação por teclado. Experiência para todos os usuários.
Agora que você domina formulários básicos, está pronto para: