W3docs

CSS :invalid Pseudo-Classe

Apprenez comment CSS :invalid stylise les champs de formulaire qui échouent à la validation, avec des exemples, des pièges et des conseils d'accessibilité.

La pseudo-classe CSS :invalid correspond aux contrôles associés aux formulaires — tels que <input>, <select> et <textarea> — dont la valeur actuelle échoue aux contraintes de validation intégrées du navigateur. C'est la partie visuelle de l'API de validation des contraintes HTML : le navigateur évalue la validité, et :invalid vous permet de styliser le résultat en CSS pur, sans JavaScript.

Cette page explique exactement quand :invalid correspond, comment le styliser sans alarmer les utilisateurs avant qu'ils aient saisi quoi que ce soit, le comportement particulier des boutons radio, l'alternative moderne :user-invalid, et comment rendre les retours d'erreur accessibles.

Quand un élément correspond-il à :invalid ?

Un contrôle correspond à :invalid dès qu'il possède au moins une contrainte de validation et que sa valeur actuelle viole cette contrainte. Déclencheurs courants :

ContrainteAttribut / typeÉchoue quand…
Obligatoire mais viderequiredle champ n'a pas de valeur
Mauvais format d'e-mailtype="email"la valeur n'est pas un e-mail syntaxiquement valide
Mauvais format d'URLtype="url"la valeur n'est pas une URL absolue valide
Hors plagemin / max sur type="number", type="date", etc.la valeur est en dehors de la plage autorisée
Mauvais passtepla valeur ne correspond pas à l'intervalle du pas
Incompatibilité de motifpatternla valeur ne correspond pas à l'expression régulière
Trop long / trop courtminlength / maxlengthla longueur de la valeur est en dehors de la plage autorisée

Si un contrôle n'a aucune contrainte (un simple <input type="text"> sans attributs supplémentaires), il est considéré comme non contraint et ni :valid ni :invalid ne s'y applique.

Un élément <fieldset> correspond à :invalid quand l'un de ses contrôles de formulaire descendants est invalide.

Pour le côté opposé, voir la pseudo-classe :valid, qui correspond aux contrôles qui passent toutes les contraintes. La pseudo-classe :required correspond aux champs obligatoires, qu'ils aient une valeur ou non.

Syntaxe

:invalid {
  /* declarations applied to all invalid form controls */
}

Ciblez :invalid sur un type d'élément spécifique pour garder les styles prévisibles :

input:invalid,
textarea:invalid {
  border: 2px solid #c00;
  outline: none;
}

Exemple de base

Le champ e-mail ci-dessous est pré-rempli avec une adresse mal formée ("not-an-email"), donc il correspond à input:invalid au chargement et reçoit une bordure rouge.

<!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>

Éviter le "rouge prématuré"

Le piège le plus courant de :invalid : un champ required vide est déjà invalide dès le chargement de la page, de sorte qu'un formulaire vierge peut s'afficher en rouge avant que l'utilisateur ait saisi quoi que ce soit. Cela peut sembler accusatoire.

Option 1 — styliser uniquement quand le champ est ciblé

Afficher la bordure d'erreur uniquement quand l'utilisateur est actif dans le champ :

input:invalid:focus {
  border-color: #c00;
  outline: 2px solid #c00;
  outline-offset: 1px;
}

C'est simple, mais le style disparaît dès que l'utilisateur quitte le champ, donc un champ obligatoire vide semble à nouveau correct.

Option 2 — masquer l'erreur quand le placeholder est visible

:placeholder-shown est vrai quand le texte de l'espace réservé est affiché (c'est-à-dire que le champ est vide). En le combinant avec :not, le style :invalid ne s'active qu'une fois que l'utilisateur a saisi quelque chose :

/* Only show the error style when the field has a value that is invalid */
input:invalid:not(:placeholder-shown) {
  border-color: #c00;
}

C'est efficace, mais cela nécessite que chaque champ ait un attribut placeholder défini — sinon :placeholder-shown n'est jamais vrai et la protection ne sert à rien.

Option 3 — utiliser :user-invalid (standard moderne)

La pseudo-classe :user-invalid a été conçue spécifiquement pour résoudre ce problème. Elle se comporte comme :invalid mais ne correspond qu'après que l'utilisateur a interagi avec le contrôle (saisi du texte, quitté le champ ou soumis le formulaire) :

