Les requêtes de conteneur CSS – Pourquoi j'utilise de moins en moins les requêtes de média
Je développe des sites web depuis 1995. J’ai créé des mises en page avec des tableaux HTML, survécu aux astuces de flottants et accueilli l’arrivée des requêtes média comme une véritable aubaine. Mais depuis que j’ai intégré les requêtes de conteneur CSS à mon processus de travail, je me suis rendu compte que les requêtes média résolvent souvent le mauvais problème.
Dans cet article, j'explique ce que sont les requêtes de conteneur, pourquoi elles constituent un véritable changement de paradigme, et dans quelles circonstances j'utilise l'une ou l'autre.
Le problème fondamental des requêtes de média
Les requêtes de média s'adaptent à la fenêtre d'affichage, c'est-à-dire à la largeur de la fenêtre du navigateur. Pendant des années, cela a suffi, tant que les mises en page restaient simples. Mais les sites web modernes sont constitués de composants réutilisables : cartes, sections « hero », encadrés de produits et barres de navigation.
Et c'est là que réside le problème. Imaginez une page produit. Dans la partie principale de la page, elle mesure 400 pixels de large. Dans une barre latérale, peut-être seulement 200 pixels. La fenêtre d'affichage a la même taille dans les deux cas, mais votre mise en page est complètement différente.
Avec les requêtes de média, vous devrez écrire quelque chose comme ceci :
/* Points d'arrêt globaux qui ne tiennent pas compte du conteneur */
@media (max-width: 768px) {
.product-card {
flex-direction: column;
}
}Cela fonctionne… jusqu’à ce que la carte apparaisse dans une grille qui est elle-même adaptative. À ce moment-là, les points de rupture ne correspondent plus. On finit par écrire des exceptions pour pallier les exceptions. Le code CSS devient confus, difficile à maintenir et étroitement lié à la mise en page.
Qu'est-ce qu'une requête de conteneur ?
Les requêtes de conteneur renversent complètement la logique : au lieu de s'adapter à la fenêtre d'affichage, un composant s'adapte à son propre conteneur.
Commencez par définir quel élément servira de conteneur :
.card-wrapper{
container-type: inline-size;
container-name: card;
}Vous créez ensuite des styles qui s'adaptent à la largeur de ce conteneur, et non à celle de la fenêtre d'affichage :
@container card (min-width: 400px) {
.product-card {
display: flex;
flex-direction: row;
}
}
@container card (max-width: 399px) {
.product-card {
flex-direction: column;
}
}La carte sait désormais de combien d'espace elle dispose, quel que soit l'endroit où elle se trouve sur la page.
Les trois principaux avantages pratiques
1. Réutilisabilité réelle des composants
Pour moi, c'est là le principal avantage. Un composant que j'ai créé une fois fonctionne partout, que ce soit dans une barre latérale, dans une grille ou en tant qu'élément autonome. Je n'ai plus besoin d'attribuer des classes dépendantes du contexte ni d'abuser de la spécificité CSS.
Autrefois :
.sidebar .product-card {/* Styles d'exception */}
.main-content .product-card { /* autres styles d'exception */ }Aujourd'hui : la carte s'en charge toute seule.
2. Less JavaScript for layout logic
ResizeObserver
Before the advent of container queries, many developers used ResizeObserver or defined classes via JavaScript to respond to changes in an element’s size. This approach is prone to errors, hinders performance, and really has no place in JavaScript.
// Je n'en ai plus besoin
const observer = new ResizeObserver(entries =>); Les requêtes de conteneur gèrent cela de manière native dans le navigateur : c'est plus rapide, plus propre et plus facile à maintenir.<br>
3. Les styles restent associés au composant
Quiconque travaille en équipe ou reprend des projets après plusieurs mois connaît bien ce problème : les styles CSS sont dispersés dans de nombreux fichiers, les règles globales se contredisent les unes les autres, et plus personne n'ose supprimer quoi que ce soit.
Avec les requêtes de conteneur, vous écrivez les styles directement au sein du composant. Tout ce dont la carte a besoin se trouve dans la carte elle-même. Cela facilite la révision du code, accélère la prise en main et rend la refactorisation moins risquée.
Quand faut-il utiliser les requêtes de média, et quand faut-il utiliser les requêtes de conteneur ?
Les deux ont leur place. Ma règle d'or :
J’utilise des requêtes de média pour la mise en page principale, c’est-à-dire pour la structure de base de la page. À quel moment la navigation passe-t-elle à un menu hamburger ? À quel moment une mise en page à deux colonnes devient-elle une mise en page à une seule colonne ? Ce sont des choix qui dépendent de la fenêtre d’affichage.
J’utilise des requêtes de conteneur pour les composants – tout ce qui est réutilisé ou peut apparaître à différents endroits dans la mise en page. Des cartes, des teasers, des widgets, des éléments de formulaire.
Un petit guide pour vous aider à faire votre choix :
| Situation | Recommendation |
|---|---|
| Navigation, structure de la page | Media Query |
| Composant réutilisable | Container Query |
| Composant dans différents contextes | Container Query |
| Styles d'impression | Media Query |
| Mode sombre | Media Query (prefers-color-scheme) |
Bonus : requêtes de style – passez au niveau supérieur
Les requêtes de conteneur peuvent non seulement s'adapter à la taille, mais elles pourront bientôt également s'adapter aux propriétés CSS personnalisées – ce qu'on appelle Requêtes de style:
@container style(--theme: dark) Cela signifie qu'un composant peut réagir à l'« état » de son conteneur sans avoir besoin de JavaScript ni de classes supplémentaires. Cette fonctionnalité est encore au stade expérimental, mais la tendance est claire : le CSS gagne en puissance et en autonomie.
Bonus 2 : Le sélecteur « has-me » – les requêtes de conteneur sans pièges
Les requêtes de conteneur présentent un inconvénient bien connu : le parent direct d’un composant doit être déclaré avec `container-type`. Si tu oublies de le faire, les requêtes de conteneur ne fonctionneront tout simplement pas.
/* You always have to set this manually – on EVERY parent element */
.sidebar .main-content .some-other-wrapperEn pratique, ça veut dire que chaque fois que tu déplaces un composant vers un nouvel emplacement dans la mise en page, tu dois vérifier si le nouvel élément parent est déclaré comme conteneur. C’est source d’erreurs et vraiment pénible.
La solution : le composant déclare lui-même son parent
Kevin Geary a mis au point une solution élégante, qu’il appelle le « Has Me Selector ». L’idée est que, plutôt que de déclarer le parent depuis l’extérieur, le composant lui-même indique à son parent :Vous devez être un conteneur"
Ça marche grâce à une combinaison de :has(), d’imbrication CSS et du sélecteur & :
.card {
:has(> &) {
container-type: inline-size;
}
}Mais qu’est-ce qui se passe exactement ici ? Dans l’imbrication CSS, le signe & représente le sélecteur actuel – dans ce cas, .card. Le signe & sert normalement à appliquer des styles à l’élément lui-même ou à ses pseudo-éléments :
.card {
&::hover { /* = .card:hover */}
&::before { /* = .card::before */ }
}
Mais tu peux aussi faire l’inverse. Si tu écris .hero-section &, cela signifie .hero-section .card – tu écris donc du CSS global depuis l’intérieur du composant. C’est ça, l’astuce.
:has(> &) signifie : « Sélectionne tous les éléments dont .card est un enfant direct » – en d’autres termes : l’élément parent de .card, quel que soit son nom.
Le résultat est le même que :
:has(.card) {
container-type: inline-size;
}La principale différence, c’est que cette règle est définie automatiquement par le composant. Pas besoin de la mettre à jour manuellement, pas de risque de l’oublier. La carte s’en charge toute seule.
Exemple complet
Voici à quoi ressemble un composant robuste et entièrement autonome utilisant une requête de conteneur :
.card{
/* « Has Me » est présent : le parent devient automatiquement un conteneur */
:has(> &) {
container-type: inline-size;
}
/* Les requêtes de conteneur fonctionnent désormais partout */
@container( min-width:400px) {
display: flex;
flex-direction: row;
gap: 1.5rem;
}
@container( max-width:399px) {
flex-direction: column;
}
}Vous pouvez placer cette carte dans n'importe quelle barre latérale, grille ou pied de page : elle s'affiche toujours correctement.
Conclusion
Les requêtes de conteneur, le sélecteur « :has(> &) » et les requêtes de style vont tous dans le même sens : le CSS devient plus autonome, plus expressif et prend en charge des tâches pour lesquelles on avait auparavant besoin de JavaScript.
Mon conseil pour les nouveaux projets :
Les requêtes de médias pour la structure de base et les choix liés à la fenêtre d’affichage
Requêtes de conteneur pour tous les composants réutilisables
Ajoute :has(> &) dans chaque composant pour que celui-ci devienne le conteneur de son parent
La prise en charge de ces trois fonctionnalités par les navigateurs est aujourd’hui excellente. Il n’y a plus aucune bonne raison de ne pas les utiliser : ton futur toi t’en remerciera.
Marcel Heiniger ist Webentwickler aus Ipsach bei Biel und entwickelt seit 1995 Websites.
Er hilft KMUs mit massgeschneiderten WordPress- und WooCommerce-Lösungen.

