Startseite / Artikel

Container-Abfragen gegenüber Medienabfragen: Entscheidungen für die Seite und Entscheidungen für Komponenten

Erfahren Sie, wann ein Media Query und wann ein Container Query besser geeignet sind, anhand von Beispielen mit Karten, Dashboards und Einheiten, die es wiederverwendbaren Komponenten ermöglichen, sich anzupassen.

Verlag: REACTAPP.TOP16 Min. Lesezeit3602 Wörter

Quellen: Container Queries vs Media Queries: How Modern CSS Changes Responsive Design

Die meisten responsiven CSS-Lösungen basieren weiterhin auf einer einzigen Eingabe: der Breite des Browserfensters. Das funktioniert gut für die gesamte Seite, bricht jedoch bei wiederverwendbaren Komponenten zusammen – schließlich sehen eine in einer Seitenleiste dargestellte Karte sowie dieselbe Karte in einer breiten Hauptspalte genau denselben Ansichtsbereich, haben aber sehr unterschiedliche verfügbare Räume. Diese Anleitung zeigt, wie Container-Queries diese Lücke schließen, wo Media-Queries weiterhin das bessere Werkzeug sind, und wie man beides zusammen mit flüssigen Einheiten zu einem verständlichen responsiven System kombinieren kann.

Eine klassische, vom Ansichtsbereich abhängige Regel sieht so aus: Sobald das Fenster unter einen Bruchpunkt fällt, stapelt die Karte ihren Inhalt vertikal.

@media (max-width: 768px) {
  .card {
    flex-direction: column;
  }
}

Es ist nichts kaputt. Die Schwäche liegt konzeptionell darin, dass die Regel lernt, wie breit das Fenster ist – niemals jedoch, wie breit die Karte ist. In einer kleinen Demo passen diese Werte zueinander; in einer Anwendung voller Gitter, Seitenleisten und Panels treten Abweichungen auf, und genau in diesem Unterschied entstehen Layoutfehler. Container-Queries ermöglichen es einem Komponenten, statt zu fragen „Wie breit ist der Bildschirm?“ zu fragen: „Wie viel Platz habe ich tatsächlich?“

Warum ein Viewport-Breakpoint eine wiederverwendbare Karte in die Irre führt

Nehmen wir eine einfache Produktkarte: Ein Bild neben einem Textblock mit Titel, kurzer Beschreibung und Aufforderung zur Handlung. Die Markup-Struktur ist hier in zwei Abschnitte aufgeteilt, handelt sich aber um ein einziges Element.

<article class="product-card">
  <img src="product.jpg" alt="Product">
  <div class="product-info">
    <h2>Wireless Headphones</h2>
    <p>Noise cancellation with 30-hour battery life.</p>
    <button>Buy now</button>
  </div>
</article>

Die erste Styling-Phase platziert in der Regel Bild und Text mit Flexbox nebeneinander, fügt anschließend einen Viewport-Breakpoint hinzu, um sie auf kleinen Bildschirmen übereinander anzuordnen.

.product-card {
  display: flex;
  gap: 1rem;
}
@media (max-width: 768px) {
  .product-card {
    flex-direction: column;
  }
}

Das scheint in Ordnung zu sein, bis die Karte wiederverwendet wird. Auf einer breiten Desktop-Seite gibt es genügend horizontalen Platz, und die nebeneinander angeordnete Darstellung sieht gut aus.

Desktop page
┌───────────────────────────────────────────┐
│                                           │
│   Product Card                            │
│   ┌──────────┐  ┌─────────────────────┐   │
│   │  Image   │  │ Product information │   │
│   └──────────┘  └─────────────────────┘   │
│                                           │
└───────────────────────────────────────────┘

Setzen Sie genau dieselbe Karte in einen schmalen Seitenbereich – dort benötigt sie eine gestapelte Anordnung, obwohl das Fenster immer noch die Größe eines Desktops hat.

