Maîtrisez les CSS Container Queries pour des composants vraiment responsives. Syntaxe, exemples pratiques et comparaison avec les Media Queries.
Introduction — la révolution du responsive par composant
Depuis l'avènement du responsive design, les développeurs utilisent les Media Queries pour adapter les interfaces à la taille de l'écran. Cette approche fonctionne bien pour les mises en page globales — mais elle révèle ses limites dès qu'on travaille avec des composants réutilisables.
Le problème fondamental : un composant ne sait pas dans quel contexte il sera intégré. Une carte produit placée dans une sidebar de 300 px devrait s'afficher différemment de la même carte en pleine largeur. Avec les Media Queries, impossible d'y parvenir sans hacks CSS fragiles.
Les CSS Container Queries résolvent élégamment ce problème. Introduites dans la spécification CSS Containment Level 3, elles permettent à un élément de réagir à la taille de son conteneur parent — et non plus à celle de la fenêtre. Chaque composant devient ainsi autonome, véritablement portable et réutilisable dans n'importe quel contexte.
- Comprendre les limites des Media Queries pour les composants
- Maîtriser la syntaxe
container-type,container-nameet@container - Créer des composants qui s'adaptent à leur contexte d'intégration
- Utiliser les unités
cqw,cqh,cqi,cqb - Combiner Container Queries et Bootstrap 5
- Gérer le support navigateurs avec progressive enhancement
Limites des Media Queries traditionnelles
Pour bien comprendre l'apport des Container Queries, illustrons d'abord le problème classique que vous avez certainement rencontré.
Le problème du composant contextuel
Imaginons une carte d'article qui doit s'afficher :
- En colonne (image au-dessus, texte dessous) quand elle est dans une sidebar étroite
- En ligne (image à gauche, texte à droite) quand elle occupe une largeur confortable
/* ❌ Approche Media Queries — problème classique */
/* La carte passe en mode ligne à partir de 768px de viewport */
@media (min-width: 768px) {
.article-card {
display: flex;
flex-direction: row;
}
.article-card img {
width: 200px;
flex-shrink: 0;
}
}
/* Mais si la carte est dans une sidebar de 320px sur un écran 1200px...
La media query passe en mode "row" à cause des 1200px de viewport,
même si le conteneur disponible est trop étroit pour ce layout !
Résultat : la carte est écrasée et illisible. */
Les contournements habituels — fragiles et non maintenables
/* ❌ Hack classique : classes utilitaires spécifiques au contexte */
/* On doit créer des variantes pour chaque contexte de placement */
.sidebar .article-card {
/* Styles pour la sidebar */
flex-direction: column;
}
.main-content .article-card {
/* Styles pour le contenu principal */
flex-direction: row;
}
.featured-section .article-card {
/* Encore un autre contexte... */
flex-direction: row;
font-size: 1.2rem;
}
/* Résultat :
- Le composant n'est plus indépendant
- Il connaît son contexte parent (couplage fort)
- Difficile à maintenir et à tester
- Impossible à réutiliser sans modifications */
Syntaxe de base des Container Queries
Les Container Queries reposent sur deux éléments : déclarer un conteneur
sur l'élément parent, puis utiliser @container pour définir des styles
conditionnels sur ses enfants.
Etape 1 — Déclarer un conteneur avec container-type
/* Déclarer un élément comme conteneur CSS */
/* container-type: inline-size
→ Observe la largeur inline (horizontale) du conteneur
→ Le cas d'usage le plus courant pour le responsive */
.card-wrapper {
container-type: inline-size;
}
/* container-type: size
→ Observe la largeur ET la hauteur du conteneur
→ Attention : nécessite une hauteur explicite sur le conteneur */
.widget-wrapper {
container-type: size;
}
/* container-type: normal (valeur par défaut)
→ L'élément ne répond pas aux @container queries
→ Utile pour désactiver explicitement le comportement */
.no-container {
container-type: normal;
}
Etape 2 — Nommer le conteneur avec container-name
/* Nommer un conteneur pour le cibler précisément */
/* Sans nom : @container cible le conteneur parent le plus proche */
.card-wrapper {
container-type: inline-size;
/* Pas de nom — le @container le plus proche sera utilisé */
}
/* Avec nom : permet de cibler un conteneur ancêtre spécifique */
.sidebar {
container-type: inline-size;
container-name: sidebar; /* Nom sémantique et lisible */
}
.main-layout {
container-type: inline-size;
container-name: main-layout;
}
/* Raccourci avec la propriété shorthand container */
.card-wrapper {
/* container: / */
container: card-context / inline-size;
}
Etape 3 — Appliquer des styles avec @container
/* Syntaxe de base : @container (condition) { styles } */
/* Cibler le conteneur parent le plus proche */
@container (min-width: 400px) {
/* Styles appliqués quand le CONTENEUR fait >= 400px de large */
.article-card {
display: flex;
flex-direction: row;
gap: 1rem;
}
}
/* Cibler un conteneur nommé spécifiquement */
@container sidebar (min-width: 280px) {
/* Appliqué seulement quand le conteneur "sidebar" fait >= 280px */
.article-card {
padding: 1rem;
}
}
/* Conditions disponibles pour @container */
@container (width >= 400px) { /* Largeur min */ }
@container (width <= 600px) { /* Largeur max */ }
@container (400px <= width <= 800px){ /* Plage de largeur */ }
@container (height >= 300px) { /* Hauteur (si container-type: size) */ }
@container (aspect-ratio >= 1) { /* Ratio largeur/hauteur */ }
@container (orientation: landscape) { /* Orientation */ }
width >= 400px) est équivalente à min-width: 400px
mais plus lisible. Les deux fonctionnent dans tous les navigateurs qui supportent
les Container Queries.
Exemples pratiques — card, sidebar, navigation
Exemple 1 — Card produit responsive par composant
La card produit classique qui passe de l'affichage colonne (étroit) à l'affichage ligne (large), indépendamment du viewport.
/* ========================================
HTML — Structure de la card produit
======================================== */
<!-- Wrapper conteneur : c'est lui qui est observé -->
<div class="product-card-container">
<article class="product-card">
<!-- Image du produit -->
<div class="product-card__image">
<img src="product.webp" alt="Nom du produit" width="300" height="200">
</div>
<!-- Contenu textuel -->
<div class="product-card__body">
<h3 class="product-card__title">Nom du produit</h3>
<p class="product-card__description">Description courte du produit.</p>
<div class="product-card__footer">
<span class="product-card__price">29,99 €</span>
<button class="btn btn-primary btn-sm">Ajouter au panier</button>
</div>
</div>
</article>
</div>
/* ========================================
CSS — Container Query sur la card
======================================== */
/* 1. Déclarer le wrapper comme conteneur */
.product-card-container {
container-type: inline-size;
container-name: product-card;
/* Le conteneur peut avoir n'importe quelle largeur selon son placement */
}
/* 2. Styles par défaut — mode colonne (petit espace) */
.product-card {
display: flex;
flex-direction: column; /* Image au-dessus, texte dessous */
background-color: var(--color-bg-card, #fff);
border: 1px solid var(--color-border, #dee2e6);
border-radius: 10px;
overflow: hidden;
}
.product-card__image img {
width: 100%;
height: 180px;
object-fit: cover; /* Recadrage intelligent de l'image */
display: block;
}
.product-card__body {
padding: 1rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.product-card__footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: auto; /* Colle le footer en bas */
}
/* 3. Quand le CONTENEUR fait plus de 420px → mode ligne */
@container product-card (min-width: 420px) {
.product-card {
flex-direction: row; /* Image à gauche, texte à droite */
}
.product-card__image {
width: 200px; /* Largeur fixe pour l'image */
flex-shrink: 0; /* Empêche l'image de rétrécir */
}
.product-card__image img {
width: 200px;
height: 100%; /* L'image prend toute la hauteur de la card */
}
.product-card__body {
padding: 1.25rem;
}
}
/* 4. Mode large — quand le conteneur dépasse 600px */
@container product-card (min-width: 600px) {
.product-card__title {
font-size: 1.4rem; /* Titre plus grand sur grand espace */
}
.product-card__description {
display: block; /* Afficher la description (masquée en petit) */
font-size: 1rem;
}
.product-card__image {
width: 280px; /* Image plus large */
}
}
Exemple 2 — Sidebar qui passe en layout horizontal
/* ========================================
Sidebar adaptative avec Container Query
======================================== */
/* Déclarer la sidebar comme conteneur */
.sidebar-container {
container-type: inline-size;
container-name: sidebar;
}
/* Mode vertical par défaut (sidebar étroite) */
.sidebar-nav {
display: flex;
flex-direction: column; /* Menu vertical */
gap: 0.25rem;
padding: 1rem 0;
}
.sidebar-nav__item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.6rem 1rem;
border-radius: 8px;
text-decoration: none;
color: var(--color-text, #212529);
transition: background-color 0.2s;
}
.sidebar-nav__item:hover {
background-color: var(--color-bg-tertiary, #e9ecef);
}
/* Icône toujours visible */
.sidebar-nav__icon {
width: 20px;
height: 20px;
flex-shrink: 0;
}
/* Label du menu : visible seulement si espace suffisant */
.sidebar-nav__label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Quand la sidebar est trop étroite : masquer les labels, centrer les icônes */
@container sidebar (max-width: 200px) {
.sidebar-nav__item {
justify-content: center; /* Centrer l'icône horizontalement */
padding: 0.75rem;
}
.sidebar-nav__label {
display: none; /* Masquer le texte, garder uniquement l'icône */
}
/* Tooltip au survol pour compenser l'absence de label */
.sidebar-nav__item[title]:hover::after {
content: attr(title);
position: absolute;
left: 100%;
top: 50%;
transform: translateY(-50%);
background: #333;
color: #fff;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.8rem;
white-space: nowrap;
margin-left: 0.5rem;
}
}
/* Quand la sidebar est assez large : affichage en grille */
@container sidebar (min-width: 480px) {
.sidebar-nav {
flex-direction: row; /* Menu horizontal */
flex-wrap: wrap;
}
.sidebar-nav__item {
flex: 1 1 auto; /* Occupe l'espace disponible équitablement */
}
}
Exemple 3 — Navigation qui change de layout selon le conteneur
/* ========================================
Widget de navigation contextuelle
======================================== */
<!-- Même composant, utilisable dans header ET dans footer -->
<div class="nav-container">
<nav class="contextual-nav" aria-label="Navigation principale">
<a href="/" class="nav-item">
<svg class="nav-icon" aria-hidden="true"><!-- icône accueil --></svg>
<span class="nav-label">Accueil</span>
</a>
<a href="/articles" class="nav-item">
<svg class="nav-icon" aria-hidden="true"><!-- icône articles --></svg>
<span class="nav-label">Articles</span>
</a>
<a href="/outils" class="nav-item">
<svg class="nav-icon" aria-hidden="true"><!-- icône outils --></svg>
<span class="nav-label">Outils</span>
</a>
</nav>
</div>
/* Conteneur de la nav */
.nav-container {
container-type: inline-size;
container-name: nav-context;
}
/* Styles de base — affichage compact (icône + label empilés) */
.contextual-nav {
display: flex;
gap: 0.5rem;
}
.nav-item {
display: flex;
flex-direction: column; /* Icône au-dessus du label */
align-items: center;
gap: 0.25rem;
padding: 0.5rem;
text-decoration: none;
color: var(--color-text, #212529);
font-size: 0.75rem;
border-radius: 6px;
transition: background-color 0.2s;
}
/* Navigation en ligne (icône + label côte à côte) si espace > 400px */
@container nav-context (min-width: 400px) {
.nav-item {
flex-direction: row; /* Icône à gauche du label */
gap: 0.5rem;
font-size: 0.9rem;
padding: 0.5rem 0.75rem;
}
}
/* Navigation avec fond et style étendu si espace > 700px */
@container nav-context (min-width: 700px) {
.contextual-nav {
gap: 0.25rem;
}
.nav-item {
padding: 0.6rem 1.25rem;
font-size: 1rem;
font-weight: 500;
}
.nav-item:hover {
background-color: rgba(13, 110, 253, 0.08); /* Bleu translucide au survol */
}
}
.contextual-nav peut
maintenant être utilisé dans un header large, un footer compact, ou une sidebar étroite.
Il s'adapte automatiquement sans aucune connaissance de son contexte d'intégration.
Container Queries vs Media Queries
Container Queries et Media Queries ne s'opposent pas — elles se complètent. Voici un tableau comparatif pour savoir laquelle utiliser selon le contexte.
| Critère | Media Queries | Container Queries |
|---|---|---|
| Référence observée | Viewport (fenêtre du navigateur) | Conteneur parent de l'élément |
| Cas d'usage idéal | Mise en page globale, layout de page | Composants réutilisables, widgets |
| Portabilité des composants | Faible — dépend du viewport | Forte — indépendant du contexte |
| Syntaxe | @media (min-width: 768px) |
@container (min-width: 400px) |
| Déclaration préalable | Aucune — fonctionne immédiatement | Requiert container-type sur le parent |
| Support navigateurs | Universel (IE 9+) | Chrome 105+, Firefox 110+, Safari 16+ (2023) |
| Performance | Très bonne (calcul au niveau global) | Bonne (calcul par conteneur déclaré) |
| Applicabilité aux fonctionnalités OS | Oui (prefers-color-scheme, etc.) |
Non (uniquement dimensionnel/style) |
Quand utiliser quoi ?
/* ✅ MEDIA QUERIES — pour la mise en page globale */
/* Changer le nombre de colonnes selon la taille de l'écran */
.page-grid {
display: grid;
grid-template-columns: 1fr; /* 1 colonne sur mobile */
}
@media (min-width: 768px) {
.page-grid {
grid-template-columns: 300px 1fr; /* sidebar + contenu sur tablette */
}
}
@media (min-width: 1200px) {
.page-grid {
grid-template-columns: 280px 1fr 240px; /* 3 colonnes sur desktop */
}
}
/* ✅ CONTAINER QUERIES — pour les composants dans ces colonnes */
/* La card s'adapte à la colonne qui la contient */
.card-wrapper {
container-type: inline-size;
}
@container (min-width: 350px) {
.article-card {
flex-direction: row; /* Horizontal quand la colonne est assez large */
}
}
/* Les deux techniques travaillent ensemble harmonieusement :
Media Queries définissent la structure globale,
Container Queries gèrent le comportement interne des composants. */
Cas d'usage avancés — Style Queries et unités cq*
Les unités de conteneur (cqw, cqh, cqi, cqb)
Analogues aux unités viewport (vw, vh), les unités
de conteneur permettent de dimensionner les éléments en pourcentage
de leur conteneur — pas du viewport.
/* Unités de conteneur disponibles */
/* cqw — Container Query Width : 1% de la largeur du conteneur */
/* cqh — Container Query Height : 1% de la hauteur du conteneur */
/* cqi — Container Query Inline : 1% de la dimension inline (largeur en mode LTR) */
/* cqb — Container Query Block : 1% de la dimension block (hauteur en mode LTR) */
/* cqmin — Minimum des deux dimensions (inline et block) */
/* cqmax — Maximum des deux dimensions */
.card-container {
container-type: inline-size;
container-name: card;
}
.card-title {
/* La taille de police s'adapte à la largeur du conteneur */
/* cqi = container query inline size (= largeur en LTR) */
font-size: clamp(1rem, 4cqi, 2rem);
/* clamp(min, préféré, max) :
- Minimum : 1rem (ne descend jamais en dessous)
- Préféré : 4% de la largeur du conteneur
- Maximum : 2rem (ne monte jamais au-dessus) */
}
.card-image {
/* L'image prend 30% de la largeur du conteneur */
width: 30cqi;
min-width: 120px; /* Mais jamais moins de 120px */
max-width: 300px; /* Et jamais plus de 300px */
}
/* Application pratique : typographie fluide par composant */
.hero-card {
container-type: inline-size;
}
.hero-card__title {
/* Titre qui grandit proportionnellement au conteneur */
font-size: clamp(1.5rem, 6cqw, 4rem);
line-height: 1.2;
}
.hero-card__subtitle {
font-size: clamp(0.9rem, 2.5cqw, 1.5rem);
}
Style Queries — réagir aux valeurs de propriétés CSS
Les Style Queries sont une extension des Container Queries permettant d'appliquer des styles selon la valeur d'une propriété CSS custom (variable) du conteneur.
/* Style Queries — syntaxe @container style() */
/* Déclarer des variables de contexte sur le conteneur */
.product-card-container {
container-type: inline-size;
container-name: product;
/* Variable de contexte : statut du produit */
--product-status: available;
}
.product-card-container.out-of-stock {
--product-status: out-of-stock;
}
.product-card-container.on-sale {
--product-status: on-sale;
}
/* Modifier le composant selon la valeur de la variable */
@container product style(--product-status: out-of-stock) {
/* Styles pour les produits épuisés */
.product-card {
opacity: 0.6; /* Aspect grisé */
filter: grayscale(1); /* Désaturation */
}
.product-card__price::after {
content: ' — Épuisé';
color: #dc3545; /* Rouge Bootstrap */
font-weight: bold;
}
}
@container product style(--product-status: on-sale) {
/* Styles pour les promotions */
.product-card {
border-color: #ffc107; /* Bordure jaune */
box-shadow: 0 0 0 3px rgba(255, 193, 7, 0.3); /* Lueur jaune */
}
.product-card__price {
color: #dc3545; /* Prix en rouge */
font-weight: 800; /* Gras accentué */
}
}
@container style())
sont supportées dans Chrome 111+ et Safari 18+. Firefox les supporte depuis la
version 129. Pour une production 2024, elles nécessitent encore un fallback
ou une utilisation progressive.
Intégration avec Bootstrap 5
Bootstrap 5 utilise des Media Queries pour sa grille. Les Container Queries s'ajoutent au-dessus pour affiner le comportement interne des composants. Les deux coexistent parfaitement.
Exemple — Card Bootstrap dans une grille adaptative
<!-- Grille Bootstrap 5 : définit le nombre de colonnes via Media Queries -->
<div class="row g-3">
<!-- Colonne principale : 8/12 sur md, 12/12 sur sm -->
<div class="col-12 col-md-8">
<!-- Wrapper conteneur CSS pour les cards internes -->
<div class="content-card-container">
<article class="content-card">
<img src="article.webp" alt="Titre de l'article" class="content-card__img">
<div class="content-card__body">
<h3 class="content-card__title">Titre de l'article</h3>
<p class="content-card__excerpt">Extrait de l'article...</p>
<a href="#" class="btn btn-primary btn-sm mt-2">Lire la suite</a>
</div>
</article>
</div>
</div>
<!-- Sidebar : 4/12 sur md, cachée sur sm -->
<div class="col-12 col-md-4 d-none d-md-block">
<!-- Même wrapper conteneur mais largeur différente -->
<div class="content-card-container">
<article class="content-card">
<img src="article.webp" alt="Titre de l'article" class="content-card__img">
<div class="content-card__body">
<h3 class="content-card__title">Titre de l'article</h3>
<p class="content-card__excerpt">Extrait de l'article...</p>
<a href="#" class="btn btn-primary btn-sm mt-2">Lire la suite</a>
</div>
</article>
</div>
</div>
</div>
/* CSS — Container Query sur le composant card */
/* Le wrapper est déclaré conteneur */
.content-card-container {
container-type: inline-size;
container-name: content-card;
height: 100%; /* Pour que les cards aient la même hauteur dans la grille */
}
/* Styles par défaut — mode colonne (adapté à une colonne étroite) */
.content-card {
display: flex;
flex-direction: column;
height: 100%;
border: 1px solid var(--bs-border-color, #dee2e6); /* Variable Bootstrap 5 */
border-radius: var(--bs-border-radius, 0.375rem);
overflow: hidden;
background-color: var(--bs-body-bg, #fff);
}
.content-card__img {
width: 100%;
height: 180px;
object-fit: cover;
display: block;
}
.content-card__body {
padding: 1rem;
flex: 1; /* Prend l'espace restant */
display: flex;
flex-direction: column;
}
.content-card__excerpt {
flex: 1;
color: var(--bs-secondary-color, #6c757d);
margin-bottom: 0;
}
/* Quand le conteneur est large (> 450px) :
La colonne principale passe en mode ligne,
la sidebar reste en mode colonne (trop étroite) — automatiquement ! */
@container content-card (min-width: 450px) {
.content-card {
flex-direction: row; /* Mode ligne pour la colonne principale */
}
.content-card__img {
width: 220px; /* Largeur fixe pour l'image */
height: auto; /* Hauteur automatique */
flex-shrink: 0;
}
.content-card__title {
font-size: 1.25rem;
}
}
/* Bootstrap 5 gère le layout global (nombre de colonnes)
Container Query gère le comportement interne de chaque composant
→ Séparation propre des responsabilités */
Composant réutilisable dans sidebar ET en pleine largeur
/* Démonstration : même composant, deux contextes différents,
comportement adaptatif automatique */
/* Contexte 1 : Grille Bootstrap — col-12 col-md-4 (sidebar ~33%) */
/* Contexte 2 : Grille Bootstrap — col-12 (pleine largeur) */
/* Le composant n'a besoin d'aucune classe contextuelle :
il s'adapte seul grâce à container-type sur son wrapper */
/* Résultats observés :
- Dans col-md-4 (~380px sur écran 1200px) : mode colonne (image + texte empilés)
- Dans col-12 (> 1100px) : mode ligne avec image à gauche et texte à droite
- Sur mobile (< 768px, Bootstrap col-12) : mode colonne adaptatif
→ Comportement parfaitement cohérent sans CSS supplémentaire */
Bonnes pratiques et organisation CSS
1. Ne déclarer que les conteneurs nécessaires
/* ❌ Mauvaise pratique : déclarer container-type sur tout */
* {
container-type: inline-size; /* Crée un contexte de containment partout — coûteux */
}
/* ❌ Trop généreux : sur des éléments qui n'en ont pas besoin */
body, main, section, div, article, aside {
container-type: inline-size;
}
/* ✅ Bonne pratique : uniquement sur les wrappers des composants adaptatifs */
.card-wrapper,
.widget-container,
.sidebar-nav-wrapper,
.product-grid-item {
container-type: inline-size;
}
/* ✅ Explication : container-type crée un contexte d'isolation CSS
qui peut affecter les z-index et le stacking context.
Ne l'appliquer qu'aux éléments qui en ont réellement besoin. */
2. Nommer les conteneurs de façon sémantique
/* ❌ Noms génériques ou absents — confusion sur les grandes bases de code */
.wrapper-1 { container-type: inline-size; container-name: c1; }
.wrapper-2 { container-type: inline-size; container-name: c2; }
.wrapper-3 { container-type: inline-size; container-name: c3; }
/* ✅ Noms sémantiques alignés avec le rôle du composant */
.product-card-wrapper {
container-type: inline-size;
container-name: product-card; /* Décrit le composant contenu */
}
.sidebar-widget-wrapper {
container-type: inline-size;
container-name: sidebar-widget; /* Décrit le contexte */
}
.hero-banner-wrapper {
container-type: size; /* size pour observer hauteur ET largeur */
container-name: hero-banner;
}
/* Convention recommandée : kebab-case, nom descriptif du composant ou contexte */
3. Organiser les breakpoints de façon cohérente
/* Définir des tokens de breakpoints conteneur dans une section dédiée */
/* ========================================
Tokens — Breakpoints par composant
======================================== */
/* Card compacte : change à partir de 320px */
/* Card standard : change à partir de 400px et 600px */
/* Card héro : change à partir de 480px et 800px */
/* Garder les @container groupés avec leur composant */
/* ========================================
Composant : .article-card
======================================== */
.article-card-wrapper {
container-type: inline-size;
container-name: article-card;
}
/* Base : mode colonne */
.article-card { /* ... styles de base ... */ }
/* Premier breakpoint : 400px */
@container article-card (min-width: 400px) {
.article-card { /* ... ajustements ... */ }
}
/* Deuxième breakpoint : 700px */
@container article-card (min-width: 700px) {
.article-card { /* ... styles large ... */ }
}
4. Combiner clamp() et unités cq* pour la typographie fluide
/* Technique avancée : typographie qui s'adapte au conteneur */
.fluid-card {
container-type: inline-size;
container-name: fluid-card;
}
.fluid-card__title {
/* font-size fluide : min 1rem, préféré 5% de la largeur conteneur, max 2rem */
font-size: clamp(1rem, 5cqi, 2rem);
/* cqi = container query inline size
Sur un conteneur de 200px : 5cqi = 10px → clamp force 1rem (16px)
Sur un conteneur de 400px : 5cqi = 20px → valeur fluide
Sur un conteneur de 600px : 5cqi = 30px → clamp force 2rem (32px) */
}
.fluid-card__excerpt {
font-size: clamp(0.875rem, 3cqi, 1.125rem);
line-height: 1.5;
}
/* Padding fluide selon le conteneur */
.fluid-card__body {
padding: clamp(0.75rem, 4cqi, 2rem);
}
container-typedéclaré uniquement sur les wrappers nécessaires- Noms de conteneurs sémantiques et cohérents
- Fallback via
@supportspour les navigateurs non supportés - Styles par défaut (sans
@container) fonctionnels en mobile-first - Testé dans Chrome DevTools avec redimensionnement du conteneur
- Aucun
container-typesur les éléments ayant des z-index critiques - Unités
cq*utilisées avecclamp()pour éviter les valeurs extrêmes - Style Queries utilisées avec prudence (support partiel en 2025)
Conclusion et ressources
Les CSS Container Queries représentent une évolution fondamentale du responsive design. En permettant aux composants de réagir à leur propre conteneur plutôt qu'au viewport global, elles ouvrent la voie à une architecture CSS véritablement modulaire, où chaque composant est autonome et portable dans n'importe quel contexte d'intégration.
Avec un support navigateur dépassant 93% en 2025, les Container Queries sont prêtes
pour la production. La stratégie recommandée est simple : commencez par les adopter
sur les nouveaux composants en mobile-first, ajoutez un @supports pour
la robustesse, et combinez-les avec Bootstrap 5 pour la mise en page globale.
Points clés à retenir
container-type: inline-sizesur le wrapper — active l'observation de la largeurcontainer-name: mon-composant— permet de cibler précisément un conteneur ancêtre@container (min-width: 400px)— réagit à la largeur du conteneur, pas du viewport- Unités
cqw,cqi— dimensionnement relatif au conteneur - Style Queries
@container style()— adapter le style selon des variables CSS custom - Combiner avec
clamp()pour une typographie fluide par composant - Toujours écrire les styles par défaut en mobile-first — les
@containerenrichissent ensuite
Ressources recommandées
- MDN — CSS Container Queries — Documentation officielle complète avec exemples interactifs et tableau de compatibilité.
- web.dev — Container Queries stable — Article Google sur la stabilisation des Container Queries dans les navigateurs.
- Chrome Developers — Container Queries guide — Guide pratique avec démos et conseils de performance.
- Can I Use — CSS Container Queries — Tableau de support navigateurs mis à jour en temps réel.