Guide des sélecteurs de pseudo-classes CSS modernes nouvellement pris en charge

Publié: 2022-03-10
Résumé rapide ↬ Le brouillon de l'éditeur du groupe de travail CSS pour les sélecteurs de niveau 4 comprend plusieurs sélecteurs de pseudo-classe qui ont déjà des candidats de proposition dans la plupart des navigateurs modernes. Ce guide couvrira ceux qui bénéficient actuellement du meilleur support, ainsi que des exemples pour montrer comment vous pouvez commencer à les utiliser dès aujourd'hui !

Les sélecteurs de pseudo-classe sont ceux qui commencent par le caractère deux-points " : " et correspondent en fonction d'un état de l'élément actuel. L'état peut être relatif à l'arborescence du document, ou en réponse à un changement d'état tel que :hover ou :checked .

:any-link

Bien que définie dans les sélecteurs de niveau 4, cette pseudo-classe est prise en charge par plusieurs navigateurs depuis un certain temps. La pseudo-classe any-link correspondra à un lien hypertexte d'ancrage tant qu'il a un href . Il correspondra d'une manière équivalente à la correspondance :link et :visited à la fois. Essentiellement, cela peut réduire vos styles d'un sélecteur si vous ajoutez des propriétés de base telles que color que vous souhaitez appliquer à tous les liens, quel que soit leur statut visité.

 :any-link { color: blue; text-underline-offset: 0.05em; }

Une remarque importante à propos de la spécificité est que :any-link l'emportera contre a tant que sélecteur même si a est placé plus bas dans la cascade puisqu'il a la spécificité d'une classe. Dans l'exemple suivant, les liens seront violets :

 :any-link { color: purple; } a { color: red; }

Donc, si vous introduisez :any-link , sachez que vous devrez l'inclure sur les instances de a tant que sélecteur s'ils seront en concurrence directe pour la spécificité.

:focus-visible

Je parierais que l'une des violations d'accessibilité les plus courantes sur le Web consiste à supprimer le outline des éléments interactifs tels que les liens, les boutons et les entrées de formulaire pour leur état :focus . L'un des principaux objectifs de ce outline est de servir d'indicateur visuel pour les utilisateurs qui utilisent principalement des claviers pour naviguer. Un état de mise au point visible est essentiel en tant qu'outil d'orientation lorsque ces utilisateurs se déplacent sur une interface et pour aider à renforcer ce qui est un élément interactif. Plus précisément, la mise au point visible est couverte par le critère de succès 2.4.11 des WCAG : Apparence de la mise au point (minimum).

La pseudo-classe :focus-visible est destinée à n'afficher un anneau de focus que lorsque l'agent utilisateur détermine via une heuristique qu'il doit être visible. En d'autres termes : les navigateurs détermineront quand appliquer :focus-visible en fonction d'éléments tels que la méthode de saisie, le type d'élément et le contexte de l'interaction. À des fins de test via un ordinateur de bureau avec saisie au clavier et à la souris, vous devriez voir les styles :focus-visible attachés lorsque vous tabulez dans un élément interactif mais pas lorsque vous cliquez dessus, à l'exception des entrées de texte et des zones de texte qui doivent afficher :focus-visible pour tous les types d'entrée de mise au point.

Note : Pour plus de détails, consultez le brouillon de la spécification :focus-visible .

Les dernières versions des navigateurs Firefox et Chromium semblent désormais gérer :focus-visible sur les entrées de formulaire conformément à la spécification qui indique que l'UA doit supprimer les styles : :focus lorsque :focus-visible correspond. Safari ne prend pas encore en charge :focus-visible , nous devons donc nous assurer qu'un style :focus est inclus comme alternative pour éviter de supprimer le outline pour l'accessibilité.

Plus après saut! Continuez à lire ci-dessous ↓

