CSS Container Queries – Responsive Komponenten
CSS Container Queries ermöglichen responsive Komponenten, die auf ihre eigene Größe reagieren statt auf den Viewport. Inkl. @container Syntax und Beispiele.
Seit Jahren kämpfen wir mit dem gleichen Problem: Media Queries reagieren nur auf die Viewport-Größe. Aber was, wenn eine Komponente in der Sidebar anders aussehen soll als im Hauptbereich – obwohl der Viewport gleich bleibt? Mit Container Queries können Komponenten endlich auf ihre eigene Größe reagieren.
Das Problem mit Media Queries
/* Klassische Media Query – reagiert auf Viewport */
@media (min-width: 768px) {
.card {
display: flex;
flex-direction: row;
}
}
Das funktioniert, solange die Karte immer die volle Breite nutzt. Aber:
┌─────────────────────────────────────────────────────────────┐
│ Viewport: 1200px │
├───────────────────────────────────┬─────────────────────────┤
│ │ │
│ Hauptbereich │ Sidebar │
│ 800px │ 300px │
│ │ │
│ ┌─────────────────────────────┐ │ ┌───────────────────┐ │
│ │ .card (horizontal) │ │ │ .card (???) │ │
│ │ ┌──────┐ │ │ │ │ │
│ │ │ Bild │ Titel + Text │ │ │ Sollte vertikal │ │
│ │ └──────┘ │ │ │ sein, aber ist │ │
│ │ │ │ │ horizontal! │ │
│ └─────────────────────────────┘ │ └───────────────────┘ │
│ │ │
└───────────────────────────────────┴─────────────────────────┘
Die Media Query sagt: “Viewport > 768px → horizontal”. Aber die Sidebar-Karte hat nur 300px Platz!
Container Queries: Die Lösung
/* 1. Container definieren */
.card-container {
container-type: inline-size;
container-name: card;
}
/* 2. Auf Container-Größe reagieren */
@container card (min-width: 400px) {
.card {
display: flex;
flex-direction: row;
}
}
@container card (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
}
<div class="card-container">
<article class="card">
<img src="bild.jpg" alt="">
<div class="card-content">
<h2>Titel</h2>
<p>Beschreibung...</p>
</div>
</article>
</div>
Jetzt reagiert die Karte auf ihren Container, nicht den Viewport:
┌─────────────────────────────────────────────────────────────┐
│ Viewport: 1200px │
├───────────────────────────────────┬─────────────────────────┤
│ │ │
│ Hauptbereich │ Sidebar │
│ Container: 800px │ Container: 300px │
│ │ │
│ ┌─────────────────────────────┐ │ ┌───────────────────┐ │
│ │ .card (horizontal ✓) │ │ │ .card (vertikal ✓)│ │
│ │ ┌──────┐ │ │ │ ┌─────────────┐ │ │
│ │ │ Bild │ Titel + Text │ │ │ │ Bild │ │ │
│ │ └──────┘ │ │ │ └─────────────┘ │ │
│ │ │ │ │ Titel + Text │ │
│ └─────────────────────────────┘ │ └───────────────────┘ │
│ │ │
└───────────────────────────────────┴─────────────────────────┘
Container-Typen
/* Nur Inline-Größe (Breite in horizontalen Sprachen) */
.container {
container-type: inline-size;
}
/* Beide Achsen (Breite und Höhe) */
.container {
container-type: size;
}
/* Nur für Style Queries (keine Größe) */
.container {
container-type: normal;
}
Empfehlung: inline-size ist der häufigste Fall. size nur nutzen, wenn du wirklich auf die Höhe reagieren musst – es erfordert explizite Höhenangaben.
Container benennen
/* Kurzform */
.sidebar {
container: sidebar / inline-size;
}
/* Langform */
.sidebar {
container-name: sidebar;
container-type: inline-size;
}
/* Mehrere Namen möglich */
.widget {
container-name: widget sidebar-item;
container-type: inline-size;
}
Container Query Syntax
Größen-Queries
/* Mindestbreite */
@container (min-width: 400px) { }
/* Maximalbreite */
@container (max-width: 599px) { }
/* Bereich */
@container (400px <= width <= 800px) { }
/* Benannter Container */
@container sidebar (min-width: 300px) { }
/* Höhe (nur mit container-type: size) */
@container (min-height: 200px) { }
/* Kombinationen */
@container (min-width: 400px) and (max-width: 800px) { }
@container card (width > 500px) or (height > 400px) { }
Container Query Units
Neue relative Einheiten, die sich auf den Container beziehen:
.card-title {
/* 5% der Container-Breite */
font-size: 5cqi;
/* 10% der Container-Höhe */
padding-block: 10cqb;
}
┌─────────────────────────────────────────────────────────────┐
│ Container Query Units │
├─────────────┬───────────────────────────────────────────────┤
│ cqw │ 1% der Container-Breite │
│ cqh │ 1% der Container-Höhe │
│ cqi │ 1% der Container Inline-Size (meist Breite) │
│ cqb │ 1% der Container Block-Size (meist Höhe) │
│ cqmin │ Kleinerer Wert von cqi/cqb │
│ cqmax │ Größerer Wert von cqi/cqb │
└─────────────┴───────────────────────────────────────────────┘
Praktisches Beispiel:
.card {
container-type: inline-size;
}
.card-title {
/* Skaliert mit Container-Breite, aber mit Grenzen */
font-size: clamp(1rem, 5cqi, 2rem);
}
.card-image {
/* Immer 40% der Container-Breite */
width: 40cqi;
}
Praktische Beispiele
Responsive Card-Komponente
.card-wrapper {
container: card / inline-size;
}
.card {
display: grid;
gap: 1rem;
padding: 1rem;
background: var(--surface);
border-radius: 0.5rem;
}
.card-image {
aspect-ratio: 16/9;
object-fit: cover;
border-radius: 0.25rem;
}
/* Klein: Vertikal gestapelt */
@container card (max-width: 399px) {
.card {
grid-template-columns: 1fr;
}
.card-title {
font-size: 1.25rem;
}
}
/* Mittel: Bild links, Content rechts */
@container card (400px <= width <= 699px) {
.card {
grid-template-columns: 150px 1fr;
align-items: start;
}
.card-image {
aspect-ratio: 1;
}
.card-title {
font-size: 1.5rem;
}
}
/* Groß: Mehr Platz für Bild */
@container card (min-width: 700px) {
.card {
grid-template-columns: 300px 1fr;
}
.card-title {
font-size: 2rem;
}
.card-description {
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
Navigation mit Container Queries
.nav-container {
container: nav / inline-size;
}
.nav {
display: flex;
gap: 0.5rem;
}
.nav-link {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
}
.nav-label {
display: none;
}
/* Kompakt: Nur Icons */
@container nav (max-width: 299px) {
.nav {
justify-content: center;
}
.nav-icon {
font-size: 1.5rem;
}
}
/* Normal: Icons + Labels */
@container nav (min-width: 300px) {
.nav-label {
display: inline;
}
}
/* Breit: Zusätzliche Infos */
@container nav (min-width: 500px) {
.nav-link {
padding: 0.75rem 1rem;
}
.nav-badge {
display: inline-flex;
}
}
Dashboard-Widget
.widget {
container: widget / inline-size;
padding: 1rem;
background: var(--surface);
border-radius: 0.5rem;
}
.widget-chart {
height: 200px;
}
.widget-stats {
display: grid;
gap: 0.5rem;
}
/* Kompakt */
@container widget (max-width: 249px) {
.widget-header {
flex-direction: column;
text-align: center;
}
.widget-stats {
grid-template-columns: 1fr;
}
.widget-chart {
height: 150px;
}
}
/* Normal */
@container widget (250px <= width <= 399px) {
.widget-stats {
grid-template-columns: repeat(2, 1fr);
}
}
/* Breit */
@container widget (min-width: 400px) {
.widget-stats {
grid-template-columns: repeat(4, 1fr);
}
.widget-chart {
height: 300px;
}
}
Style Queries (Experimentell)
Style Queries ermöglichen Abfragen auf CSS Custom Properties:
.card-wrapper {
container-type: normal; /* Keine Größe nötig */
--theme: light;
}
.card-wrapper.dark {
--theme: dark;
}
/* Reagiert auf Custom Property */
@container style(--theme: dark) {
.card {
background: #1a1a1a;
color: #fff;
}
.card-title {
color: #60a5fa;
}
}
@container style(--theme: light) {
.card {
background: #fff;
color: #1a1a1a;
}
}
Hinweis: Style Queries haben noch eingeschränkte Browser-Unterstützung (Stand 2025).
Browser-Unterstützung
┌─────────────────────────────────────────────────────────────┐
│ Container Queries Browser Support (Januar 2025) │
├──────────────────┬──────────────────────────────────────────┤
│ Chrome │ ✅ 105+ (August 2022) │
│ Edge │ ✅ 105+ (August 2022) │
│ Safari │ ✅ 16+ (September 2022) │
│ Firefox │ ✅ 110+ (Februar 2023) │
├──────────────────┼──────────────────────────────────────────┤
│ Global Support │ ~93% (caniuse.com) │
├──────────────────┴──────────────────────────────────────────┤
│ │
│ Style Queries: Nur Chrome 111+ (experimentell) │
│ │
└─────────────────────────────────────────────────────────────┘
Fallback-Strategie
.card {
/* Fallback: Immer vertikal */
display: flex;
flex-direction: column;
}
/* Progressive Enhancement */
@supports (container-type: inline-size) {
.card-wrapper {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
flex-direction: row;
}
}
}
Best Practices
1. Container sinnvoll wählen
/* GUT: Wrapper-Element als Container */
.card-wrapper {
container-type: inline-size;
}
/* SCHLECHT: Das Element selbst als Container */
.card {
container-type: inline-size; /* Kann nicht auf eigene Größe reagieren! */
}
Regel: Ein Element kann nicht auf seine eigene Container-Größe reagieren – nur auf die eines Vorfahren.
2. Performance beachten
/* Vermeide container-type: size wenn möglich */
.widget {
container-type: size; /* Braucht explizite Höhe! */
height: 400px; /* Muss gesetzt werden */
}
/* Besser: inline-size reicht meist */
.widget {
container-type: inline-size;
}
3. Mit Media Queries kombinieren
/* Media Query für Layout-Grundstruktur */
@media (min-width: 768px) {
.layout {
display: grid;
grid-template-columns: 1fr 300px;
}
}
/* Container Query für Komponenten */
@container (min-width: 400px) {
.card {
flex-direction: row;
}
}
4. Benennung für Klarheit
/* Ohne Namen: Nächster Container-Vorfahre */
@container (min-width: 400px) { }
/* Mit Namen: Explizit */
@container sidebar (min-width: 300px) { }
@container main-content (min-width: 600px) { }
Container Queries vs. Media Queries
┌────────────────────────────────────────────────────────────┐
│ Wann was nutzen? │
├────────────────────────────────────────────────────────────┤
│ │
│ Media Queries: │
│ • Seiten-Layout (Grid, Spalten) │
│ • Navigation (Burger-Menü vs. horizontal) │
│ • Schriftgrößen für die ganze Seite │
│ • Print-Styles │
│ • Dark Mode (prefers-color-scheme) │
│ │
│ Container Queries: │
│ • Wiederverwendbare Komponenten (Cards, Widgets) │
│ • Komponenten in unterschiedlichen Kontexten │
│ • Design-System-Komponenten │
│ • Sidebar-Inhalte │
│ • Dashboard-Widgets │
│ │
└────────────────────────────────────────────────────────────┘
Fazit
Container Queries sind ein Game-Changer für komponentenbasiertes CSS:
- Intrinsisch responsive: Komponenten passen sich ihrem Kontext an
- Wiederverwendbar: Gleiche Komponente funktioniert überall
- Wartbar: Keine speziellen Klassen für “sidebar-card” vs “main-card”
- Zukunftssicher: Teil des CSS-Standards mit exzellenter Browser-Unterstützung
Die Kombination aus Media Queries für das Seiten-Layout und Container Queries für Komponenten ist der moderne Ansatz für responsive Webdesign.