Estilizando Estados Válidos e Inválidos de Formulários com CSS
As pseudo-classes CSS user-valid e user-invalid evitam erros prematuros. A combinação com seletores has e atributos ARIA garante estilização acessível.
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,maxepattern - Evite
:valide:invalidpara estilização de erros, pois são acionados antes da interação do usuário - Use
:user-valide:user-invalidpara 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.
Discover how at OpenReplay.com.
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-describedbyvincula o input à sua mensagem de erroaria-live="polite"anuncia mudanças de erro aos leitores de telaaria-invaliddeve 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
Qual é a diferença entre :invalid e :user-invalid?
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.
Posso usar :user-valid e :user-invalid em todos os navegadores?
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.
Por que minha estilização de validação CSS não funciona com envio de formulário JavaScript?
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.
Como estilizo mensagens de erro que aparecem fora do elemento input?
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.
Star on GitHub12k