Placeholder não é rótulo: o campo precisa dizer o que pergunta depois de preenchido
Ligue o rótulo ao campo, use placeholder só como exemplo, ponha instruções em texto de ajuda com aria-describedby e faça rótulo flutuante sem perder acessibilidade.
No cadastro de fornecedor, os campos diziam "Razão social", "CNPJ" e "E-mail do financeiro", tudo em cinza dentro da caixa. A atendente preencheu, foi revisar antes de salvar e encontrou dois e-mails. Qual era o do financeiro e qual era o comercial? A pergunta tinha sumido junto com a dica.
A resposta curta: todo campo precisa de um rótulo visível, fora da caixa, ligado a ele por label for. O placeholder é opcional e serve só para mostrar um exemplo de formato, como "00.000.000/0000-00". Instrução importante vai num texto de ajuda abaixo do campo, ligado com aria-describedby, e não dentro da caixa.
Por que o placeholder não funciona como rótulo
O placeholder desaparece no primeiro caractere digitado. Isso causa quatro problemas, todos concretos.
- Memória: quem volta ao campo para revisar não sabe mais o que ele perguntava. Em formulários longos, ou quando o preenchimento automático completa tudo de uma vez, isso acontece o tempo todo.
- Contraste: o placeholder costuma ser cinza claro para "parecer dica", e fica difícil de ler. Se você aumenta o contraste, ele passa a parecer um valor já preenchido, e a pessoa pula o campo.
- Nome acessível: leitores de tela até leem o placeholder quando não há rótulo, mas o suporte varia, e ferramentas de controle por voz dependem de um nome visível para a pessoa dizer "clicar em CNPJ".
- Erro: quando o campo é marcado como inválido, o usuário vê a mensagem de erro e o próprio valor, mas não a pergunta original.
Existe uma versão antiga desse problema, a "marca d'água" feita com JavaScript que acabava gravada no banco como se fosse dado. Se o seu sistema ainda tem isso, o post marca d'água no campo mostra como migrar e limpar os registros. Aqui o foco é o formulário novo, feito certo desde o início.
Como montar o campo certo
- Escreva o rótulo como pergunta curta ou substantivo: "E-mail do financeiro". Fica acima do campo, sempre visível.
- Ligue o rótulo ao campo com
forapontando para oiddo input. Clicar no rótulo passa a focar o campo, o que também aumenta a área de toque. - Coloque instruções (formato, para que serve, se é opcional) num parágrafo abaixo do rótulo ou do campo, com
idpróprio. - Ligue esse texto ao campo com
aria-describedby, para o leitor de tela ler a ajuda junto com o nome. - Se usar placeholder, que seja exemplo e comece com "Ex.:", para não parecer valor preenchido nem instrução.
- Quando houver erro, acrescente a mensagem ao
aria-describedby, sem trocar o rótulo.
<label for="cnpj">CNPJ</label>
<p id="cnpj-ajuda">Só números ou com pontuação, como aparece no cartão CNPJ.</p>
<input id="cnpj" name="cnpj" inputmode="numeric"
aria-describedby="cnpj-ajuda" placeholder="Ex.: 12.345.678/0001-90">
<label for="email-fin">E-mail do financeiro</label>
<p id="email-fin-ajuda">Para onde enviamos boletos e notas fiscais.</p>
<input id="email-fin" name="email_financeiro" type="email" autocomplete="email"
aria-describedby="email-fin-ajuda">
Com erro, o campo ganha a mensagem sem perder nada:
<input id="email-fin" name="email_financeiro" type="email" autocomplete="email"
aria-describedby="email-fin-ajuda email-fin-erro" aria-invalid="true">
<p id="email-fin-erro">Falta o domínio. Exemplo: financeiro@empresa.com.br</p>
O leitor de tela anuncia algo como "E-mail do financeiro, campo de edição, inválido, Para onde enviamos boletos e notas fiscais. Falta o domínio". A ordem exata varia entre leitores, mas nome, ajuda e erro chegam juntos. Onde e como mostrar o erro está detalhado em onde colocar o erro para ninguém perder.
Rótulo flutuante sem cair na armadilha
O design pede o visual "Material", em que o nome começa dentro da caixa e sobe quando a pessoa digita. Dá para fazer, desde que o texto que sobe seja um label de verdade, não o placeholder. O truque é usar um placeholder de um espaço só e o seletor :placeholder-shown para saber se o campo está vazio.
<div class="campo-flutuante">
<input id="razao" name="razao_social" placeholder=" " autocomplete="organization">
<label for="razao">Razão social</label>
</div>
.campo-flutuante {
position: relative;
}
.campo-flutuante input {
width: 100%;
padding: 1.5rem 0.75rem 0.5rem;
font-size: max(16px, 1rem);
}
.campo-flutuante label {
position: absolute;
left: 0.75rem;
top: 1rem;
color: #444;
pointer-events: none;
transition: top 0.15s, font-size 0.15s;
}
.campo-flutuante input:focus + label,
.campo-flutuante input:not(:placeholder-shown) + label {
top: 0.35rem;
font-size: 0.75rem;
}
Três cuidados. O rótulo reduzido ainda precisa ter contraste de texto normal, nada de cinza fantasma. A área de cima do campo precisa de espaço suficiente para o rótulo pequeno não sobrepor o valor. E o preenchimento automático do navegador às vezes completa o campo sem disparar a mudança visual em todos os navegadores, então teste com o gerenciador de senhas e o autopreenchimento de endereço ligados. Se o rótulo ficar por cima do valor preenchido, a versão simples, com rótulo fixo acima, é mais segura.
Como conferir o que o navegador entendeu
Abra as ferramentas de desenvolvedor, selecione o campo e procure o painel de acessibilidade. Ele mostra o "nome" calculado para o elemento. Esse nome deve ser igual ao rótulo visível, ou começar com ele. Se aparecer vazio, ou vier do placeholder, o campo está sem rótulo de verdade. O mesmo painel mostra a descrição, que deve trazer o texto de ajuda.
O teste humano é mais simples ainda: preencha o formulário inteiro com o autopreenchimento, tire uma captura de tela e mostre a alguém que não viu o formulário vazio. Cada valor tem um nome ao lado? A arrumação geral do formulário, com rótulo em cima e uma coluna só, está em formulário que não cansa, e os tokens de preenchimento automático em autocomplete que ajuda.
Erros comuns
- Placeholder como único nome do campo. Depois de preenchido, ninguém sabe o que o campo perguntava, nem quem revisa nem quem corrige um erro.
- Rótulo visível sem
forligado aoid. O leitor de tela anuncia "campo de edição" sem nome, e clicar no texto não foca o campo. - Instrução importante dentro do placeholder. "Mínimo 8 caracteres, com um número" some no primeiro caractere, quando a pessoa mais precisava ler.
- Placeholder com cara de valor. "contato@empresa.com" em cor escura faz a pessoa achar que o campo já está preenchido e pular.
- Rótulo flutuante feito com o próprio placeholder. O texto que sobe é decorativo, e o campo continua sem nome acessível.
- Trocar o rótulo pela mensagem de erro. O campo passa a se chamar "Campo obrigatório" e a pergunta original desaparece.
Checklist
- Todo campo tem
labelvisível comforigual aoiddo input. - O nome acessível de cada campo, no painel do navegador, bate com o rótulo visível.
- Instruções estão em texto de ajuda ligado por
aria-describedby. - Placeholder, quando existe, é um exemplo e começa com "Ex.:".
- Rótulo e placeholder têm contraste legível.
- Mensagens de erro somam ao rótulo, não o substituem.
- O formulário preenchido continua compreensível numa captura de tela.
Preencha agora o formulário mais longo do seu sistema com o autopreenchimento do navegador e olhe para ele sem clicar em nada. Você ainda sabe, campo por campo, o que cada um perguntava?
Referência: https://www.w3.org/WAI/tutorials/forms/labels/