Ajouter une classe en JavaScript à chaque fois qu’un état visuel dépend d’un contenu enfant, puis la retirer quand cet état disparaît : c’est la solution la plus courante pour styler différemment une ligne de panier lorsqu’un coupon s’y applique. Le sélecteur CSS :has(), désormais pris en charge par les trois moteurs de rendu majeurs depuis l’arrivée du support dans Firefox fin 2023, permet de se passer de cette étape dans de nombreux cas.
Comparer les deux approches sur un cas concret — une ligne de panier WooCommerce affichant un badge quand une remise s’applique — montre où ce sélecteur apporte un vrai gain, et où il atteint ses limites.
L’approche classique : une classe ajoutée en JavaScript
Sur le panier de blocs WooCommerce, un badge de réduction est généralement injecté conditionnellement, ce qui impose de surveiller le rendu du composant et d’ajouter une classe correspondante sur le conteneur parent :
document.querySelectorAll('.wc-block-cart-item').forEach((ligne) => {
if (ligne.querySelector('.wc-block-components-totals-discount')) {
ligne.classList.add('a-un-coupon');
}
});
Ce script doit s’exécuter à chaque changement de contenu du panier — ajout d’un coupon, changement de quantité — ce qui suppose de le relier à un observateur de mutation ou à un événement déclenché par le panier, avec le risque d’un décalage temporaire entre le rendu et l’ajout de la classe.
Ce que permet :has()
:has() autorise à sélectionner un élément parent en fonction de la présence d’un descendant correspondant à un sélecteur donné, ce qui correspond exactement à ce besoin :
.wc-block-cart-item:has(.wc-block-components-totals-discount) {
background-color: var(--couleur-fond-remise, #f6fbf3);
border-left: 3px solid var(--couleur-accent-remise, #4caf50);
}
Cette seule règle CSS reproduit le comportement du script précédent, sans observateur de mutation ni risque de décalage : le style s’applique dès que le balisage correspondant est présent dans le document, à chaque rendu.

Où :has() apporte un vrai gain
- Suppression complète d’un script dédié à la seule gestion d’une classe d’état visuel.
- Cohérence garantie entre le contenu réellement affiché et le style appliqué, sans risque de désynchronisation.
- Possibilité de composer plusieurs conditions, par exemple cibler une ligne qui contient à la fois un coupon et un produit en rupture de stock.
Où ce sélecteur atteint ses limites
Le support de :has() dans les navigateurs récents ne couvre pas nécessairement l’ensemble du trafic d’une boutique, en particulier sur des parcs de terminaux professionnels dont le navigateur n’est pas toujours mis à jour au même rythme que le grand public. Sur une fonctionnalité purement décorative comme ce badge, une dégradation silencieuse — l’absence du style sans rupture fonctionnelle — reste acceptable. Sur une information réellement nécessaire à la compréhension du panier, un repli en JavaScript doit être conservé, l’un ne remplaçant pas nécessairement l’autre selon la criticité du contenu concerné.
Vérifier la prise en charge avant de retirer le repli JavaScript
@supports selector(:has(a)) {
.wc-block-cart-item .badge-remise-js {
display: none;
}
}
Cette règle, construite avec @supports selector(), permet de masquer le badge généré en JavaScript uniquement sur les navigateurs qui prennent en charge :has(), et de conserver le repli sur les autres, le temps que le support se généralise davantage.
Composer plusieurs sélecteurs :has() imbriqués
Rien n’empêche d’aller plus loin en combinant plusieurs conditions dans une seule règle, par exemple pour distinguer visuellement une ligne à la fois remisée et proposée en quantité limitée :
.wc-block-cart-item:has(.wc-block-components-totals-discount):has(.badge-stock-limite) {
outline: 1px dashed var(--couleur-alerte, #e07b00);
}
Cette écriture reste lisible tant que le nombre de conditions combinées reste raisonnable. Au-delà de deux ou trois sélecteurs imbriqués, la règle devient difficile à relire six mois plus tard, et il vaut mieux revenir à une classe explicite posée par le composant lui-même plutôt que de multiplier les conditions structurelles.
En résumé
Ce sélecteur ne change rien à la logique de calcul des coupons, qui reste entièrement gérée côté panier WooCommerce : il change uniquement la façon dont un état déjà présent dans le balisage se traduit en style visuel. Pour une fonctionnalité d’affichage non critique, c’est une simplification réelle du code ; pour une information indispensable, la prudence reste de mise tant que la couverture des navigateurs n’est pas totale sur le trafic réel du site concerné.