┌───────────────┐
│               │
│   Sidebar     │
│               │
│ ┌───────────┐ │
│ │   Image   │ │
│ ├───────────┤ │
│ │   Info    │ │
│ └───────────┘ │
│               │
└───────────────┘

Platzieren Sie einige davon in einer Zeile des Dashboards – jede Karte erhält je nach Spalte einen unterschiedlichen Anteil an Breite.

┌───────────────────────────────────────────┐
│ ┌─────────────┐ ┌──────────────────────┐ │
│ │   Card      │ │       Card           │ │
│ └─────────────┘ └──────────────────────┘ │
└───────────────────────────────────────────┘

Drei Anordnungen, drei verschiedene Breiten – der Ansichtsbereich bleibt unverändert. Eine an das Fenster gebundene Regel kann sie nicht voneinander unterscheiden, weshalb die Karte im Seitenbereich weiterhin eng bleibt.

Wozu sind Media Queries tatsächlich gut

Eine Media-Abfrage bewertet Eigenschaften der Umgebung, in der die Seite angezeigt wird: Abmessungen des Ansichtsfensters, Ausrichtung, Farbschema, Typ des Zeigers, Einstellungen zur Reduzierung von Bewegungen und so weiter. Eine Breitenabfrage ist der bekannte Fall.

@media (max-width: 768px) {
  ...
}

Der Informationsfluss ist einfach: Der Browser misst das Ansichtsfenster, die Abfrage passt oder nicht, und die Darstellung reagiert darauf.

Viewport
    ↓
Media Query
    ↓
Layout changes

Deshalb sind Media-Abfragen das richtige Instrument für Entscheidungen, die tatsächlich der Seite als Ganzes zugehören. Ein typisches Beispiel ist ein Zwei-Spalten-Layout mit einer fest breiten Seitenleiste:

.page-layout {
  display: grid;
  grid-template-columns: 1fr 300px;
}

Unter einer bestimmten Fensterbreite kollabiert das Layout auf eine einzige Spalte:

@media (max-width: 900px) {
  .page-layout {
    grid-template-columns: 1fr;
  }
}

Das ist eine sinnvolle Verwendung einer Media-Abfrage: Ob die Seite Platz für eine Seitenleiste hat, ist tatsächlich eine Frage des Ansichtsfensters. Probleme entstehen, wenn derselbe Mechanismus darüber entscheidet, wie ein einzelnes Komponente seine internen Elemente anordnet.

Wie Container-Abfragen funktionieren

Eine Container-Abfrage ermöglicht es einem Element, auf die Größe eines Vorfahren zu reagieren, den Sie ausdrücklich als Abfrage-Container markiert haben. Die Einrichtung erfolgt in zwei Schritten. Zuerst deklarieren Sie den Container:

.card-container {
  container-type: inline-size;
}

Der Wert inline-size teilt dem Browser mit, dass Nachkommen die inline-Dimension des Containers abfragen dürfen – das ist seine Breite in horizontalen Schreibmodi. Zweitens schreiben Sie eine Regel für diesen Container:

@container (max-width: 500px) {
  .product-card {
    flex-direction: column;
  }
}

Die Informationen fließen nun vom Container und nicht vom Fenster aus:

Container
    ↓
Container Query
    ↓
Component layout

Die Karte kümmert sich nicht mehr darum, ob der Besucher ein Telefon oder einen großen Monitor verwendet; sie reagiert auf den Platz, den ihr Elternelement ihr zur Verfügung stellt.

Ein Detail, das viele Menschen verwirrt

Eine Container-Abfrage gestaltet die Nachkommen des Containers, niemals den Container selbst. Deshalb werden in den Beispielen immer ein Wrapper (.card-container, .card-wrapper) mit einem inneren Element (.product-card) kombiniert. Wenn Sie container-type auf .product-card setzen und anschließend eine Abfrage ausführen, um die eigene flex-direction von .product-card zu ändern, passiert nichts. Behalten Sie den Wrapper und das Komponentenelement als getrennte Elemente bei.

