Início / Componentes / Inputs

Inputs

Input coleta dado. Parece simples, mas tem muito contexto implícito que o usuário precisa para preencher corretamente. O trabalho do designer e do dev é deixar esse contexto explícito.

Label não é opcional

Placeholder não é label. Ele desaparece quando o usuário começa a digitar — e some completamente para quem usa screen reader antes de interagir com o campo.

A conexão semântica entre label e input é feita com id + htmlFor. Sem isso, um leitor de tela anuncia apenas "campo de texto" — sem contexto nenhum do que preencher.

/* Errado — sem label */
<input type="text" placeholder="Nome completo" />

/* Certo — label conectado */
<label for="nome">Nome completo</label>
<input id="nome" type="text" placeholder="Ex: Ana Silva" />

Com a conexão correta, o screen reader anuncia "Nome completo, campo de texto". O usuário sabe exatamente o que preencher.

Estados e feedback

Um input tem mais estados do que parece:

  • default: borda sutil, placeholder em cinza claro
  • focus: borda destacada — nunca remova o outline sem substituir por algo equivalente
  • filled: dado preenchido, borda volta ao normal
  • error: borda vermelha + mensagem de erro embaixo
  • disabled: opacidade reduzida, cursor-not-allowed, não editável

Tente preencher o campo de email abaixo com um valor inválido e sair do campo:

Validação e erro

Regras de quando e como validar:

  • Valide no blur (quando o usuário sai do campo), não enquanto digita — validar durante a digitação é agressivo
  • Nunca confie só em cor para indicar erro — quem tem daltonismo não vai ver. Use ícone ou texto junto
  • A mensagem de erro precisa estar conectada ao input via aria-describedby
/* HTML */
<label for="email">Email</label>
<input type="email" id="email" placeholder="exemplo@email.com" />
<span class="error" id="email-erro" role="alert"></span>
/* CSS */
input {
  display: block;
  width: 100%;
  padding: 0.5rem;
  border: 1px solid var(--border);
  border-radius: 0.375rem;
}

input:focus {
  outline: none;
  border-color: var(--primary);
}

input[aria-invalid="true"] {
  border-color: var(--error);
}

.error {
  color: var(--error);
  font-size: 0.875rem;
  margin-top: 0.25rem;
}
/* JavaScript */
const input = document.getElementById('email');
const erro = document.getElementById('email-erro');

input.addEventListener('blur', () => {
  const email = input.value;
  const valido = /@/.test(email);
  
  input.setAttribute('aria-invalid', !valido);
  erro.textContent = valido ? '' : 'Email inválido';
});

O aria-describedby conecta a mensagem de erro ao campo. O role="alert" na mensagem faz com que o screen reader anuncie o erro automaticamente quando ele aparece.

A decisão

Neste guia, todo input tem label visível e acima do campo. Mensagem de erro aparece abaixo, em vermelho, com aria-describedby conectando os dois. O estado de focus usa var(--border-highlight) sem outline padrão do browser — mas só porque substituímos por algo equivalente.