Back

Styliser les états valides et invalides des formulaires avec CSS

Styliser les états valides et invalides des formulaires avec CSS

Des champs obligatoires vides qui s’illuminent en rouge avant même que les utilisateurs ne saisissent un seul caractère. C’est l’expérience frustrante que proposent de nombreux formulaires — et elle est entièrement évitable avec le CSS moderne.

Ce guide couvre les états de validation des formulaires CSS et les pseudo-classes qui les contrôlent. Vous apprendrez quand utiliser :valid et :invalid, pourquoi :user-valid et :user-invalid offrent une meilleure expérience utilisateur, et comment styliser les erreurs de formulaire avec CSS en utilisant des patterns comme :has() pour le stylage des éléments parents.

Points clés à retenir

  • La validation par contraintes HTML5 fonctionne automatiquement avec des attributs comme required, type, min, max et pattern
  • Évitez :valid et :invalid pour le stylage des erreurs car ils se déclenchent avant l’interaction de l’utilisateur
  • Utilisez :user-valid et :user-invalid pour afficher les retours de validation uniquement après que les utilisateurs ont interagi avec les champs
  • Combinez :has() avec les pseudo-classes de validation pour styliser les éléments parents et contrôler la visibilité des messages d’erreur
  • Associez toujours la validation visuelle avec des attributs ARIA pour l’accessibilité aux lecteurs d’écran

Comment fonctionne la validation par contraintes

La validation par contraintes HTML5 se produit automatiquement. Lorsque vous ajoutez des attributs comme required, type="email", min, max, minlength, maxlength ou pattern aux champs de formulaire, les navigateurs évaluent la validité en continu.

Le navigateur vérifie les contraintes chaque fois que la valeur de l’input change. Les pseudo-classes CSS reflètent cet état de validité en temps réel, vous permettant de styliser les champs selon qu’ils réussissent ou échouent la validation.

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

Ce champ devient invalide s’il est vide, si la valeur n’est pas au format email, ou s’il contient moins de cinq caractères.

Pseudo-classes CSS de base pour les formulaires

:valid et :invalid

Ces pseudo-classes ciblent les champs en fonction de leur validité actuelle :

input:valid {
  border-color: green;
}

input:invalid {
  border-color: red;
}

Le problème : :invalid s’applique immédiatement au chargement de la page. Un champ obligatoire sans valeur est techniquement invalide avant que l’utilisateur ne le touche. Cela crée une mauvaise expérience — les utilisateurs voient des erreurs avant d’avoir fait quoi que ce soit de mal.

:required et :optional

Ces pseudo-classes ciblent les champs en fonction de l’attribut required :

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

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

Utiles pour des indicateurs visuels montrant quels champs doivent être complétés.

:in-range et :out-of-range

Pour les inputs de type number et range avec des contraintes min/max :

input:in-range {
  background: white;
}

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

Meilleure expérience utilisateur avec :user-valid et :user-invalid

Les pseudo-classes :user-valid et :user-invalid résolvent le problème d’affichage prématuré des erreurs. Elles ne correspondent qu’après que l’utilisateur a interagi avec le champ — typiquement après l’avoir modifié et quitté.

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

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

Maintenant, les champs obligatoires n’affichent pas d’erreurs au chargement de la page. Le stylage de validation n’apparaît qu’après que les utilisateurs ont eu la possibilité de saisir des données. Cela correspond aux attentes des utilisateurs et réduit la frustration.

Ces pseudo-classes bénéficient d’un large support navigateur et devraient être votre choix par défaut pour styliser les erreurs de formulaire avec CSS.

Stylage des éléments parents avec :has()

Le sélecteur :has() permet de styliser les éléments parents en fonction des états de validation des enfants — quelque chose auparavant impossible sans JavaScript.

<div class="field">
  <label for="email">Email</label>
  <input type="email" id="email" required />
  <span class="error">Veuillez saisir un email valide</span>
