Les navigateurs exécutent la validation des formulaires de manière native ; la plupart des équipes réimplémentent ensuite ses visuels en JavaScript. CSS peut styliser directement les machines, ce qui convient parfaitement à un site statique.

/* touched, wrong */
input:user-invalid { border-color: #bb0000; }
/* untouched but already empty by design */
input:placeholder-shown { border-color: #d9d9d9; }
/* the whole form is submittable */
form:has(:invalid) .save { opacity: .4; pointer-events: none; }

Composez les états honnêtement

  1. Mauvaise valeur : :user-invalid, donc les champs ne crient jamais avant que le lecteur ne les touche.
  2. Formulaire prêt : form:has(:invalid) désactive le bouton d'enregistrement tant qu'un champ requis échoue.
  3. Vide contre erreur : attribuez aux entrées un attribut d'espace réservé afin que :placeholder-shown sépare "vide" de "invalide".
  4. Texte d'erreur : associez le style à un message visible ; une bordure rouge à elle seule échoue à tous les contrôles.

Gardez la sémantique réelle : le navigateur bloque aussi la soumission nativement, donc le CSS peint un comportement déjà jugé, il ne le remplace pas. Le support de :has() mesurait 95,91 pour cent sur caniuse dans ce dépôt, et le formulaire valide tout aussi bien sans lui.