La Cascade

Un peu de tout sur CSS, HTML, SVG et JS,traduit du web anglophone
Rechercher

Un monde de possibilités nouvelles avec CSS :has()

par Stephanie Eckles, 28 janvier 2023, css, pseudo-classes, article original paru le 23 janvier 2023 dans Smashing Magazine

Le sélecteur relationnel CSS nous offre ce qui était auparavant impossible sans JavaScript. Nous examinerons comment combiner :has() avec d'autres sélecteurs CSS et les pouvoirs magiques qu'il apporte.


L'utilisation de la pseudo-classe :has() nous permet de "regarder en avant" avec CSS et de styliser un élément parent ou ancêtre. Nous pouvons ensuite élargir le sélecteur pour cibler un ou plusieurs éléments frères ou enfants. En utilisant l'état ou la position des éléments, nous pouvons styliser pratiquement n'importe quelle combinaison d'éléments comme des éléments uniques ou des plages.

La compatibilité de :has() s'améliore rapidement, :has() est disponible depuis Safari 15.4 et Chrome/Edge 105. Elle se cache également derrière un drapeau dans Firefox à partir de la version 103. Jusqu'à ce que la prise en charge complète soit disponible, consultez ce conseil de Bramus Van Damme sur la prise en charge de :has() aujourd'hui.

Comment :has() fonctionne avec les combinateurs et les pseudo-classes

Pour mieux comprendre le fonctionnement des sélecteurs avancés que nous allons créer, nous allons passer rapidement en revue les combinateurs et les pseudo-classes les plus pertinents.

Un "combinateur" est un caractère spécial qui désigne le type de relation entre les parties du sélecteur. Voici les principaux combinateurs à connaître :

  • caractère espace : le combinateur descendant correspond à un enfant direct ou imbriqué ;
  • > : le combinateur d'enfant direct correspond uniquement aux enfants de niveau supérieur, non imbriqués ;
  • + : le combinateur de frère ou sœur adjacent(e) ne correspond qu'au frère ou sœur le plus proche ;
  • ~ : le combinateur sibling général correspond à un ou plusieurs frères et sœurs suivant le sélecteur de base.

La première étape de la création de sélecteurs complexes consiste à ajouter une pseudo-classe en une ou plusieurs parties. Une "pseudo-classe" définit un état spécial d'un élément, comme :hover, et a le format d'un simple deux-points suivi du nom. La pseudo-classe :has() est appelée fonctionnelle car elle accepte un argument. Plus précisément, elle accepte comme argument une liste de sélecteurs, qu'ils soient simples comme img ou complexes avec des combinateurs comme img + p.

:has() est l'une des quatre pseudo-classes fonctionnelles, les autres étant :is(), :where() et :not(). Chacune prend une liste de sélecteurs ainsi que quelques autres caractéristiques uniques.

Si vous avez déjà utilisé :is() et :where(), c'était probablement pour gérer la spécificité. L'utilisation de :is() signifie que le sélecteur de la liste ayant la spécificité la plus élevée donne son poids à l'ensemble du sélecteur. L'utilisation de :where() quant à elle confère à l'ensemble de la liste de sélecteurs une spécificité nulle, ce qui permet aux règles ultérieures de la cascade de l'ignorer.

Par ailleurs, :is() et :where() ont la capacité spéciale d'être des sélecteurs indulgents, ce qui signifie que vous pouvez inclure (volontairement ou non) des sélecteurs que le navigateur ne comprend pas, et il traitera quand même les parties qu'il comprend. Sans ce comportement indulgent, le navigateur rejetterait la règle entière.

L'autre avantage de :is() et de :where() est de créer des sélecteurs succincts et complexes. C'est particulièrement pratique lorsque vous utilisez des combinateurs et que vous affectez plusieurs frères et sœurs ou descendants, par exemple, article :is(h1, h2, h3).