Ein weiterer Punkt: Die inline-size-Einschränkung bedeutet, dass die Breite des Containers nicht mehr aus seinem Inhalt abgeleitet werden kann. Ein Container, der ein Block-Kind eines normalen Flusslayouts ist, funktioniert einwandfrei, da seine Breite von seinem Elternteil stammt. Ein Container, der auf eine Anpassung an die verfügbare Fläche angewiesen ist – wie beispielsweise ein inline-block-Element oder ein absolut positioniertes Element ohne explizite Breite – kann bei Anwendung der Einschränkung auf eine Breite von null schrumpfen.

Breite des Ansichtsfensters versus Breite der Komponente

Der Unterschied wird deutlich in einem Dashboard mit einer breiten Hauptspalte und einer schmaleren Seitenspalte:

.dashboard {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 1rem;
}

Nehmen wir ein Fenster mit 1440px Breite an. Eine auf das Ansichtsfenster bezogene Regel für die Karte würde wie folgt aussehen:

@media (max-width: 768px) {
  .card {
    flex-direction: column;
  }
}

In diesem Fenster trifft die Regel niemals zu. Dennoch kann eine Karte in der schmaleren Spalte nur etwa 350px breit sein. Die Media-Abfrage berücksichtigt dabei nur eine Zahl:

Viewport = 1440px

Die Karte befindet sich zusammen mit einer anderen:

Component width = 350px

Nur die zweite Zahl beschreibt den tatsächlichen Zustand der Karte, und nur eine Container-Abfrage kann darauf reagieren.

Eine minimale Einrichtung einer Container-Abfrage

Die Implementierung ist kurz. Markieren Sie den Wrapper als Container:

.card-wrapper {
  container-type: inline-size;
}

Geben Sie der Karte ein zweispaltiges Raster, wenn ihr Container mindestens 600px breit ist:

@container (min-width: 600px) {
  .product-card {
    display: grid;
    grid-template-columns: 200px 1fr;
  }
}

Falls nötig, wechseln Sie dazu auf ein einfaches Blocklayout:

@container (max-width: 599px) {
  .product-card {
    display: block;
  }
}

Die Markup-Struktur benötigt nur das Wrapper-Element um die Karte herum:

<div class="card-wrapper">
  <article class="product-card">
    ...
  </article>
</div>

Die Anordnung der Karte leitet sich nun von ihrem Container ab, sodass das Verschieben des Umschließers in eine andere Spalte automatisch die richtige Anordnung ergibt. Beachten Sie, dass die beiden Regeln min-width: 600px und max-width: 599px verwenden; bei bruchzahligen Breiten kann sich ein Container zwischen diesen Werten befinden. Die neuere Bereichssyntax @container (width >= 600px) und @container (width < 600px) vermeidet diese Lücke und sollte in neuem Code bevorzugt werden.

Warum dies zur komponentenbasierten Architektur passt

Container-Abfragen sind wichtiger als eine typische neue CSS-Funktion, denn moderne Frontends bestehen aus wiederverwendbaren Komponenten – sei es Framework-Komponenten in React, Vue oder Angular, frameworkfreie Web Components oder die Bausteine eines gemeinsamen Design-Systems. Keine dieser Komponenten sollte wissen müssen, in welchem Seitenbereich sie schließlich angezeigt werden. Betrachten wir ein user-card-Element: Es könnte im Hauptinhaltsbereich dargestellt werden:

<main>
  <user-card></user-card>
</main>

Es könnte auch in einem Nebenbereich angezeigt werden:

<aside>
  <user-card></user-card>
</aside>

Oder es könnte innerhalb eines Dashboards in Form einer Gitterstruktur wiederholt werden:

<div class="dashboard-grid">
  <user-card></user-card>
  <user-card></user-card>
  <user-card></user-card>
</div>

Das Härtecodieren eines Viewport-Breakpoints in dieser Komponente setzt eine Annahme voraus, die sie nicht überprüfen kann:

