Container Queries: Komponenten, die auf ihren Container reagieren
Baue responsive Komponenten mit CSS Container Queries, die sich am Container statt am Viewport orientieren — ein Layout für Sidebars und Modals.

Media Queries beantworten die falsche Frage für komponentenbasiertes Design. Wenn du @media (min-width: 768px) schreibst, fragst du: „Wie breit ist der Viewport?" Aber was deine Komponente tatsächlich wissen muss, ist: „Wie viel Platz habe ich?" Eine Card-Komponente in einer schmalen Seitenleiste und dieselbe Card in einem breiten Inhaltsbereich teilen sich dieselbe Viewport-Breite, brauchen aber völlig unterschiedliche Layouts.
Container Queries lösen dieses Problem, indem Komponenten auf die Abmessungen ihres Containers statt auf den Viewport reagieren. Das macht Komponenten wirklich portabel: Wirf sie in einen beliebigen Layout-Kontext, und sie passen sich automatisch an. Es ist die Responsive-Design-Primitive, die wir uns gewünscht haben, seit wir angefangen haben, Komponentenbibliotheken zu bauen.
Grundlagen der Container Queries
Um Container Queries zu nutzen, definierst du einen Containment-Kontext auf dem Elternelement und schreibst dann Queries gegen diesen Container aus dem Kindelement heraus.
/* ❌ Media queries: component responds to viewport */
.card {
display: grid;
grid-template-columns: 1fr;
}
@media (min-width: 600px) {
.card {
grid-template-columns: 200px 1fr;
}
}
/* This card always switches at 600px viewport width
even if it's in a 300px sidebar */
/* ✅ Container queries: component responds to its container */
.card-container {
container-type: inline-size;
container-name: card;
}
.card {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
padding: 1rem;
}
@container card (min-width: 400px) {
.card {
grid-template-columns: 200px 1fr;
}
}
@container card (min-width: 700px) {
.card {
grid-template-columns: 250px 1fr;
gap: 2rem;
padding: 2rem;
}
}<!-- Same component, different contexts, different layouts -->
<!-- In sidebar: stays vertical (container < 400px) -->
<aside class="sidebar">
<div class="card-container">
<article class="card">
<img src="thumbnail.jpg" alt="Article thumbnail" />
<div class="card-content">
<h3>Article Title</h3>
<p>Description text here...</p>
</div>
</article>
</div>
</aside>
<!-- In main content: goes horizontal (container > 400px) -->
<main class="content">
<div class="card-container">
<article class="card">
<img src="thumbnail.jpg" alt="Article thumbnail" />
<div class="card-content">
<h3>Article Title</h3>
<p>Description text here...</p>
</div>
</article>
</div>
</main>Container-Query-Einheiten
Container Queries führen neue Einheiten ein, die relativ zu den Abmessungen des Containers sind und Viewport-Einheiten für die komponenteninterne Größenbestimmung ersetzen.
.card-container {
container-type: inline-size;
container-name: card;
}
/* Container query units */
.card-title {
/* cqi = 1% of container's inline size */
font-size: clamp(1rem, 3cqi, 2rem);
}
.card-image {
/* cqw = 1% of container width */
/* cqh = 1% of container height */
/* cqi = 1% of container inline size */
/* cqb = 1% of container block size */
/* cqmin = smaller of cqi and cqb */
/* cqmax = larger of cqi and cqb */
height: 30cqi;
object-fit: cover;
}
/* Fluid spacing based on container */
.card {
padding: clamp(0.75rem, 3cqi, 2rem);
gap: clamp(0.5rem, 2cqi, 1.5rem);
}Komponentenmuster aus der Praxis
Hier sind praktische Muster, die zeigen, wo Container Queries Probleme lösen, die Media Queries nicht lösen können.
/* Navigation that adapts to its container */
.nav-container {
container-type: inline-size;
container-name: nav;
}
.nav-list {
display: flex;
flex-direction: column;
gap: 0.25rem;
list-style: none;
padding: 0;
}
.nav-label {
display: none;
}
.nav-icon {
width: 24px;
height: 24px;
}
/* When nav has enough space, show labels */
@container nav (min-width: 200px) {
.nav-list {
gap: 0.5rem;
}
.nav-label {
display: inline;
}
}
/* When nav has lots of space, go horizontal */
@container nav (min-width: 600px) {
.nav-list {
flex-direction: row;
gap: 1rem;
}
}/* Data table that adapts its columns to available space */
.table-container {
container-type: inline-size;
container-name: data-table;
}
.data-table {
width: 100%;
}
/* Hide lower-priority columns when space is tight */
.col-priority-low {
display: none;
}
.col-priority-medium {
display: none;
}
@container data-table (min-width: 500px) {
.col-priority-medium {
display: table-cell;
}
}
@container data-table (min-width: 800px) {
.col-priority-low {
display: table-cell;
}
}
/* Switch to card layout on very narrow containers */
@container data-table (max-width: 350px) {
.data-table,
.data-table thead,
.data-table tbody,
.data-table tr,
.data-table td {
display: block;
}
.data-table thead {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
}
.data-table td::before {
content: attr(data-label);
font-weight: 600;
display: block;
margin-bottom: 0.25rem;
}
.data-table tr {
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
}
}Container Queries mit Style Queries kombinieren
Style Queries (CSS @container style()) ermöglichen es, den berechneten Stil eines Containers abzufragen, sodass Komponenten ohne JavaScript auf Zustände reagieren können.
/* Style queries: respond to custom property values */
.card-container {
container-type: inline-size;
container-name: card;
}
/* Dark variant via custom property */
.card-container[data-theme="dark"] {
--card-theme: dark;
}
@container card style(--card-theme: dark) {
.card {
background: #1e293b;
color: #f1f5f9;
}
.card-title {
color: #e2e8f0;
}
}
/* Featured variant */
.card-container[data-featured] {
--card-featured: true;
}
@container card style(--card-featured: true) {
.card {
border-left: 4px solid var(--accent-color);
background: var(--featured-bg);
}
.card-image {
aspect-ratio: 16 / 9;
}
}Verschachtelte Container
Container können verschachtelt werden, und jede Container Query verweist auf den nächsten Vorfahren mit dem passenden Container-Namen.
/* Page layout: outer container */
.page-container {
container-type: inline-size;
container-name: page;
}
/* Card grid: inner container */
.card-grid-container {
container-type: inline-size;
container-name: grid;
}
/* Individual card: innermost container */
.card-container {
container-type: inline-size;
container-name: card;
}
/* Grid responds to its container */
.card-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@container grid (min-width: 500px) {
.card-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@container grid (min-width: 900px) {
.card-grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* Card responds to its own container */
/* (which gets narrower as more columns appear) */
@container card (min-width: 300px) {
.card {
grid-template-columns: 120px 1fr;
}
}Migrationsstrategie: von Media Queries zu Container Queries
Du musst nicht alles auf einmal umschreiben. Migriere Komponente für Komponente und beginne mit denen, die in mehreren Layout-Kontexten vorkommen.
/* Step 1: Identify components that need container awareness */
/* Good candidates: cards, navigation, data tables, form layouts */
/* Bad candidates: full-page layouts (viewport is the container) */
/* Step 2: Add containment without changing behavior */
.card-wrapper {
container-type: inline-size;
/* containment has no visual effect — safe addition */
}
/* Step 3: Convert media queries to container queries one at a time */
/* Before */
@media (min-width: 768px) {
.card { grid-template-columns: 200px 1fr; }
}
/* After — test in multiple contexts */
@container (min-width: 400px) {
.card { grid-template-columns: 200px 1fr; }
}
/* Step 4: Keep media queries for truly viewport-dependent styles */
/* Page-level layouts, sticky headers, print styles */
@media (min-width: 1024px) {
.page-layout {
grid-template-columns: 280px 1fr;
}
}Die wichtigsten Erkenntnisse
Container Queries lassen Komponenten auf die Abmessungen ihres Containers statt auf den Viewport reagieren: Eine Card, die bei 400px verfügbarem Platz horizontal wird, funktioniert korrekt, egal ob sie in einer 300px breiten Seitenleiste steckt (bleibt vertikal) oder in einem 800px breiten Inhaltsbereich (wird horizontal) — ganz ohne Codeänderungen. Verwende container-type: inline-size auf Elternelementen und schreibe @container (min-width: ...)-Regeln auf den Kindelementen, kombiniert mit Container-Query-Einheiten wie cqi für fluide Typografie und Abstände, die relativ zum tatsächlichen Platz der Komponente statt zum Bildschirm skalieren. Beginne die Migration mit den Komponenten, die in den unterschiedlichsten Layout-Kontexten vorkommen — Cards, Navigation, Datentabellen — denn diese profitieren am meisten vom Container-Bewusstsein, während Layouts auf Seitenebene, die wirklich von den Viewport-Abmessungen abhängen, bei Media Queries bleiben sollten. Verschachtele Container beim Bau komplexer Layouts, damit jede Ebene unabhängig reagiert: Ein Grid, das seine Spaltenanzahl an den verfügbaren Platz anpasst, enthält Cards, die ihr internes Layout an den Platz jeder einzelnen Card anpassen — so entstehen Kompositionen, die sich auf jeder Ebene anpassen, ohne Breakpoints koordinieren zu müssen.