Étant donné un bouton et une entrée de texte avec l'ensemble de styles suivant, voyons ce qui se passe :

 input:focus, button:focus { outline: 2px solid blue; outline-offset: 0.25em; } input:focus-visible { outline: 2px solid transparent; border-color: blue; } button:focus:not(:focus-visible) { outline: none; } button:focus-visible { outline: 2px solid transparent; box-shadow: 0 0 0 2px #fff, 0 0 0 4px blue; }

Chrome et Firefox

  • input
    Supprimez correctement les styles :focus lorsque les éléments sont mis au point via l'entrée de la souris en faveur de :focus-visible ce qui entraîne la modification de la border-color et le masquage du outline lors de l'entrée au clavier
  • button
    N'utilise pas seulement :focus-visible sans la règle supplémentaire pour button:focus:not(:focus-visible) qui supprime le contour sur :focus , mais permettra la visibilité de la box-shadow uniquement sur la saisie au clavier

Safari

  • input
    Continue à n'utiliser que les styles :focus
  • button
    Cela semble désormais respecter partiellement l'intention de :focus-visible sur le bouton en masquant les styles :focus au clic, mais en affichant toujours les styles :focus lors de l'interaction avec le clavier

Donc, pour l'instant, la recommandation serait de continuer à inclure les styles :focus , puis de les améliorer progressivement jusqu'à utiliser :focus-visible , ce que le code de démonstration permet. Voici un CodePen avec lequel vous pouvez continuer à tester :

Voir le Pen [Application de test de :focus-visible](https://codepen.io/smashingmag/pen/MWJZbew) par Stephanie Eckles.

Voir l'application Pen Testing de :focus-visible par Stephanie Eckles.

:focus-within

La pseudo-classe :focus-within est prise en charge par tous les navigateurs modernes et agit presque comme un sélecteur parent, mais uniquement pour une condition très spécifique. Lorsqu'ils sont attachés à un élément conteneur et qu'un élément enfant correspond à :focus , des styles peuvent être ajoutés à l'élément conteneur et/ou à tout autre élément du conteneur.

Une amélioration pratique pour utiliser ce comportement consiste à styliser une étiquette de formulaire lorsque l'entrée associée a le focus. Pour que cela fonctionne, nous enveloppons l'étiquette et l'entrée dans un conteneur, puis attachons :focus-within à ce conteneur et sélectionnons l'étiquette :

 .form-group:focus-within label { color: blue; }

Il en résulte que l'étiquette devient bleue lorsque l'entrée a le focus.

Cette démo CodePen inclut également l'ajout d'un plan directement au conteneur .form-group :

Voir le Pen [application de test de :focus-within](https://codepen.io/smashingmag/pen/xxgmREq) par Stephanie Eckles.

Voir l'application Pen Testing de :focus-within par Stephanie Eckles.

:is()

Également connue sous le nom de pseudo-classe "correspond à n'importe quel", :is() peut prendre une liste de sélecteurs pour essayer de faire correspondre. Par exemple, au lieu de lister les styles de titre individuellement, vous pouvez les regrouper sous le sélecteur de :is(h1, h2, h3) .

Quelques comportements uniques à propos de la liste de sélecteurs :is() :

  • Si un sélecteur répertorié n'est pas valide, la règle continuera à correspondre aux sélecteurs valides. Étant donné :is(-ua-invalid, article, p) la règle correspondra à article et p .
  • La spécificité calculée sera égale à celle du sélecteur passé avec la spécificité la plus élevée. Par exemple, :is(#id, p) aura la spécificité de #id — 1.0.0 — tandis que :is(p, a) aura une spécificité de 0.0.1.

Le premier comportement consistant à ignorer les sélecteurs non valides est un avantage clé. Lors de l'utilisation d'autres sélecteurs dans un groupe où un sélecteur est invalide, le navigateur rejettera toute la règle. Cela entre en jeu pour quelques cas où les préfixes de fournisseur sont encore nécessaires, et le regroupement de sélecteurs préfixés et non préfixés entraîne l'échec de la règle parmi tous les navigateurs. Avec :is() vous pouvez regrouper ces styles en toute sécurité et ils s'appliqueront lorsqu'ils correspondent et seront ignorés lorsqu'ils ne le font pas.

Pour moi, regrouper les styles de titre comme mentionné précédemment est déjà une grande victoire avec ce sélecteur. C'est aussi le type de règle que je me sentirais à l'aise d'utiliser sans repli lors de l'application de styles non critiques, tels que :

 :is(h1, h2, h3) { line-height: 1.2; } :is(h2, h3):not(:first-child) { margin-top: 2em; }

Dans cet exemple (qui provient des styles de document de mon projet SmolCSS), le fait d'avoir la plus grande line-height héritée des styles de base ou l'absence de margin-top n'est pas vraiment un problème pour les navigateurs non compatibles. C'est tout simplement loin d'être idéal. Ce que vous ne voudriez pas encore utiliser :is() serait des styles de mise en page critiques tels que Grid ou Flex qui contrôlent considérablement votre interface.

De plus, lorsqu'il est enchaîné à un autre sélecteur, vous pouvez tester si le sélecteur de base correspond à un sélecteur descendant dans :is() . Par exemple, la règle suivante sélectionne uniquement les paragraphes qui sont des descendants directs d'articles. Le sélecteur universel sert de référence au sélecteur de base p .

 p:is(article > *)

Pour le meilleur support actuel, si vous souhaitez commencer à l'utiliser, vous voudrez également doubler les styles en incluant des règles de duplication à l'aide de :-webkit-any() et :matches() . N'oubliez pas de créer ces règles individuelles, sinon le navigateur de support les supprimera ! En d'autres termes, incluez tous les éléments suivants :

 :matches(h1, h2, h3) { } :-webkit-any(h1, h2, h3) { } :is(h1, h2, h3) { }

Il convient de mentionner à ce stade qu'avec les nouveaux sélecteurs eux-mêmes, il existe une variante mise à jour de @supports qui est @supports selector . Ceci est également disponible en tant que @supports not selector .

Note : A l'heure actuelle (parmi les navigateurs modernes), seul Safari ne supporte pas cette règle-at.

Vous pouvez vérifier la prise en charge de :is() avec quelque chose comme ce qui suit, mais vous perdriez en fait la prise en charge de Safari puisque Safari prend en charge :is() mais ne prend pas en charge @supports selector .

 @supports selector(:is(h1)) { :is(h1, h2, h3) { line-height: 1.1; } }

:where()

La pseudo-classe :where() est presque identique à :is() à l'exception d'une différence critique : elle aura toujours une spécificité nulle. Cela a des implications incroyables pour les personnes qui créent des frameworks, des thèmes et des systèmes de conception . En utilisant :where() , un auteur peut définir des valeurs par défaut et les développeurs en aval peuvent inclure des remplacements ou des extensions sans conflit de spécificité.

Considérez l'ensemble suivant de styles img . En utilisant :where() , même avec un sélecteur de spécificité plus élevé, la spécificité reste nulle. Dans l'exemple suivant, quelle couleur de bordure pensez-vous que l'image aura ?

 :where(article img:not(:first-child)) { border: 5px solid red; } :where(article) img { border: 5px solid green; } img { border: 5px solid orange; }

La première règle n'a aucune spécificité puisqu'elle est entièrement contenue dans :where() . Donc directement contre la deuxième règle, la deuxième règle l'emporte. En introduisant le sélecteur d'élément img uniquement comme dernière règle, il va gagner en raison de la cascade. En effet, il calculera avec la même spécificité que la règle :where(article) img puisque la partie :where() n'augmente pas la spécificité.

L'utilisation de :where() aux côtés des solutions de repli est un peu plus difficile en raison de la fonctionnalité de spécificité zéro, car cette fonctionnalité est probablement la raison pour laquelle vous voudriez l'utiliser sur :is() . Et si vous ajoutez des règles de secours, celles-ci sont susceptibles de battre :where() en raison de sa nature même. Et, il a un meilleur support global que le @supports selector donc essayer de l'utiliser pour créer un repli n'apportera probablement pas beaucoup (le cas échéant) de gain. Fondamentalement, soyez conscient de l'incapacité de créer correctement des solutions de secours pour :where() et vérifiez soigneusement vos propres données pour déterminer s'il est sûr de commencer à l'utiliser pour votre public unique.

Vous pouvez tester davantage :where() avec le CodePen suivant qui utilise les sélecteurs img ci-dessus :

Voir le Pen [Testing `:where()` specificity](https://codepen.io/smashingmag/pen/jOyXVMg) par Stephanie Eckles.

Voir la spécificité Pen Testing :where() par Stephanie Eckles.

Amélioré :not()

Le sélecteur de base :not() est pris en charge depuis Internet Explorer 9. Mais les sélecteurs de niveau 4 améliorent :not() en lui permettant de prendre une liste de sélecteurs, tout comme :is() et :where() .

Les règles suivantes fournissent le même résultat dans les navigateurs compatibles :

 article :not(h2):not(h3):not(h4) { margin-bottom: 1.5em; } article :not(h2, h3, h4) { margin-bottom: 1.5em; }

La capacité de :not() à accepter une liste de sélecteurs est très bien prise en charge par les navigateurs modernes.

Comme nous l'avons vu avec :is() , l'amélioration :not() peut également contenir une référence au sélecteur de base en tant que descendant en utilisant * . Ce CodePen démontre cette capacité en sélectionnant des liens qui ne sont pas des descendants de nav .

Voir le Pen [Tester :not() avec un sélecteur descendant](https://codepen.io/smashingmag/pen/BapvQQv) par Stephanie Eckles.

Voir Pen Testing :not() avec un sélecteur descendant par Stephanie Eckles.

Bonus : La démo précédente comprend également un exemple de chaînage :not() et :is() pour sélectionner des images qui ne sont pas des frères et sœurs adjacents des éléments h2 ou h3 .

Proposé mais « à risque » — :has()

La pseudo-classe finale qui est une proposition très excitante mais qui n'a pas de navigateur actuel l'implémentant même de manière expérimentale est :has() . En fait, il est répertorié dans le brouillon de l'éditeur de niveau 4 du sélecteur comme « à risque », ce qui signifie qu'il est reconnu qu'il a des difficultés à terminer sa mise en œuvre et qu'il peut donc être retiré de la recommandation.

S'il est implémenté, :has() serait essentiellement le "sélecteur parent" que de nombreux utilisateurs de CSS ont souhaité avoir à disposition. Cela fonctionnerait avec une logique similaire à une combinaison de :focus-within et :is() avec des sélecteurs descendants, où vous recherchez la présence de descendants mais le style appliqué serait à l'élément parent.

Étant donné la règle suivante, si la navigation contenait un bouton, la navigation aurait diminué le rembourrage supérieur et inférieur :

 nav { padding: 0.75rem 0.25rem; nav:has(button) { padding-top: 0.25rem; padding-bottom: 0.25rem; }

Encore une fois, cela n'est actuellement implémenté dans aucun navigateur, même expérimentalement - mais c'est amusant d'y penser ! Robin Rendle a fourni des informations supplémentaires sur ce futur sélecteur sur CSS-Tricks.

Mention honorable du niveau 3 : :empty

Une pseudo-classe utile que vous avez peut-être manquée dans les sélecteurs de niveau 3 est :empty qui correspond à un élément lorsqu'il n'a pas d'éléments enfants, y compris des nœuds de texte.

La règle p:empty correspondra à <p></p> mais pas à <p>Hello</p> .

Une façon d'utiliser :empty consiste à masquer des éléments qui sont peut-être des espaces réservés pour le contenu dynamique rempli de JavaScript. Peut-être avez-vous une div qui recevra des résultats de recherche, et quand elle sera peuplée, elle aura une bordure et un rembourrage. Mais sans résultats pour le moment, vous ne voulez pas qu'il occupe de l'espace sur la page. En utilisant :empty vous pouvez le cacher avec :

 .search-results:empty { display: none; }

Vous pensez peut-être ajouter un message à l'état vide et être tenté de l'ajouter avec un pseudo-élément et content . L'écueil ici est que les messages peuvent ne pas être disponibles pour les utilisateurs de technologies d'assistance qui sont incohérents quant à savoir s'ils peuvent accéder au content . En d'autres termes, pour vous assurer qu'un type de message "sans résultat" est accessible , vous voudriez l'ajouter en tant qu'élément réel comme un paragraphe (une aria-label ne serait plus accessible pour une div masquée).

Ressources pour en savoir plus sur les sélecteurs

CSS a beaucoup plus de sélecteurs, y compris des pseudo-classes. Voici quelques endroits supplémentaires pour en savoir plus sur ce qui est disponible :

  • La documentation des sélecteurs CSS MDN comprend une liste catégorisée complète ;
  • J'ai écrit un guide en deux parties sur les sélecteurs CSS avancés, vous pouvez commencer par la première partie ;
  • Amusez-vous à découvrir les sélecteurs CSS avec le jeu CSS Diner ;
  • Kitty Giraudel a créé un outil d'explication de sélecteur qui décompose et décrit les parties d'un sélecteur fourni.