@media (max-width: 768px) {
  ...
}

Die Zahl 768 sagt nichts über den tatsächlichen Kontext der Komponente aus. Eine Container-Abfrage bietet einen besseren Ansatz: Bei diesem Platzangebot wird dieses Layout verwendet. Wenn Ihre Komponenten framework-unabhängige Bausteine sind, ist die Diskussion zu dem Platz, an dem die Framework-Grenze in einer wiederverwendbaren UI-Struktur liegen sollte ein nützlicher Begleiter zu dieser Idee.

Eine Profilkarte an ihre Umgebung anpassen

Hier ist eine kompakte Profilkarte, die ein Avatar neben Namen und Rolle platziert:

.profile-card {
  display: flex;
  gap: 1rem;
  align-items: center;
}

Ihr Wrapper wird zu einem Abfrage-Container:

.profile-container {
  container-type: inline-size;
}

Wenn dieser Container 400 Pixel breit oder weniger ist, stapelt die Karte ihre Inhalte und zentriert sie:

@container (max-width: 400px) {
  .profile-card {
    flex-direction: column;
    text-align: center;
  }
}

In einem geräumigen Container steht der Avatar neben dem Text:

┌──────────────────────────────────────┐
│ [Avatar]  John Smith                 │
│           Senior Developer           │
└──────────────────────────────────────┘

In Notfällen ordnet sich derselbe Komponente vertikal neu an:

┌──────────────────┐
│                  │
│     [Avatar]     │
│                  │
│    John Smith    │
│ Senior Developer │
│                  │
└──────────────────┘

Kein Resize-Observer, keine zweite „kompakte“ Variante, keine von der übergeordneten Seite gesteuerte Klasse.

Media Queries haben immer noch ihre Aufgabe