/* 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 est la solution la plus propre quand vous pouvez vous y fier. Firefox l'a supporté en tant que :-moz-ui-invalid depuis des années ; le standard :user-invalid est maintenant disponible dans tous les navigateurs modernes.

Approche stylistique

Une bordure rouge pleine de 2 px est lisible mais saisissante. Envisagez de combiner un changement de bordure avec un léger box-shadow pour un rendu plus doux :

input:invalid:not(:placeholder-shown) {
  border-color: #c00;
  box-shadow: 0 0 0 3px rgba(204, 0, 0, 0.15);
}

Évitez de vous reposer uniquement sur la couleur — voir la section Accessibilité ci-dessous.

Pièges

Boutons radio

Lorsqu'un groupe de boutons radio a required sur l'un de ses inputs, chaque bouton du groupe correspond à :invalid tant qu'aucun n'est sélectionné. Styliser de petits cercles radio n'est pas pratique ; stylisez plutôt le <fieldset> ou le <label> environnant :

/* Style the fieldset, not the radio buttons themselves */
fieldset:invalid {
  border: 2px solid #c00;
  border-radius: 4px;
  padding: 8px 12px;
}

Tous les boutons radio d'un groupe partagent le même attribut name — c'est ce qui en fait un groupe dans le modèle de validité du navigateur.

Champs optionnels vides

Un simple <input type="text"> sans required, sans pattern et sans contrainte de longueur est toujours :valid même quand il est vide. :invalid ne se déclenche que quand une contrainte existe et est violée.

select et textarea

<select> correspond à :invalid s'il est required et que sa value actuelle est une chaîne vide (un modèle courant est une option de type "-- choisir --" avec <option value=""> en tête). <textarea> suit les mêmes règles que <input> pour required, minlength et maxlength.

Firefox et :-moz-ui-invalid

Firefox applique depuis longtemps des styles via :-moz-ui-invalid, qui ne s'active qu'après une interaction utilisateur — ce qui correspond effectivement au comportement :user-invalid intégré. Si vous ajoutez vos propres règles :invalid et testez dans Firefox, le champ peut sembler correct (car la protection par interaction utilisateur du navigateur est activée), puis se comporter différemment dans Chrome (où cette protection est désactivée par défaut). Définissez des règles explicites et utilisez :user-invalid avec un repli pour obtenir un comportement cohérent.

Accessibilité

La couleur seule ne suffit jamais pour communiquer une erreur — les utilisateurs ayant des déficiences de vision des couleurs peuvent ne pas percevoir une bordure rouge. Associez le style :invalid avec :

  • Un message texte visible expliquant ce qui s'est passé et comment y remédier.
  • Une icône ou un symbole en complément du changement de couleur (par ex., un ✕ ou une icône d'avertissement).
  • aria-invalid="true" sur le contrôle pour que les lecteurs d'écran l'annoncent comme invalide.
  • aria-describedby pointant vers l'élément du message d'erreur pour que la description soit lue automatiquement.
<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>

Le role="alert" sur le span d'erreur amène les lecteurs d'écran à annoncer le message dès qu'il apparaît dans le DOM, même sans focus.

Pseudo-classes associées

Pseudo-classeCorrespond quand…
:validle contrôle passe toutes ses contraintes
:requiredle contrôle possède l'attribut required
:optionalle contrôle ne possède pas required
:out-of-rangela valeur d'un input numérique/date dépasse min/max
:in-rangela valeur d'un input numérique/date est dans la plage min/max
:placeholderle texte de l'espace réservé d'un input
:focusle contrôle a actuellement le focus clavier

Compatibilité navigateurs

:invalid fait partie de Selectors Level 4 et est supporté dans tous les principaux navigateurs depuis de nombreuses années. :user-invalid (la variante sensible aux interactions) est disponible depuis Chrome 119, Firefox 88 (en tant que :-moz-ui-invalid bien plus tôt), et Safari 16.5.

Pour en savoir plus sur les attributs de contrainte natifs HTML, voir <input> et HTML Forms.

Pratique

Pratique
Quelle est la fonction de la pseudo-classe ':invalid' en CSS ?
Quelle est la fonction de la pseudo-classe ':invalid' en CSS ?
Was this page helpful?