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

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 !

  1. Amusez-vous à commenter le second sélecteur pour voir le comportement ordinaire. ↩︎

  2. Pour la question du support et de la tolérance à l’erreur, Chris Coyier préfère évoquer @supports plutôt que d’accumuler les sélecteurs. Ça ne me paraît pas nécessaire dans le cas présent, mais à réfléchir ! ↩︎

Article rédigé par . Publié le et modifié le .