Container-Queries ersetzen Media Queries nicht; sie beantworten eine andere Frage. Eine schnelle Möglichkeit, häufige Entscheidungen zu ordnen:

  • Zusammenfalten der Hauptnavigation: Media Query.
  • Entfernen der Seitenübersicht: Media Query.
  • Wechseln des Layouts einer Karte zwischen horizontal und vertikal: Container Query.
  • Anpassen einer wiederverwendbaren Komponente an ihren Platz: Container Query.
  • Anpassen der Schriftart im gesamten Site: Media Query.
  • Lassen eines Dashboard-Widgets auf die Breite seiner Spalte reagieren: Container Query.
  • Wechseln des gesamten Anwendungsrahmens auf ein anderes Layout: Media Query.
  • Änderung des Verhaltens einer Komponente innerhalb eines begrenzten Elternteils: Container-Abfragen.
  • Ein mentales Modell: globale Entscheidungen und Komponentenentscheidungen

    Die klarste Art, die Arbeit aufzuteilen, besteht darin, zu unterscheiden, was von der Seite entschieden wird und was jede einzelne Komponente entscheidet.

    Media-Abfragen beziehen sich auf den Ansichtsbereich

    Die Frage hinter einer Media-Abfrage lautet:

    "What is the viewport doing?"
    

    Sie eignen sich für:

    • Seitenstruktur
    • Navigation
    • Globale Abstände
    • Globale Typografie
    • Größere Layout-Änderungen

    Container-Abfragen beziehen sich auf die Umgebung der Komponente

    Die Frage hinter einer Container-Abfrage lautet:

    "What is my component's environment doing?"
    

    Sie eignen sich für:

    • Karten
    • Widgets
    • Panels
    • wiederverwendbare UI-Komponenten
    • Dashboard-Module
    • Komponenten aus Design-Systemen

    Größenbestimmung mit Einheiten von Container-Abfragen

    Container-Abfragen sind nicht nur bedingte Blöcke. CSS definiert außerdem Längeneinheiten in Bezug auf den nächstgelegenen Abfrage-Container, sodass die Werte kontinuierlich skaliert werden können, anstatt bei einem Bruchpunkt zu springen:

    .title {
      font-size: 5cqi;
    }
    

    Hier bedeutet cqi ein Prozent der inlinen Größe des Containers, wodurch 5cqi fünf Prozent seiner Breite entspricht. Die vollständige Liste der Einheiten:

    cqw  → container width
    cqh  → container height
    cqi  → container inline size
    cqb  → container block size
    cqmin → smaller container dimension
    cqmax → larger container dimension
    

    Ein gängiges Muster besteht darin, eine Container-Einheit in clamp() zu packen, damit der Wert mit dem Container skaliert, aber niemals einen sinnvollen Bereich verlässt:

    .card-title {
      font-size: clamp(1rem, 4cqi, 2rem);
    }
    

    Der Titel skaliert nun mit dem Platz des Komponenten-Containers statt mit dem Fenster, was Viewport-Einheiten wie vw nicht darstellen können. Es gibt zwei Einschränkungen: Wenn ein Element keinen übergeordneten Query-Container hat, weichen diese Einheiten auf kleinere Viewport-Einheiten aus, sodass zwar ein Wert erzeugt wird, aber nicht der gewünschte. Zudem ist es aus Gründen der Barrierefreiheit wichtig, innerhalb von clamp() eine Obergrenze und Untergrenze für rem festzulegen – eine Schriftgröße, die ausschließlich durch die Breite des Containers bestimmt wird, reagiert nicht auf die Einstellungen zur Textgröße des Benutzers.

    Namen für Container in größeren Anwendungen

    Für eine kleine Seite reicht ein anonymer Container aus:

    .container {
      container-type: inline-size;
    }
    

    Sobald Container verschachtelt sind, passt eine einfache @container-Regel auf den nächstgelegenen übergeordneten Container, was nicht unbedingt der gewünschte sein kann. Die Abkürzung container ermöglicht es Ihnen, einem Container einen Namen zusammen mit seinem Typ zu geben:

    .dashboard {
      container: dashboard / inline-size;
    }
    

    Dann können Regeln gezielt auf diesen Container verweisen:

    @container dashboard (min-width: 700px) {
      .widget {
        grid-template-columns: 1fr 1fr;
      }
    }
    

    Vergleichen Sie eine namenlose Bedingung:

    @container (min-width: 700px)
    

    mit einer benannten:

    @container dashboard (min-width: 700px)
    

    Die benannte Version macht ihre Absicht klar und funktioniert weiterhin, selbst wenn später ein weiterer Container zwischen dem Dashboard und dem Widget hinzugefügt wird.

    Wählen Sie die Containergrenzen bewusst aus

    Containerabfragen sind mächtig, aber das ist kein Grund, diese Deklaration zu jedem Element hinzuzufügen:

    container-type: inline-size;
    

    Jeder Container wendet Layout-, Stil- und inline-size-Einstellungen an, was die Größenanpassung des Elements verändert. Deshalb sollten Container sinnvolle Grenzen für Komponenten markieren anstatt überall verteilt zu sein. Betrachten Sie ein Dashboard mit darin enthaltenen Widgets:

    <section class="dashboard">
      <article class="widget">
        <div class="widget-content">
          ...
        </div>
      </article>
    </section>
    

    Eine sinnvolle Wahl ist es, das Dashboard zum benannten Container zu machen:

    .dashboard {
      container: dashboard / inline-size;
    }
    

    und jedes Widget soll darauf reagieren. Die Regel lautet nicht „Verwenden Sie überall Container-Abfragen“, sondern „Verwenden Sie sie dort, wo sich das Verhalten vom verfügbaren Platz abhängt“.

    Ersetzung von Breakpoint-Stacks durch content-basierte Breakpoints

    Viele Stylesheets enthalten eine Art Leiter aus Viewport-Breakpoints wie diese:

    @media (max-width: 1200px) {
      ...
    }
    
    @media (max-width: 1024px) {
      ...
    }@media (max-width: 900px) {
      ...
    }@media (max-width: 768px) {
      ...
    }@media (max-width: 640px) {
      ...
    }@media (max-width: 480px) {
      ...
    }
    

    Manchmal ist diese Anordnung gerechtfertigt. Oft deutet sie jedoch darauf hin, dass Komponenten eher nach der Breite des Geräts als nach ihrem eigenen Inhalt gestaltet wurden. Container-Abfragen führen zu einer anderen Fragestellung: Anstatt „Welchen Brechpunkt verwendet das Team normalerweise?“ zu fragen, sollte man stattdessen „Bei welcher Breite sieht diese Komponente nicht mehr richtig aus?“ fragen. Die Antwort liefert einen breichpunkt, der auf dem Inhalt basiert:

    @container (max-width: 420px) {
      .card {
        flex-direction: column;
      }
    }
    

    420px entspricht keiner bestimmten Telefonmarke – es ist einfach die Breite, bei der dem Karten-Element der Platz ausgeht. Auf diese Weise gewählte Brechpunkte bleiben auch bei Layout-Änderungen und neuen Geräten gültig.

    Entwicklung von Komponenten, die nicht von der Platzierung abhängen

    Container-Abfragen ermutigen Teams zudem dazu, wirklich unabhängige Komponenten zu entwickeln, deren Verhalten überall dort vorhersehbar ist, wo sie dargestellt werden. Stellen Sie sich eine Schaltgruppe aus einem Design-System vor – sie könnte innerhalb eines Formulars auf einer Seite angeordnet sein:

    Page
     └── Form
          └── Button Group
    

    innerhalb einer Karte auf einem Dashboard:

    Dashboard
     └── Card
          └── Button Group
    

    oder innerhalb einer Karte in einem Seitenbereich:

    Sidebar
     └── Card
          └── Button Group
    

    Der Ansichtsbereich ist in allen drei Fällen identisch; der Platz hingegen nicht. Mit einer Container-Abfrage passt sich die Gruppe an, ohne etwas über die darüber liegende Struktur zu wissen.

    Ein wiederverwendbares responsives Kartenmuster

    Hier ist ein vollständiges Muster, das Sie direkt übernehmen können. Die Markup-Struktur umschließt die Karte in einem Container-Element; auch hier wird das HTML in zwei aufeinanderfolgenden Teilen gezeigt.

    <div class="card-container">
      <article class="card">
        <img src="image.jpg" alt="">
    
        <div class="card-content">
          <h2>Performance Dashboard</h2>
          <p>
            Monitor application performance and infrastructure metrics.
          </p>
          <a href="#">View dashboard</a>
        </div>
      </article>
    </div>
    

    Der Wrapper wird als Container deklariert:

    .card-container {
      container-type: inline-size;
    }
    

    Die Karte beginnt als einzeiliges Raster mit einem 16:9-Bild. Sobald ihr Container eine Breite von 600px erreicht, wechselt sie zu einer festen Bildspalte neben dem Inhalt und macht das Bild quadratisch. Die Regeln sind ohne Zeilenumbrüche geschrieben, was gültiges CSS ist, aber bei Einfügen in eine Codebasis neu formatiert werden sollte.

    .card {
      display: grid;
      gap: 1rem;
    }.card img {
      width: 100%;
      aspect-ratio: 16 / 9;
      object-fit: cover;
    }@container (min-width: 600px) {
      .card {
        grid-template-columns: 200px 1fr;
        align-items: center;
      }  .card img {
        aspect-ratio: 1;
      }
    }
    

    Achten Sie darauf, was diese Stylesheet-Datei nicht enthält:

    @media (max-width: 768px)
    

    Die Karte berücksichtigt niemals den Ansichtsbereich. Alle Entscheidungen basieren ausschließlich auf ihrem Container.

    Kombination von Container-Queries mit einem automatisch anpassbaren Raster

    Container-Queries passen besonders gut zu einem Raster, das seine eigene Anzahl an Spalten festlegt:

    .dashboard {
      display: grid;
      grid-template-columns:
        repeat(auto-fit, minmax(280px, 1fr));
      gap: 1rem;
    }
    

    Mit repeat(auto-fit, minmax(280px, 1fr)) passt der Browser so viele Spalten mit mindestens 280px Breite ein, wie die Zeile zulässt, und dehnt sie aus, um den verbleibenden Platz zu füllen. Jedes Widget erhält anschließend seinen eigenen Container:

    .widget-container {
      container-type: inline-size;
    }
    

    und wechselt auf ein zweispaltiges internes Layout, wenn sein Slot breit genug ist:

    @container (min-width: 500px) {
      .widget {
        grid-template-columns: 1fr 1fr;
      }
    }
    

    Das Dashboard passt sich bei einer breiten Palette von Breiten ohne Viewport-Bruchpunkte an: Das Raster bestimmt, wie viel Platz jedes Widget erhält, und jedes Widget entscheidet selbst, wie es diesen Platz nutzt.

    Ein responsives Layout ist auch eine Frage der Benutzerfreundlichkeit

    Dass etwas auf dem Bildschirm Platz findet, bedeutet noch nicht, dass es einfach zu bedienen ist. Eine Reihe von Schaltern, die technisch gesehen in eine schmale Karte passen, kann dennoch eng sein und schwer zu bedienen. Eine Container-Abfrage kann die Schaltflächen stapeln und bei schmaler Karte auf die volle Breite dehnen (die Regel ist in zwei Abschnitten dargestellt):

    @container (max-width: 350px) {
      .actions {
        flex-direction: column;
      }
    
      .actions button {
        width: 100%;
      }
    }
    

    Vollbreitige gestapelte Schaltflächen bieten größere Berührungsziele und verringern versehentliche Berührungen. Dasselbe Prinzip kann auch auf Text angewandt werden, wobei die Größe je nach Breite des Komponentenbereichs angepasst wird:

    .card-title {
      font-size: clamp(1rem, 4cqi, 1.75rem);
    }
    

    Die konkreten Werte sind weniger wichtig als das Prinzip dahinter: das responsive Verhalten sollte die Bedingungen widerspiegeln, unter denen eine Komponente tatsächlich verwendet wird, und nicht eine Schätzung auf der Grundlage der Fenstergröße.

    Wo Media Queries weiterhin die richtige Wahl sind

    Container-Queries sind nicht automatisch die bessere Option. Globale Navigation ist ein klares Beispiel für eine Media Query, denn das Verbergen des vollständigen Menüs und das Anzeigen eines Menü-Buttons ist eine Entscheidung bezüglich der Seitenstruktur (diese Regel wird ebenfalls in zwei Teile unterteilt):

    @media (max-width: 900px) {
      .navigation {
        display: none;
      }
    
      .menu-button {
        display: block;
      }
    }
    

    Benutzerpräferenzen gehören zu einer völlig anderen Kategorie. Die Reduzierung der Animationen für Nutzer, die ihr Betriebssystem um weniger Bewegung gebeten haben, hat nichts mit Containern zu tun:

    @media (prefers-reduced-motion: reduce) {
      * {
        animation-duration: 0.01ms;
        animation-iteration-count: 1;
      }
    }
    

    Das ist eine Umgebungspräferenz, die nur über Media Queries abgerufen werden kann. Ziel ist es nicht, Media Queries ganz zu beseitigen, sondern lediglich, sie nicht mehr für Entscheidungen bezüglich Komponenten zu verwenden.

    Ein dreischichtiger Ansatz für responsives CSS

    Bei einer komponentenbasierten Anwendung hilft es, beim responsiven Styling in drei Schichten zu denken.

    Schicht 1: globale Responsivität mit Media Queries

    Media Queries kümmern sich um Aspekte, die die gesamte Seite betreffen:

    Page layout
    Navigation
    Global typography
    Major structural changes
    Application shell
    

    Schicht 2: Komponenten-Responsivität mit Container Queries

    Container Queries kümmern sich um die interne Gestaltung selbstständiger Komponenten:

    Cards
    Widgets
    Panels
    Reusable components
    Design-system components
    Dashboard modules
    

    Ebene 3: flüssiges Verhalten mit Funktionen und Einheiten

    Wo kein fester Bruchpunkt erforderlich ist, sollen die Werte mithilfe moderner Funktionen und Einheiten kontinuierlich skaliert werden:

    clamp()
    min()
    max()
    minmax()
    cqi
    cqw
    

    Ein Überschriftstext kann beispielsweise zusammen mit seinem Container zwischen zwei festgelegten Grenzen wachsen:

    .heading {
      font-size: clamp(1.5rem, 3cqi, 3rem);
    }
    

    Zusammen bilden diese Ebenen ein System statt nur eine Ansammlung von Bruchpunkt-Lösungen.

    Die eigentliche Veränderung liegt darin, wer die Entscheidungen trifft

    Es ist verlockend, Container-Abfragen als „Media Queries für Elemente“ zusammenzufassen. Das ist technisch korrekt, geht aber am Wesentlichen vorbei. Was sich ändert, ist das Verantwortungsmodell. Im traditionellen responsiven CSS legt die Seite im Grunde fest, wie sich jede Komponente verhält:

    Viewport
       ↓
    Media Query
       ↓
    Component
    

    Mit Container-Abfragen erhält jede Komponente ihre Anweisungen von ihrem eigenen Container:

    Component Container
            ↓
    Container Query
            ↓
    Component
    

    Komponenten, die ihre eigenen responsiven Regeln besitzen, sind leichter wiederverwendbar, zu verschieben, isoliert zu testen und zu warten. Dieser Übergang der Verantwortung ist der nachhaltige Vorteil.

    Eine Frage, um jeden Fall zu klären

    Falls Sie unsicher sind, welches Werkzeug zu verwenden ist, fragen Sie sich, ob die Layoutentscheidung sich auf die Seite oder auf die Komponente bezieht. Wenn sie sich auf die Seite bezieht, verwenden Sie eine Media Query. Wenn sie sich auf den zur Verfügung gestellten Platz einer Komponente bezieht, verwenden Sie eine Container Query. Bei wiederkehrenden Fällen gilt:

    Should the sidebar disappear?
            ↓
        Media Query
    
    Should this card switch from horizontal
    to vertical?
            ↓
      Container Query
    
    Should the navigation collapse?
            ↓
        Media Query
    
    Should this reusable widget change
    its internal layout?
            ↓
      Container Query
    

    Wichtige Erkenntnisse

    • Media Queries beschreiben den Ansichtsbereich und die Benutzerumgebung, weshalb sie sich auf die Seitenstruktur, Navigation, globale Typografie sowie Einstellungen wie reduzierte Bewegungen beziehen.
    • Container Queries beschreiben den Platz, den eine Komponente erhält, weshalb sie sich auf Karten, Widgets, Panels und Komponenten eines Design-Systems beziehen.
  • Eine Container-Abfrage gestaltet die Nachkommen des Containers; halten Sie den Container und das Komponenten-Element als getrennte Elemente und achten Sie auf Container, die eine breite Basis nach ihrem Inhalt benötigen.
  • Inhaltsbasierte Bruchpunkte, die dort festgelegt werden, wo eine Komponente tatsächlich nicht mehr funktioniert, sind langlebiger als breitenbasierte Bruchpunkte.
  • Container-Einheiten wie cqi und cqw, die in clamp() mit rem-Grenzen eingebettet sind, ermöglichen eine flexible Größenanpassung, ohne die Einstellungen zur Textgröße der Benutzer zu beeinträchtigen.
  • Namen Sie Container erst dann, wenn sie verschachtelt sind, und erstellen Sie sie nur an sinnvollen Grenzen der Komponenten.
  • Die zuverlässigste Konfiguration kombiniert Media-Abfragen, Container-Abfragen und fluiden CSS anstelle der Auswahl nur eines davon.