Back

Estilizando Estados Válidos e Inválidos de Formulários com CSS

Estilizando Estados Válidos e Inválidos de Formulários com CSS

Campos obrigatórios vazios brilhando em vermelho antes que os usuários digitem um único caractere. Essa é a experiência frustrante que muitos formulários oferecem—e é totalmente evitável com CSS moderno.

Este guia aborda estados de validação de formulários CSS e as pseudo-classes que os controlam. Você aprenderá quando usar :valid e :invalid, por que :user-valid e :user-invalid proporcionam melhor UX, e como estilizar erros de formulário com CSS usando padrões como :has() para estilização de elementos pai.

Principais Conclusões

  • A validação de restrições HTML5 funciona automaticamente com atributos como required, type, min, max e pattern
  • Evite :valid e :invalid para estilização de erros, pois são acionados antes da interação do usuário
  • Use :user-valid e :user-invalid para mostrar feedback de validação apenas após os usuários interagirem com os campos
  • Combine :has() com pseudo-classes de validação para estilizar elementos pai e controlar a visibilidade de mensagens de erro
  • Sempre combine validação visual com atributos ARIA para acessibilidade de leitores de tela

Como Funciona a Validação de Restrições

A validação de restrições HTML5 acontece automaticamente. Quando você adiciona atributos como required, type="email", min, max, minlength, maxlength ou pattern aos campos de formulário, os navegadores avaliam a validade continuamente.

O navegador verifica as restrições sempre que o valor do input muda. As pseudo-classes CSS refletem esse estado de validade em tempo real, permitindo que você estilize campos com base em se eles passam ou falham na validação.

<input type="email" required minlength="5" />

Este campo se torna inválido se estiver vazio, se o valor não estiver formatado como e-mail, ou se for menor que cinco caracteres.

Pseudo-Classes Principais de Formulários CSS

:valid e :invalid

Essas pseudo-classes correspondem a campos com base na validade atual:

input:valid {
  border-color: green;
}

input:invalid {
  border-color: red;
}

O problema: :invalid se aplica imediatamente no carregamento da página. Um campo obrigatório sem valor é tecnicamente inválido antes que o usuário o toque. Isso cria uma experiência ruim—os usuários veem erros antes de terem feito algo errado.

:required e :optional

Essas segmentam campos com base no atributo required:

input:required {
  border-left: 3px solid blue;
}

input:optional {
  border-left: 3px solid gray;
}

Útil para indicadores visuais mostrando quais campos devem ser preenchidos.

:in-range e :out-of-range

Para inputs de número e intervalo com restrições min/max:

input:in-range {
  background: white;
}

input:out-of-range {
  background: #ffe0e0;
}

Melhor UX com :user-valid e :user-invalid

As pseudo-classes :user-valid e :user-invalid resolvem o problema de erro prematuro. Elas só correspondem após o usuário ter interagido com o campo—tipicamente após editar e desfocar.

input:user-invalid {
  border-color: red;
}

input:user-valid {
  border-color: green;
}

Agora os campos obrigatórios não mostram erros no carregamento da página. A estilização de validação aparece apenas após os usuários terem tido a chance de inserir dados. Isso corresponde às expectativas do usuário e reduz a frustração.

Essas pseudo-classes têm amplo suporte de navegadores e devem ser sua escolha padrão para estilizar erros de formulário com CSS.

Estilização de Elementos Pai com :has()

O seletor :has() permite estilizar elementos pai com base nos estados de validação dos filhos—algo anteriormente impossível sem JavaScript.

<div class="field">
  <label for="email">Email</label>
  <input type="email" id="email" required />
  <span class="error">Por favor, insira um email válido</span>
</div>
.error {
  display: none;
  color: red;
}

.field:has(input:user-invalid) .error {
  display: block;
}

.field:has(input:user-invalid) input {
  border-color: red;
}

Este padrão mostra mensagens de erro apenas quando o input contido falha na validação após a interação do usuário. O contêiner pai controla a visibilidade de elementos de erro irmãos.

Considerações de Acessibilidade

A estilização visual sozinha não é suficiente para formulários acessíveis. Usuários de leitores de tela precisam de associações de erro programáticas.

Combine estados de validação CSS com atributos ARIA apropriados:

<div class="field">
  <label for="email">Email</label>
  <input 
    type="email" 
    id="email" 
    required
    aria-describedby="email-error"
    aria-invalid="false"
  />
  <span id="email-error" class="error" aria-live="polite">
    Por favor, insira um email válido
  </span>
</div>

Requisitos principais de acessibilidade:

  • aria-describedby vincula o input à sua mensagem de erro
  • aria-live="polite" anuncia mudanças de erro aos leitores de tela
  • aria-invalid deve ser atualizado via JavaScript quando a validação falhar
  • Contraste de cores deve atender aos requisitos WCAG (4.5:1 para texto)
  • Não confie apenas na cor—use ícones ou texto junto com mudanças de cor

Padrão Prático: Validação Completa de Campo

Aqui está um padrão pronto para produção combinando essas técnicas:

/* Estilos base do campo */
.field input {
  border: 2px solid #ccc;
  transition: border-color 0.2s;
}

/* Estado válido após interação */
.field:has(input:user-valid) input {
  border-color: #2e7d32;
}

/* Estado inválido após interação */
.field:has(input:user-invalid) input {
  border-color: #c62828;
}

.field:has(input:user-invalid) .error {
  display: block;
}

/* Mensagem de erro oculta por padrão */
.error {
  display: none;
  color: #c62828;
  font-size: 0.875rem;
  margin-top: 0.25rem;
}

Esta abordagem mantém as mensagens de erro ocultas até que os usuários interajam com os campos, revelando-as apenas quando a validação falha.

Conclusão

As pseudo-classes modernas de formulários CSS fornecem estilização de validação poderosa sem JavaScript. Use :user-valid e :user-invalid como suas ferramentas principais—elas previnem exibição prematura de erros e correspondem às expectativas do usuário. Combine-as com :has() para estilização em nível de pai que controla a visibilidade de mensagens de erro.

Lembre-se de que a estilização visual complementa, mas não substitui, o tratamento de erros acessível. Sempre combine estados de validação CSS com atributos ARIA apropriados para usuários de leitores de tela.

Perguntas Frequentes

A pseudo-classe :invalid corresponde a qualquer campo que falhe na validação, incluindo no carregamento da página antes da interação do usuário. A pseudo-classe :user-invalid só corresponde após o usuário ter interagido e saído do campo. Isso evita mostrar estilos de erro em campos obrigatórios vazios antes que os usuários tenham a chance de preenchê-los.

Essas pseudo-classes têm amplo suporte em navegadores modernos, incluindo Chrome, Firefox, Safari e Edge. Para suporte a navegadores mais antigos, considere usar as pseudo-classes :focus e :not(:placeholder-shown) como padrão alternativo, embora essa abordagem seja menos precisa.

As pseudo-classes de validação CSS refletem o estado da API de validação de restrições do navegador. Se você prevenir o envio padrão do formulário ou usar JavaScript para lidar com formulários, certifique-se de chamar checkValidity() nos elementos do formulário. Os estados CSS são atualizados com base na propriedade validity, não em tentativas de envio.

Use o seletor :has() em um contêiner pai para detectar estados de validação de input filho. Por exemplo, .field:has(input:user-invalid) .error-message permite que você mostre ou oculte elementos de erro irmãos com base em se o input dentro desse contêiner falha na validação após a interação do usuário.

Truly understand users experience

See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..

OpenReplay