Notre dernière pseudo-classe, :not(), est disponible depuis plus longtemps. Toutefois, parallèlement aux sélecteurs de niveau 4, lorsque :is() et :where() ont été publiés, :not() a été améliorée. Elle a été autorisée à accepter une liste de sélecteurs au lieu d'un seul sélecteur. Elle a également le même comportement de spécificité que :is().

Enfin, nous devons connaître une fonctionnalité sous-utilisée et incroyablement puissante de :is(), :where() et :not(), que nous utiliserons pour créer nos sélecteurs :has() avancés. L'utilisation du caractère * dans ces sélecteurs — qui, normalement, en CSS, est le "sélecteur universel" — fait référence à la cible du sélecteur. Cela permet de vérifier les frères et sœurs ou les ancêtres précédents de la cible du sélecteur. Par exemple, dans img:not(h1 + *), nous sélectionnons les images qui ne suivent pas directement un h1. Et dans p:is(h2 + *), nous sélectionnons les paragraphes uniquement s'ils suivent directement un h2. Nous allons utiliser ce comportement pour notre première démo à venir.

Polyfill pour le sélecteur :only-of-type

:only-of-type est une pseudo-classe valide, (NdT : elle est décrite ainsi dans MDN : La pseudo-classe :only-of-type permet de cibler un élément qui ne possède aucun nœud frère du même type au sein d'un même élément parent). À partir du HTML ci-dessous...

<p>Not highlighted</p>
<p class="highlight">.highlight</p>
<p>Not highlighted</p>

...avec le CSS .highlight:only-of-type, aucune correspondance ne serait faite parce que la classe n'a aucun effet sur la réduction de la portée (scope).

Si le paragraphe porteur de la classe highlight était le seul paragraphe à l'intérieur de son parent, on pourrait croire que ça fonctionne. Mais ce serait une illusion car, dans ce cas, ce serait uniquement parce que le type d'élément racine auquel la classe est attachée est un paragraphe, donc il correspond comme vrai puisqu'il n'y a pas de paragraphes frères.

:has nous permet de résoudre ce problème. En combinant :has() et :not(), nous pouvons effectivement créer un sélecteur :only-of-selector qui fera correspondre un singleton dans une plage de frères et sœurs basée sur une classe ou un autre sélecteur valide.

En fin de compte, nous voulons que notre sélecteur trouve une correspondance lorsqu'il n'existe aucun frère ou sœur correspondant avant ou après la cible.

Une force de :has() est de tester ce qui suit un élément. Ici, nous voulons tester un nombre quelconque de frères et sœurs qui suivent, nous utiliserons donc le combinateur général de frères et sœurs ~ pour créer la première condition.

.highlight:not(:has(~ .highlight)

Jusqu'à présent, cela nous donne la correspondance de "classe highlight qui n'a pas de classe highlight sœur qui la suit".

Maintenant, nous devons vérifier les frères et sœurs précédents, et nous allons utiliser la capacité de :not() pour ajouter cette condition.

.highlight:not(:has(~ .highlight)):not(.highlight ~ *)

La deuxième condition :not() ajoute une clause ET à notre sélecteur qui dit "ET pas lui-même un frère ou une sœur d'une highlight précédente".

Avec cela, nous avons polyfillé la pseudo-classe inexistante :only-of-selector !

voir :only-of-selector using :has() de smashingmag dans CodePen

Sélecteur de frères et sœurs précédents

Nous avons parlé de la vérification des frères et sœurs précédents avec :not(), :is() et :where(). Avec :has(), nous pouvons en fait sélectionner et styliser les frères et sœurs précédents en fonction des conditions de ce qui vient après eux !

Pour démontrer cela, nous allons créer une liste d'éléments. Le comportement que nous souhaitons est le suivant : lorsqu'un élément de la liste est survolé, il s'agrandit et les éléments qui le précèdent et le suivent s'agrandissent également légèrement. Les autres éléments de liste non survolés doivent être réduits. L'opacité de tous les éléments de la liste, à l'exception de l'élément survolé, doit également être réduite. La vidéo suivante présente un aperçu de l'effet.



Le premier sélecteur doit correspondre à l'élément de la liste qui précède celui qui est survolé, ce que :has() rend possible. Le CSS qui suit se lit : "sélectionner l'élément liste dont le frère adjacent est survolé".

li: has(+ li: hover);

Nous allons coupler cela avec un sélecteur de base de frère ou sœur adjacent pour obtenir également l'élément liste suivant celui qui est survolé, puis appliquer nos styles :

/* Sélectionne l'élément liste avant celui qui est survolé */
li:has(+ li:hover),
/* Sélectionner l'élément liste après l'élément survolé */
li:hover + li {
  /* ...modifier l'échelle et l'opacité */
}

Le troisième sélecteur complexe que nous allons créer utilise notre combinaison de :has() et :not() mais d'une nouvelle manière. Nous qualifions d'abord le sélecteur pour qu'il ne s'applique que lorsqu'un enfant direct de l'ul (qui sera un élément liste) est survolé. Et si c'est le cas, nous sélectionnons les éléments de la liste en excluant celui qui est survolé et les éléments avant et après celui qui est survolé.

/* Lorsqu'un élément de liste est survolé,
sélectionner les éléments de la liste non survolés, ou avant/après le survol */

ul:has(> :hover) li:not(:hover, :has(+ :hover), li:hover + *) {
  /* ...modifier l'échelle et l'opacité */
}

Ceci nous montre non seulement la sélection d'un frère ou d'une sœur précédent(e) avec :has() mais aussi son utilisation pour sélectionner en fonction de l'état (state). La démo finale créera un exemple plus complexe en utilisant les états avec :has().

D'autres personnes ont exploré des exemples similaires de sélection et d'application de frères et sœurs précédents, notamment Chris Coyier, pourya et Jim Nielsen.

Sélection à l'intérieur d'une plage

Considérons maintenant une plage d'éléments frères et sœurs, comme entre h2 ou entre hr.

<article>
  <h2>Lorem, ipsum.</h2>
  <!-- h2 range starts -->
  <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit.</p>
  <p>
    Nobis iusto voluptates reiciendis molestias, illo inventore ipsum?
  </p>
  <!-- h2 range ends -->
  <h2>Lorem, ipsum dolor.</h2>
  <p>Lorem ipsum dolor sit amet.</p>
  <hr />
  <!-- hr range starts -->
  <p>Lorem ipsum dolor sit.</p>
  <p>Dolor animi nisi ut?</p>
  <p>Sunt consectetur esse quia.</p>
  <!-- hr range ends -->
  <hr />
  <p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
</article>

En utilisant :has() nous pouvons styliser :

  • Le premier élément de la plage,
  • Le dernier élément de la plage,
  • tous les éléments frères et sœurs de la plage.

Ces sélecteurs s'appuient fortement sur le combinateur général ~, qui nous permet à la fois de "regarder vers l'avant" et de styliser plusieurs frères et sœurs à la fois.

Sélectionner le premier élément de la plage

Le CSS qui suit se lit ainsi : "sélectionner le frère ou la sœur adjacent(e) du h2 tant qu'il existe un autre h2 comme frère ou sœur ultérieur(e)", ce qui correspond au paragraphe suivant directement le premier h2 dans notre exemple HTML.

article h2 + :has(~ h2)

Sélectionner le dernier élément de la plage

Le CSS qui suit se lit ainsi : " sélectionner un élément qui suit un h2 tant que son frère ou sa sœur suivant(e) est un h2", ce qui correspond au paragraphe qui précède directement le deuxième h2 dans notre exemple HTML.

article h2 ~ :has(+ h2)

Sélectionner tous les frères et sœurs dans une plage

Le sélecteur suivant est limité en ce sens qu'il ne fonctionne que pour une seule plage au sein d'un parent. En effet, lorsqu'on utilise le combinateur général de frères et sœurs sans indiquer de limite, ces frères et sœurs peuvent se trouver n'importe où après l'élément. On peut donc "sauter" d'autres éléments qui pourraient se trouver entre eux, ce qui a pour effet une plage étendue pour ce sélecteur.

Cela peut être utile si l'on est certain de n'avoir qu'un seul éventail de possibilités au sein d'un parent donné. Ce sélecteur se lit comme suit : "sélectionner tous les éléments frères et sœurs qui suivent un hr et qui ont eux-mêmes un frère et une sœur ultérieurs d'un hr", ce qui correspond aux trois paragraphes entre les éléments hr dans notre exemple HTML.

article hr ~ :has(~ hr)

Nous verrons bientôt comment fixer une limite à notre plage et comment autoriser les groupes à plages multiples dans un seul parent.

Sélection d'une seule plage complète

Dans la série de sélecteurs suivante, nous supposerons que nous avons un élément d'identification supplémentaire à utiliser pour établir le début et la fin de notre plage. Pour les besoins de la démonstration, nous avons une liste où deux éléments de liste ont l'attribut data-range. Cette technique fonctionnerait pour visualiser la fonctionnalité de sélection multiple pour un contrôle personnalisé afin d'afficher la plage des éléments dans la sélection.

<ul>
  <li>Lorem</li>
  <li data-range>Veritatis</li>
  <li>Eos</li>
  <li>Debitis</li>
  <li>Autem</li>
  <li data-range>Atque</li>
  <li>Eius</li>
  <li>Lorem</li>
  <li>Nostrum</li>
</ul>

Nous avons utilisé un attribut data pour signifier le début et la fin, cependant nous supposons qu'aucune valeur d'attribut n'a été fournie. Gardons cela à l'esprit lorsque nous examinerons la construction des sélecteurs d'éléments de début et de fin.

Pour sélectionner à la fois le début et la fin de la plage, nous pouvons simplement utiliser le sélecteur d'attribut [data-range].

Ensuite, nous pouvons réutiliser le sélecteur que nous venons de créer dans la section précédente pour sélectionner tous les frères et sœurs dans la plage.

[data-range]~: has(~[data-range]);

Pour styliser l'élément de plage de départ, le CSS qui suit se lit : "sélectionnez l'élément [data-range] qui a un frère ou une soeur [data-range] quelque part après lui" :

[data-range]: has(~[data-range]);

Et enfin, pour sélectionner l'élément final de la plage, le CSS qui suit se lit : "sélectionnez un élément de [data-range] qui suit quelque part un élément de [data-range]".

[data-range] ~ [data-range]

Dans cette démo CodePen, nous avons également réutilisé nos sélecteurs précédemment créés pour identifier le premier et le dernier élément de la plage.

Sélection de groupes de plages multiples

Maintenant, nous allons poursuivre notre démo et résoudre le problème de la sélection de plages multiples.

Auparavant, notre problème était qu'un h2 ou un hr dans une série de plus de deux ne nous donnait aucun moyen de déterminer les limites de chacune des plages.

La clé pour rendre possible les groupes à plages multiples au sein d'un seul parent est la disponibilité d'indicateurs de début et de fin distincts.

Nous utiliserons à nouveau des attributs de données sur nos éléments de liste, mais cette fois, nous leur donnerons les valeurs réelles de "start" et "end".

<ul>
  <li>Lorem</li>
  <li data-range="start">Veritatis</li>
  <li>Eos</li>
  <li>Debitis</li>
  <li>Autem</li>
  <li data-range="end">Atque</li>
  <li>Eius</li>
  <li>Lorem</li>
  <li>Nostrum</li>
</ul>

Maintenant que nous avons fourni des valeurs d'attributs explicites, nos sélecteurs de début et de fin sont assez basiques :

/* Éléments de début et de fin de plage */ [data-range] /* Élément de
départ d'une plage */ [data-range="start"] /* Élément de fin d'une
plage */ [data-range="end"]

Allons-y et marquons le premier et le dernier élément de la plage. La première modif par rapport aux versions précédentes consiste à inclure les valeurs des attributs de données de début et de fin. Deuxièmement, nous avons ajouté la condition que le style ne soit pas appliqué à nos indicateurs de début/fin avec la condition d'exclusion :not([data-range]).

/* Premier élément à l'intérieur de la plage */ [data-range="start"] +
:has(~ [data-range="end"]):not([data-range]) /* Dernier élément à
l'intérieur de la plage */ [data-range="start"] ~ :has(+
[data-range="end"]):not([data-range])

Enfin, notre sélecteur doit faire correspondre les éléments à l'intérieur de notre plage. Au départ, il est similaire à ce que nous avons créé précédemment pour les sélecteurs "à l'intérieur de la plage". Une fois encore, nous ajoutons la condition selon laquelle il ne doit pas correspondre à un élément qui est lui-même une [data-range].

[data-range="start"] ~ :has(~ [data-range="end"]):not([data-range])

Mais si vous vous souvenez, j'ai mentionné que le sélecteur général de frères et sœurs a la capacité de "sauter", donc pour l'instant, ce sélecteur va styliser les éléments en dehors de la limite de notre plage prévue. L'image montre comment la règle fonctionne sans autres restrictions.

La liste des éléments a une plage et quelques éléments au milieu, et le début d'une autre plage. Le sélecteur précédent tente d'appliquer une couleur d'arrière-plan aux éléments d'une plage, mais comme le montrent les étiquettes au-dessus de l'image, la couleur d'arrière-plan s'applique à l'intérieur de la plage mais elle est également ajoutée aux éléments situés entre les plages.

Pour résoudre ce problème, nous devons ajouter une condition ET complexe à l'aide de :not() afin d'exclure les éléments qui ne se trouvent pas entre [data-range="end"] et [data-range="start"], dans cet ordre.

Seule, cette partie du sélecteur se lit comme suit : "ne pas sélectionner les éléments qui suivent [data-range="end"] et qui ont également un frère ou une soeur plus tardif(ve) de [data-range="start"]".

/* Note : ceci doit être ajouté au sélecteur précédent, et non utilisé seul */
:not([data-range="end"] ~ :has(~ [data-range="start"]))

Au total, cela donne un sélecteur certes long, mais très puissant, qui n'était pas possible avant :has() sans utiliser JavaScript, en raison de l'absence des capacités de "regarder devant" et "regarder derrière" dans CSS.

/* Sélectionner tout ce qui est compris dans une plage */
[data-range="start"] ~ :has(~ [data-range="end"]):not([data-range]):not([data-range="end"] ~ :has(~ [data-range="start"]))

N'oubliez pas que, tout comme les autres sélecteurs, vous pouvez utiliser :has() lorsque vous construisez un sélecteur dans JavaScript. La possibilité de sélectionner des frères et sœurs précédents, des ancêtres et les autres fonctionnalités que nous avons apprises rendront également vos sélecteurs JS plus performants !

Sélection linéaire d'une plage basée sur l'état

Rassemblons certaines des qualités des sélecteurs et combinateurs :has() que nous avons appris pour créer un composant de classement par étoiles.

L'"étoile" sous-jacente sera une entrée radio, qui nous donnera accès à un état :checked pour nous aider à développer les sélecteurs.

<div class="star-rating">
  <fieldset>
    <legend>Notez cette démo</legend>
    <div class="stars">
      <label class="star">
        <input type="radio" name="rating" value="1" />
        <span>1</span>
      </label>
      <!-- ...4 étoiles de plus -->
    </div>
  </fieldset>
</div>

Comme le montre l'aperçu vidéo qui suit, lorsqu'un utilisateur survole les étoiles soulignées, la plage allant du début (le plus à gauche) à l'étoile survolée doit se remplir de couleur. Lors de la sélection, lorsque le outon radio des étoiles est coché, la taille de l'étoile et du numéro augmente et la couleur de remplissage est conservée. Si l'utilisateur survole des étoiles après l'étoile cochée, la plage doit remplir les étoiles jusqu'au survol. Si l'utilisateur survole les étoiles avant l'étoile cochée, la plage ne doit remplir que jusqu'à l'étoile survolée, et les étoiles situées entre le survol et l'étoile précédemment cochée doivent avoir leur couleur de remplissage éclaircie.


Cela fait beaucoup de plages à gérer, mais avec :has(), nous pouvons les décomposer en sélecteurs segmentés très rapidement !

La série de sélecteurs suivante s'applique à tous les états où nous voulons qu'une étoile ou une plage d'étoiles ait un background bleu jusqu'à l'étoile :checked. La règle met à jour un ensemble de propriétés qui affecteront la forme de l'étoile, créée par une combinaison des pseudo-éléments ::before et ::after sur le label .star.

Au total, cette règle sélectionne la plage d'étoiles entre la première étoile et l'étoile survolée, ou la première étoile et l'étoile avec un bouton radio coché.

.star:hover,
/* Frères et sœurs précédents de l'étoile survolée */
.star:has(~ .star:hover),
/* L'étoile a une radio cochée */
.star:has(:checked),
/* Frères et soeurs précédents d'une étoile cochée */
.star:has(~ .star :checked) {
  --star-rating-bg: dodgerblue;
}

Ensuite, nous voulons éclaircir la couleur de remplissage des étoiles situées entre l'étoile survolée et une étoile cochée ultérieure, et des étoiles cochées qui suivent l'étoile survolée.

/* Frères et sœurs entre une étoile survolée et une étoile cochée */
.star:hover ~ .star:has(~ .star :checked),
/* Étoile cochée suivant une étoile survolée */
.star:hover ~ .star:has(:checked) {
  --star-rating-bg: lightblue;
}

Concernant les sélecteurs d'état pour notre composant de classement par étoiles, c'est tout ce qu'il y a à faire !

La démo CodePen qui suit présente quelques astuces supplémentaires sur la façon dont le composant est créé à l'aide de la grille CSS, des propriétés personnalisées et du clip-path. Pour l'accessibilité, elle garantit également que la couleur n'est pas le seul indicateur en mettant à l'échelle l'étoile cochée. Et il gère les thèmes à fort contraste (alias "couleurs forcées") en fournissant des valeurs de la palette de couleurs du système pour garantir la visibilité du remplissage de l'étoile :checked. En outre, les transitions sont raccourcies lorsqu'un utilisateur préfère un mouvement réduit.

Groupes de sélection multi-plages avec état

Alors que le composant de notation par étoiles présentait un changement de style dynamique basé sur l'état, la disponibilité d'éléments avec état facilite également l'utilisation de :has() pour créer des limites visuelles.

Nos précédents sélecteurs multi-plages s'appuyaient sur l'ajout manuel de "crochets" dans le balisage pour styliser correctement les gammes sans s'infiltrer dans les zones intermédiaires. Mais si nous avons un ensemble de champs contenant des cases à cocher, nous pouvons à nouveau utiliser l'état :checked pour identifier clairement les limites autour des éléments cochés et non cochés.

Dans cette vidéo d'aperçu, lorsque les cases à cocher sont sélectionnées, elles reçoivent une bordure et un fond vert pour créer la frontière visuelle. Grâce à :has(), cette limite s'agrandit pour paraître envelopper les groupes d'éléments cochés, de sorte que le cadre visuel semble entourer l'ensemble du groupe. Le premier élément (ou un singleton) reçoit des coins supérieurs arrondis, et le dernier élément (ou un singleton) reçoit des coins inférieurs arrondis ainsi qu'une ombre légère.

Nous devons créer des règles pour gérer l'apparence du haut, du milieu et du bas en fonction de l'emplacement de l'élément dans le jeu. Les éléments uniques doivent recevoir les trois styles.

Notre HTML est configuré pour envelopper chaque entrée de case à cocher avec son étiquette, donc tous nos sélecteurs commenceront par correspondre à label:has(:checked) pour voir si l'étiquette contient une entrée cochée.

Pour déterminer le premier ou l'unique élément de l'ensemble, nous devons ajouter la condition qu'il ne suive pas un élément précédent avec une entrée cochée. Cette règle donnera un style à l'apparence supérieure.

/* Premier élément coché dans une plage
 OU haut d'un élément unique coché */
label:has(:checked):not(label:has(:checked) + label)

Pour déterminer le dernier ou l'unique élément de l'ensemble, nous retournons la condition précédente pour vérifier qu'elle n'est pas suivie d'une entrée cochée. Cette règle donnera un style à l'apparence du bas.

/* Dernier élément coché dans une plage
 OU le bas d'un seul élément coché */
label:has(:checked):not(label:has(+ label :checked))

Pour l'apparence du milieu, nous allons créer une règle qui capture réellement le groupe du début à la fin puisque tous les éléments de la règle doivent recevoir une couleur de fond et des bordures latérales.

Nous pourrions simplement utiliser label:has(:checked) pour ce sélecteur étant donné le contexte. Cependant, nous apprenons à sélectionner et à styliser des plages, donc pour compléter notre exercice, nous allons écrire les sélecteurs étendus.

La logique représentée dans le premier sélecteur est la suivante : "sélectionner les étiquettes avec des entrées cochées qui sont suivies par des étiquettes sœurs contenant des entrées cochées", ce qui capture tous les éléments de la plage sauf le dernier. Pour cela, nous répétons le sélecteur que nous venons de créer pour styliser le dernier élément coché de la plage.

/* Plage d'éléments cochés */
label:has(:checked):has(~ label :checked),
label:has(:checked):not(label:has(+ label :checked))

Cette démo CodePen présente également accent-color pour modifier la couleur de l'entrée cochée et utilise des propriétés personnalisées pour gérer le rayon de la bordure. Elle utilise également des propriétés logiques.

Quelques ressources complémentaires pour écrire des sélecteurs :has()

Vous pouvez explorer toutes les démos dans ma collection CodePen.

D'autres personnes on expérimenté ce qu'on peut faire avec :has(), n'hésitez pas à consulter ces ressources pour vous inspirer. Comme pour toutes les nouvelles fonctionnalités, les possibilités sont nombreuses, et nous en bénéficions tous lorsque nous partageons ce ue nous avons découvert ou appris !



NdT : un exemple intéressant d'utilisation de :has() cité dans la formidable newsletter Bytes :

Si nous voulions mettre en place un effet de style ESPN où l'on assombrit tous les éléments de liste qui ne sont pas survolés, comment pourrions-nous le faire uniquement à l'aide de CSS ?

<ul>
  <li>NBA</li>
  <li>NFL</li>
  <li>NCAAF</li>
  <li>MLB</li>
  <li>NCAAM</li>
  <li>NHL</li>
</ul>

Grâce au nouveau sélecteur :has en CSS, nous pouvons utiliser le sélecteur général sibling (~) et un pseudo-sélecteur :hover pour cibler les éléments avant et après en seulement 3 lignes de css.

ul {
  display: flex;
  gap: 8px;
}

li {
  display: block;
  cursor: pointer;
  transition: opacity 0.3s ease-in-out;
}

li:has(~ li:hover),
li:hover ~ li {
  opacity: 0.5;
}

Vous pouvez le voir en action ici.

Autres ressources externes

Articles de Stephanie Eckles traduits dans La Cascade

Voir la page de Stephanie Eckles et la liste de ses articles dans La Cascade.
Article original paru le 23 janvier 2023 dans Smashing Magazine
Traduit avec l'aimable autorisation de Smashing Magazine et de Stephanie Eckles.
Copyright Smashing Magazine © 2023