Mais en arrivant sur la page, ledit champ est vide — et par conséquent, invalide.
Le souci
Lorsque vous appliquez des styles à un champ invalide (en utilisant la pseudo-classe :invalid
), ils seront donc appliqués dès le chargement de la page si ce champ est également affublé de l’attribut required
. Dommage !
C’est ce qui a conduit Stéphane Deschamps à ouvrir une issue sur le Github du W3C.
L’état indéterminé
Quelques échanges plus tard, on évoque l’existence de l’état indéterminé pour les cases à cocher, boutons radios et barres de progression dans la spécification HTML5 qui pourrait porter le statu quo sur l’invalidité de notre champ. Cet état est facile à cibler en CSS à l’aide de la pseudo-classe :indeterminate
.
Le brouillon du module 4 des sélecteurs
Dans le brouillon du module de niveau 4 des sélecteurs CSS, une nouvelle pseudo-classe tente de résoudre ce problème : :user-invalid
. Vous noterez par ailleurs que le paragraphe descriptif n’est pas à jour puisqu’il évoque la première dénomination de ce nouveau sélecteur, à savoir :user-error
.
Mais vous l’aurez compris, il s’agit d’un brouillon et ce nouveau sélecteur est très loin d’avoir été implémenté où que ce soit.
Un contournement possible
Une idée m’est venue en lisant un article du calendrier de l’avent Digitpaint intitulé Fantastic form pseudo selectors, dans lequel le premier exemple implémente un label flottant non pas au :focus
sur le champ, mais lors du début de saisie grâce à la pseudo-classe :placeholder-shown
. Génie !
Cette pseudo-classe est active uniquement lorsque le placeholder
est affiché, autrement dit tant qu’aucune saisie n’a été effectuée dans le champ. Et ça, ça ressemble beaucoup à une partie de l’énoncé de notre problème du jour. Nous pourrions donc faire en sorte de distinguer un champ invalide vierge d’un champ invalide ayant été saisi, avec les sélecteurs suivants :
input:invalid {
box-shadow: none;
}
input:invalid:not(:focus) {
box-shadow: 0 0 0 2px red;
}
input:invalid:placeholder-shown {
box-shadow: none;
}
Et bien a priori, ça fonctionne !
Et bonne nouvelle, :placeholder-shown
est décemment supporté. Je vous ai même fait un petit CodePen pour jouer.[1]
Remarques post-publication
- Note : Merci à Vincent qui me fait réfléchir, dans une discussion sur Twitter.
- Note : Gaëtan me signale que sur Chrome, le placeholder ne peut pas être vide pour que cette astuce fonctionne.
- Note : Johan me signale que le comportement observé de visu était également vocalisé par les lecteurs d’écran. Là, ça devient vraiment gênant !
Pourquoi pas :not()
Peut-être supposez-vous (à juste titre) qu’au lieu d’utiliser deux sélecteurs différents, nous pourrions styler directement les champs invalides dont le placeholder
n’est pas affiché, grâce à input:invalid:not(:placeholder-shown)
.
Le fait est que le support de :invalid
est bien meilleur que celui de :placeholder-shown
; de plus, vous n’êtes pas sans savoir que CSS est un langage tolérant à l’erreur. Quand un navigateur rencontre un sélecteur qu’il ne comprend pas, il ignore l’ensemble du bloc de déclaration concerné — et ce pour une bonne raison : être capable d’appliquer les styles suivants.
Ainsi les navigateurs ne comprenant pas :placeholder-shown
(Internet Explorer et Edge au premier rang) n’appliqueraient pas nos styles dédiés aux champs invalides. Et ça, a priori, ce n’est pas ce qu’on veut ! Alors en utilisant deux sélecteurs, le seul inconvénient est que les navigateurs ne comprenant pas le second sélecteur conservent leur comportement actuel.
Et ça, c’est mieux.
Au final
Je ne pense pas que ce soit une bonne idée, je tiens à le préciser. Cette combinaison n’a pas vocation à être employée de la sorte, ce n’est qu’un détournement un peu tordu, il est vrai. Mais bon, j’aime les petits défis en CSS, ne m’en voulez pas !
Un précédent
Après de petites recherches sur les internets, j’ai trouvé plusieurs références à cette combinaison de sélecteurs antérieures à cet article, toutes remontant finalement à un article sur la validation des formulaires en CSS rédigé par Chris Coyier en 2016.[2]
Un véritable CSS trick !
We think that a third indeterminate state should be added, and the browser should, for instance, consider the field as invalid only when one tabs/clicks out of it (AKA when the blur event is triggered) or when one submits the form.
Pour la petite histoire, ce serait un *état*, et pas seulement un moyen de faire un rendu visuel propre mais aussi de donner la bonne information aux agents utilisateurs qui en ont besoin (au hasard : les technologies d’assistance).