Cours CSS / container queries pour des composants réutilisables
Les requêtes de taille de conteneur permettent à un composant de répondre à la taille d'un conteneur ancêtre éligible plutôt qu’à la seule fenêtre d’affichage (viewport). Déclarez un conteneur de requête avec container-type, puis utilisez @container pour modifier les styles des descendants à la taille appropriée.
Comparer la fenêtre d’affichage et le conteneur
Même si la largeur du navigateur est de 1200 px, le placement correct variera en fonction de si la carte se trouve dans la zone de texte principale de 700 px ou dans la zone de la barre latérale de 300 px. Il est difficile d'exprimer cette différence en utilisant uniquement les media queries et les points de rupture adaptés au contenu, où les deux cartes partagent les mêmes conditions de fenêtre d'affichage. Lors de la réutilisation de parties, vous pouvez réduire les règles d'exception spécifiques à la mise en page en modifiant leur configuration interne en fonction de la largeur parente.
| division | Critères de jugement | Rôle approprié |
|---|---|---|
| @media | Environnement tel que la largeur de la fenêtre d'affichage | Nombre de colonnes sur la page, emplacement de la barre latérale |
| @container | taille du conteneur ancêtre sélectionné, etc. | Placement des images et du texte à l'intérieur de la carte, espacement entre les parties |
Les requêtes de conteneur comparent également la largeur de l'expression conditionnelle plutôt que « disposer horizontalement si l'écran est horizontal ». Lorsque la barre latérale devient plus grande que 30rem, les cartes qu'elle contient peuvent également être disposées horizontalement. Si vous voulez toujours une barre latérale étroite, vous devez également spécifier une largeur maximale dans la mise en page parente.
Déclarer un conteneur de requête de taille
.slot { container: panel / inline-size; }
@container panel (min-width: 30rem) {
.card { display: grid; grid-template-columns: 8rem minmax(0, 1fr); gap: 1rem; }
}
container: panel / inline-size déclare le nom panel et active le confinement de la dimension inline. En écriture horizontale, la dimension en ligne est horizontale ; l'écriture verticale change cette correspondance. Fournissez la largeur du conteneur via sa mise en page en bloc externe ou la grille afin que la carte ait une taille significative à interroger. Voir Les bases de CSS Grid : pistes, lignes et placement.
Le parent pour évaluer la condition est .slot et le descendant pour recevoir le style conditionnel est .card. Ne pas écrire dans une structure qui change la carte elle-même en consultant la taille de la carte elle-même. Pour éviter le problème des conditions qui se renversent encore et encore à mesure que la taille change, les requêtes de taille fonctionnent par rapport aux ancêtres appropriés.
Le confinement dimensionnel inline-size rend la taille en ligne du conteneur indépendante des contributions de contenu de ses descendants. Un conteneur à rétrécissement automatique peut donc devenir de manière inattendue très petit. Ici, un bloc ou une piste de grille fournit sa largeur. L'utilisation du confinement dimensionnel size affecte également l'autre axe et peut réduire une hauteur ajustée au contenu, il faut donc choisir le confinement dimensionnel délibérément. Pour plus de détails, voir la documentation MDN sur container-type.
Réutiliser une carte dans différentes largeurs de parent
Enregistrez-le sous container-card.html et ouvrez-le dans votre navigateur. La boîte colorée est un élément d'exemple qui représente la zone de l'image, donc il n'y a pas de fichier image séparé. Sur les écrans plus grands, séparez le texte principal et une barre latérale jusqu'à 20rem, et à 44rem ou moins, placez les deux zones dans une seule colonne.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>container-card.html</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font: 1rem/1.6 sans-serif; color: #243447; }
h1, h2, p { margin-top: 0; } a { color: #145f8f; }
a:focus-visible, button:focus-visible, input:focus-visible { outline: 3px solid #ac4100; outline-offset: 3px; }
.layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 20rem); gap: 1rem; width: min(100%, 75rem); margin-inline: auto; padding: 1rem; }
.slot { min-width: 0; container: panel / inline-size; }
.card { min-width: 0; padding: 1rem; background: #e8f3fa; overflow-wrap: anywhere; }
.media { min-height: 7rem; background: #287699; }
.card h2 { font-size: 1.25rem; } .card p { margin-bottom: 0; }
@container panel (min-width: 30rem) {
.card { display: grid; grid-template-columns: 8rem minmax(0, 1fr); gap: 1rem; align-items: start; }
}
@media (max-width: 44rem) { .layout { grid-template-columns: minmax(0, 1fr); } }
</style>
</head>
<body>
<div class="layout">
<main class="slot"><article class="card"><div class="media" aria-hidden="true"></div><div><h2>Card in the main content area</h2><p>The image and text appear side by side when the parent is at least 30rem wide.</p></div></article></main>
<aside class="slot"><article class="card"><div class="media" aria-hidden="true"></div><div><h2>The same card in a sidebar</h2><p>The sidebar in this example stays at or below 20rem even on a wide screen, so the card keeps its vertical layout.</p></div></article></aside>
</div>
</body>
</html>
Dans cet exemple sans modification de la racine, avec une taille de police de 16px, 30rem correspond à 480px. Si le parent du corps sur un grand écran fait 480px ou plus, l'intérieur de la carte sera en deux colonnes, et dans une barre latérale allant jusqu'à 320px, l'intérieur de la carte sera disposé verticalement. Sur les écrans étroits, lorsque la barre latérale descend, son parent peut devenir plus large. Par conséquent, il est normal que les deux cartes soient placées horizontalement sur un écran de 600px tant que la largeur du parent satisfait les conditions.
La condition extérieure de 44rem et la condition intérieure de 30rem résolvent des problèmes différents. La première condition détermine comment placer la zone sur l'ensemble de la page, et la deuxième condition détermine comment placer les images et le texte dans l'espace attribué à chaque carte. Même si les noms des parents sont les mêmes, chaque carte sélectionne d’abord son ancêtre éligible le plus proche nommé panel, puis évalue la condition sur sa taille. Un résultat faux ne la fait pas chercher un autre ancêtre plus large.
Choisir les conteneurs les plus proches et nommés
Une requête de taille sans nom sélectionne le conteneur ancêtre éligible le plus proche, puis teste sa taille. Elle ne saute pas ce conteneur pour en chercher un plus large lorsque la condition est fausse. Ajouter un conteneur plus étroit entre la carte et son ancien ancêtre peut donc modifier le résultat. Enregistrez cet exemple sous container-nested.html pour comparer les deux sélections.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>container-nested.html</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font: 1rem/1.6 sans-serif; color: #243447; }
h1, h2, p { margin-top: 0; } a { color: #145f8f; }
a:focus-visible, button:focus-visible, input:focus-visible { outline: 3px solid #ac4100; outline-offset: 3px; }
main { width: min(100%, 42rem); padding: 1rem; }
.outer { width: 100%; container: outer / inline-size; border: 2px solid #789; padding: 1rem; }
.inner { width: min(100%, 17rem); container-type: inline-size; }
.card { padding: 1rem; background: #e8f3fa; overflow-wrap: anywhere; }
.media { min-height: 5rem; background: #287699; }
@container (min-width: 30rem) {
.card { display: grid; grid-template-columns: 5rem minmax(0, 1fr); gap: 1rem; }
}
</style>
</head>
<body>
<main><h1>Which parent's width is queried?</h1><div class="outer"><div class="inner"><article class="card"><div class="media" aria-hidden="true"></div><p>The nearest inner container has a maximum of 17rem.</p></article></div></div></main>
</body>
</html>
Sur les écrans larges, le conteneur extérieur est plus grand que 30rem, mais le conteneur intérieur ne dépasse pas 17rem. La condition non nommée sélectionne l'élément le plus proche à l'intérieur, donc les cartes sont disposées verticalement. Gardez l'autre code et remplacez @container (min-width: 30rem) par ce qui suit :
@container outer (min-width: 30rem) {
.card { display: grid; grid-template-columns: 5rem minmax(0, 1fr); gap: 1rem; }
}
Maintenant, nous sélectionnons l'ancêtre extérieur nommé outer, donc la carte est disposée horizontalement sur l'écran large. La carte elle-même reste dans la limite maximale de 17rem, donc vous devrez vérifier séparément si la largeur du corps est suffisante. Le nom n'est pas une propriété qui corrige automatiquement les normes incorrectes, mais une fonction qui spécifie quel ancêtre utiliser comme référence. Après comparaison, revenez à la condition non nommée.
Diagnostiquer les requêtes qui ne s'appliquent pas
- Vérifiez si la cible du sélecteur conditionnel est un descendant du conteneur. Vérifiez que vous n'essayez pas d'appliquer le même style au parent lui-même.
- Vérifiez container-type sur le parent et la largeur réelle du contenu dans les outils de développement. Distinguez la largeur extérieure avec padding et bordure de la largeur du contenu effectivement interrogée.
- Vérifiez si un nouveau conteneur a été créé, et si nécessaire, spécifiez les critères avec container-name.
- Si vous ne changez que container-type en normal, vous pouvez voir la différence dans le fait que la requête de taille n'est pas appliquée. Après confirmation, restaurez en inline-size.
Conservez une carte verticale lisible comme référence, puis ajoutez un emplacement horizontal lorsque la requête de conteneur s'applique. La référence sert également aux navigateurs qui ne prennent pas en charge la requête de taille. Vérifiez la prise en charge et le comportement dans les navigateurs dont vos lecteurs ont réellement besoin. Voir MDN : Requêtes de taille et de style de conteneur.