</div>
.error {
  display: none;
  color: red;
}

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

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

Ce pattern affiche les messages d’erreur uniquement lorsque l’input contenu échoue la validation après l’interaction de l’utilisateur. Le conteneur parent contrôle la visibilité des éléments d’erreur frères.

Considérations d’accessibilité

Le stylage visuel seul ne suffit pas pour des formulaires accessibles. Les utilisateurs de lecteurs d’écran ont besoin d’associations d’erreurs programmatiques.

Associez les états de validation CSS avec les attributs ARIA appropriés :

<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">
    Veuillez saisir un email valide
  </span>
</div>

Exigences clés d’accessibilité :

  • aria-describedby lie l’input à son message d’erreur
  • aria-live="polite" annonce les changements d’erreur aux lecteurs d’écran
  • aria-invalid doit être mis à jour via JavaScript lorsque la validation échoue
  • Le contraste des couleurs doit respecter les exigences WCAG (4,5:1 pour le texte)
  • Ne vous fiez pas uniquement à la couleur — utilisez des icônes ou du texte en complément des changements de couleur

Pattern pratique : validation complète de champ

Voici un pattern prêt pour la production combinant ces techniques :

/* Styles de base du champ */
.field input {
  border: 2px solid #ccc;
  transition: border-color 0.2s;
}

/* État valide après interaction */
.field:has(input:user-valid) input {
  border-color: #2e7d32;
}

/* État invalide après interaction */
.field:has(input:user-invalid) input {
  border-color: #c62828;
}

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

/* Message d'erreur masqué par défaut */
.error {
  display: none;
  color: #c62828;
  font-size: 0.875rem;
  margin-top: 0.25rem;
}

Cette approche garde les messages d’erreur masqués jusqu’à ce que les utilisateurs interagissent avec les champs, puis les révèle uniquement lorsque la validation échoue.

Conclusion

Les pseudo-classes CSS modernes pour les formulaires offrent un stylage de validation puissant sans JavaScript. Utilisez :user-valid et :user-invalid comme outils principaux — ils préviennent l’affichage prématuré des erreurs et correspondent aux attentes des utilisateurs. Combinez-les avec :has() pour un stylage au niveau parent qui contrôle la visibilité des messages d’erreur.

Rappelez-vous que le stylage visuel complète mais ne remplace pas la gestion accessible des erreurs. Associez toujours les états de validation CSS avec les attributs ARIA appropriés pour les utilisateurs de lecteurs d’écran.

FAQ

La pseudo-classe :invalid correspond à tout champ qui échoue la validation, y compris au chargement de la page avant l'interaction de l'utilisateur. La pseudo-classe :user-invalid ne correspond qu'après que l'utilisateur a interagi avec le champ et l'a quitté. Cela empêche d'afficher des styles d'erreur sur des champs obligatoires vides avant que les utilisateurs n'aient eu la possibilité de les remplir.

Ces pseudo-classes bénéficient d'un large support dans les navigateurs modernes incluant Chrome, Firefox, Safari et Edge. Pour le support des navigateurs plus anciens, envisagez d'utiliser les pseudo-classes :focus et :not(:placeholder-shown) comme solution de repli, bien que cette approche soit moins précise.

Les pseudo-classes de validation CSS reflètent l'état de l'API de validation par contraintes du navigateur. Si vous empêchez la soumission par défaut du formulaire ou utilisez JavaScript pour gérer les formulaires, assurez-vous d'appeler checkValidity() sur les éléments du formulaire. Les états CSS se mettent à jour en fonction de la propriété validity, pas des tentatives de soumission.

Utilisez le sélecteur :has() sur un conteneur parent pour détecter les états de validation des inputs enfants. Par exemple, .field:has(input:user-invalid) .error-message vous permet d'afficher ou de masquer des éléments d'erreur frères en fonction de si l'input à l'intérieur de ce conteneur échoue la validation après l'interaction de l'utilisateur.

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