CSS :invalid Pseudo-Classe
Aprenda como o CSS :invalid estiliza campos de formulário com falha de validação, com exemplos, dicas de acessibilidade e suporte a navegadores.
A pseudo-classe CSS :invalid corresponde a controles associados a formulários — como <input>, <select> e <textarea> — cujo valor atual falha nas restrições de validação nativas do navegador. É a metade visual da API de validação de restrições do HTML: o navegador avalia a validade e :invalid permite estilizar o resultado com CSS puro, sem JavaScript.
Esta página cobre exatamente quando :invalid corresponde, como estilizá-lo sem assustar os usuários antes que tenham digitado qualquer coisa, a particularidade do botão de rádio, a alternativa moderna :user-invalid e como manter o feedback de erro acessível.
Quando um elemento corresponde a :invalid?
Um controle corresponde a :invalid sempre que possui pelo menos uma restrição de validação e seu valor atual viola essa restrição. Gatilhos comuns:
| Restrição | Atributo / tipo | Falha quando… |
|---|---|---|
| Obrigatório mas vazio | required | o campo não tem valor |
| Formato de e-mail incorreto | type="email" | o valor não é um e-mail sintaticamente válido |
| Formato de URL incorreto | type="url" | o valor não é uma URL absoluta válida |
| Fora do intervalo | min / max em type="number", type="date", etc. | o valor está fora do intervalo permitido |
| Passo incorreto | step | o valor não se alinha com o intervalo de passo |
| Incompatibilidade de padrão | pattern | o valor não corresponde à expressão regular |
| Comprimento inválido | minlength / maxlength | o comprimento do valor está fora do intervalo permitido |
Se um controle não tiver nenhuma restrição (um <input type="text"> simples sem atributos extras), ele é considerado sem restrições e nem :valid nem :invalid se aplica a ele.
Um elemento <fieldset> corresponde a :invalid quando qualquer um dos controles de formulário descendentes for inválido.
Para o lado oposto, veja a pseudo-classe :valid, que corresponde a controles que passam em todas as restrições. A pseudo-classe :required corresponde a campos obrigatórios independentemente de terem um valor.
Sintaxe
:invalid {
/* declarations applied to all invalid form controls */
}Delimite :invalid a um tipo de elemento específico para manter os estilos previsíveis:
input:invalid,
textarea:invalid {
border: 2px solid #c00;
outline: none;
}Exemplo básico
O campo de e-mail abaixo está pré-preenchido com um endereço malformado ("not-an-email"), portanto corresponde a input:invalid ao carregar e recebe uma borda vermelha.
<!DOCTYPE html>
<html>
<head>
<title>:invalid example</title>
<style>
input:invalid {
border: 2px solid #c00;
background-color: #fff0f0;
}
input:valid {
border: 2px solid #090;
background-color: #f0fff0;
}
</style>
</head>
<body>
<h2>:invalid selector example</h2>
<form>
<label for="email">Email:</label>
<input id="email" type="email" value="not-an-email" required />
</form>
</body>
</html>Evitando o "vermelho prematuro"
A armadilha mais comum de :invalid: um campo required vazio já é inválido no momento em que a página carrega, então um formulário recém-aberto pode ficar todo vermelho antes que o usuário tenha digitado qualquer coisa. Isso parece acusatório.
Opção 1 — estilizar apenas enquanto o campo está focado
Mostre a borda de erro somente enquanto o usuário está ativamente no campo:
input:invalid:focus {
border-color: #c00;
outline: 2px solid #c00;
outline-offset: 1px;
}É simples, mas desaparece no momento em que o usuário pressiona Tab para sair, fazendo com que um campo obrigatório em branco pareça correto novamente.
Opção 2 — ocultar o erro enquanto o placeholder estiver visível
:placeholder-shown é verdadeiro quando o texto do placeholder é exibido (ou seja, o campo está vazio). Combiná-lo com :not faz o estilo de :invalid entrar em ação somente depois que o usuário tiver digitado algo:
/* Only show the error style when the field has a value that is invalid */
input:invalid:not(:placeholder-shown) {
border-color: #c00;
}Isso é eficaz, mas exige que todo campo tenha um atributo placeholder definido — caso contrário, :placeholder-shown nunca é verdadeiro e a proteção não funciona.
Opção 3 — usar :user-invalid (padrão moderno)
A pseudo-classe :user-invalid foi criada especificamente para resolver esse problema. Ela se comporta como :invalid, mas só corresponde depois que o usuário interagiu com o controle (digitou nele, saiu dele ou enviou o formulário):
/* Supported in all modern browsers as of 2024 */
input:user-invalid {
border-color: #c00;
}
/* Fallback for older browsers */
@supports not selector(:user-invalid) {
input:invalid:not(:placeholder-shown) {
border-color: #c00;
}
}:user-invalid é a solução mais limpa quando você pode contar com ela. O Firefox tem suportado isso como :-moz-ui-invalid há anos; o padrão :user-invalid agora está em todos os navegadores modernos.
Abordagem de estilização
Uma borda completa de 2 px em vermelho intenso é legível, mas impactante. Considere combinar uma mudança de borda com um box-shadow sutil para um visual mais suave:
input:invalid:not(:placeholder-shown) {
border-color: #c00;
box-shadow: 0 0 0 3px rgba(204, 0, 0, 0.15);
}Evite depender apenas da cor — veja a seção Acessibilidade abaixo.
Armadilhas
Botões de rádio
Quando um grupo de botões de rádio tem required em um de seus inputs, todos os botões do grupo correspondem a :invalid enquanto nenhum está selecionado. Estilizar círculos de rádio minúsculos não é prático; em vez disso, estilize o <fieldset> ou <label> ao redor:
/* Style the fieldset, not the radio buttons themselves */
fieldset:invalid {
border: 2px solid #c00;
border-radius: 4px;
padding: 8px 12px;
}Todos os botões de rádio em um grupo compartilham o mesmo atributo name — é isso que os torna um grupo no modelo de validade do navegador.
Campos opcionais vazios
Um <input type="text"> simples sem required, sem pattern e sem restrições de comprimento é sempre :valid mesmo quando vazio. :invalid só é acionado quando uma restrição existe e é violada.
select e textarea
<select> corresponde a :invalid se for required e seu value atual for uma string vazia (um padrão comum é um <option value=""> de placeholder "-- escolha --" no topo). <textarea> segue as mesmas regras que <input> para required, minlength e maxlength.
Firefox e :-moz-ui-invalid
O Firefox aplica estilos há muito tempo via :-moz-ui-invalid, que só é ativado após a interação do usuário — efetivamente o comportamento de :user-invalid embutido. Se você adicionar suas próprias regras :invalid e testar no Firefox, o campo pode parecer correto (porque o guard de interação do usuário padrão do navegador está ativo), depois se comportar de forma diferente no Chrome (onde o guard padrão está desativado). Defina regras explícitas e use :user-invalid com um fallback para obter comportamento consistente.
Acessibilidade
A cor sozinha nunca é suficiente para comunicar um erro — usuários com deficiências na visão de cores podem não perceber uma borda vermelha. Combine o estilo de :invalid com:
- Uma mensagem de texto visível explicando o que deu errado e como corrigir.
- Um ícone ou símbolo junto à mudança de cor (por exemplo, um ✕ ou ícone de aviso).
aria-invalid="true"no controle para que leitores de tela o anunciem como inválido.aria-describedbyapontando para o elemento de mensagem de erro para que a descrição seja lida automaticamente.
<label for="email">Email address</label>
<input
id="email"
type="email"
aria-invalid="true"
aria-describedby="email-error"
required
/>
<span id="email-error" role="alert">
Please enter a valid email address.
</span>O role="alert" no span de erro faz com que os leitores de tela anunciem a mensagem assim que ela aparecer no DOM, mesmo sem foco.
Pseudo-classes relacionadas
| Pseudo-classe | Corresponde quando… |
|---|---|
:valid | o controle passa em todas as suas restrições |
:required | o controle tem o atributo required |
:optional | o controle não tem required |
:out-of-range | o valor de um input numérico/data excede min/max |
:in-range | o valor de um input numérico/data está dentro de min/max |
:placeholder | o texto de placeholder de um input |
:focus | o controle atualmente tem foco do teclado |
Suporte a navegadores
:invalid faz parte do Selectors Level 4 e tem suporte em todos os principais navegadores há muitos anos. :user-invalid (a variante com consciência de interação) foi lançada no Chrome 119, Firefox 88 (como :-moz-ui-invalid muito antes) e Safari 16.5.
Para mais informações sobre os atributos de restrição nativos do HTML, veja <input> e HTML Forms.