CSS-Layouts und responsives Design

  1. 1 Grundlagen
    1. Welchen Layoutmodus sollte ich verwenden?
    2. Arbeitsgrundsätze
  2. 2 Flexbox
  3. 3 Raster und Unterraster
    1. Codebeispiel: Raster und Unterraster
  4. 4 Containerabfragen
    1. Codebeispiel: Flüssige Typografie mit Containerabfrageeinheiten
  5. 5 Native Overlays, Ankerpositionierung und Stapelkontexte
  6. 6 Überlauf-Tracking und Layoutstabilität
  7. 7 Viewport-Mechanik und Track-Verteilung
  8. 8 Rasterspalten (auch „Masonry“ genannt)

1 Education Fundamentals

Verwenden Sie nach Möglichkeit die Layout-Engine des Browsers, um die Leistung zu verbessern. Verwenden Sie intrinsische Größen, logische Eigenschaften und aspect-ratio, bevor Sie auf hartcodierte Dimensionen oder komplizierte Medienabfragen zurückgreifen.

1.1 Welcher Layoutmodus soll verwendet werden?

Gehen Sie den Entscheidungsbaum von oben nach unten durch und halten Sie beim ersten Treffer an. Layouts können ineinander verschachtelt werden. Jede Entscheidung basiert auf dem Anwendungsfall für den jeweiligen Container.

  1. Handelt es sich um eine einfache Zeile ODER Spalte mit Elementen? Verwenden Sie Flexbox – 1D, inhaltsorientiert, Inhalte werden entlang einer einzelnen Achse verteilt.
  2. Muss ein verschachteltes Element an den Spuren des übergeordneten Rasters ausgerichtet werden? Verwenden Sie subgrid – 2D, beziehungsorientiert, übernimmt übergeordnete Tracks, sodass Enkelkinder sich an Geschwistern ausrichten können.
  3. Ist es eine komplexe Seiten- oder Komponentenstruktur mit Zeilen UND Spalten? Verwenden Sie grid – 2D, Layout-First, Sie definieren das Skelett und der Inhalt füllt es.
  4. Besteht der Inhalt aus einem langen Fließtext, der in ausgewogene Spalten aufgeteilt werden sollte? Verwenden Sie mehrere Spalten – 1D-Fluss, Zeitungsstil.
  5. Müssen Artikel unterschiedlicher Höhe dicht gepackt werden? Verwenden Sie heute grid mit grid-auto-flow: dense. Greifen Sie erst dann auf natives Masonry (auch „Grid Lanes“ genannt) zurück, wenn es in Ihrem Baseline-Ziel verfügbar ist (siehe § 8).
  6. Muss ein Element über der Seite schweben und räumlich an einen Trigger gebunden bleiben, auch über DOM-Grenzen oder Stapelkontexte hinweg? Verwenden Sie die Ankerpositionierung – anchor-name für den Trigger, position-anchor für das Overlay (siehe § 5).

1.2 Arbeitsprinzipien

  • Verwenden Sie logische Eigenschaften (inline-size, block-size, margin-inline, padding-block, inset-inline-start) für Layoutdimensionen und Abstände. Weitere Informationen finden Sie unter CSS: Moderne Architektur.
  • Wenden Sie das mentale Modell „Inhalt zuerst“ oder „Layout zuerst“ an: Flexbox, wenn die Elemente den Fluss vorgeben, Grid, wenn Sie zuerst das Skelett definieren.
  • Verwenden Sie die place-*-Kurzschreibweisen (place-content, place-items, place-self), um die Ausrichtung auf beiden Achsen in einer Deklaration festzulegen.
  • Verwenden Sie die intrinsische Größenanpassung (min-content, max-content, fit-content()) und flexible Tracks (fr, minmax()) anstelle von festen width/height-Werten. Das bedeutet weniger Medienabfragen und robustere Layouts.
  • Verwenden Sie overflow: hidden NICHT, um Layoutprobleme zu verbergen. Beheben Sie sie stattdessen an der Quelle. Beispielsweise verwenden bestimmte ersetzte Elemente wie <canvas>, <img>, <svg>, <video> und <iframe> standardmäßig display: inline, was in bestimmten Layouts zu einer Lücke führen kann. Anstatt die Lücke mit overflow zu entfernen, entfernen Sie sie, indem Sie einen Block-Display-Outside-Wert anwenden (z.B. display: block, grid, flex).
  • Verwende aspect-ratio, um Platz für Medien zu reservieren und Layout Shifts zu verhindern, bevor Assets geladen werden.
.sidebar       { inline-size: max-content; }    /* Size to longest unbreakable token. */
.main-content  { inline-size: fit-content; }    /* Grow to available space, no further. */
.media         { aspect-ratio: 16 / 9; inline-size: 100%; block-size: auto; }
body.centered  { display: grid; place-content: center; min-block-size: 100dvb; }

Informationen zu calc-size() und zur intrinsischen Größenanpassung unter Berücksichtigung von Einschränkungen finden Sie unter Mit intrinsischen Größen berechnen.

2 Flexbox

Eindimensionales Layout: Die Elemente werden entlang einer einzelnen Hauptachse angeordnet und an der Querachse ausgerichtet. Verwenden Sie es für Navigationsleisten, Symbolleisten, Elementzeilen und alle einzeiligen oder einspaltigen Verteilungen.

Das sollten Sie tun:

  • Erstellen Sie einen Kontext mit display: flex und legen Sie die Hauptachse mit flex-direction fest (row ist der Standardwert).
  • Verwenden Sie flex-wrap: wrap, wenn ein Überlauf möglich ist. nowrap ohne overflow: auto/hidden wird auf schmalen Darstellungsbereichen überlaufen.
  • Layer flex-wrap: balance nach flex-wrap: wrap in umgebrochenen Zeilen kompakter Elemente (z. B. Tag-Clouds, Filter-Pills, Schaltflächengruppen oder Navigationslinks), um Elemente gleichmäßig auf Zeilen zu verteilen und ein einzelnes verwaistes Element in der letzten Zeile zu verhindern. Browser ohne Unterstützung greifen auf wrap zurück.
  • Verwenden Sie für Elemente die Kurzschreibweise <grow> <shrink> <basis> von flex (z.B. flex: 1 1 250px), anstatt flex-grow/flex-shrink/flex-basis einzeln festzulegen.
  • Verwenden Sie gap (oder die Langform row-gap/column-gap) für den Abstand zwischen Elementen anstelle von untergeordneten Rändern.
  • Stellen Sie der Positionsausrichtung safe voran (z.B. align-items: safe center), damit fokussierbare Inhalte nicht abgeschnitten werden, wenn der Container schmaler als sein Inhalt ist.
  • Mit margin-inline-start: auto (oder margin-block-start: auto) können Sie ein einzelnes Element an das Ende der Hauptachse verschieben. Das ist die Standardmethode.
  • Mit align-self können Sie die Querachsenausrichtung für einzelne Elemente überschreiben.
  • Verwenden Sie align-items, um alle Elemente auf der Querachse zu zentrieren. Verwenden Sie margin: auto für ein einzelnes Element, um es unabhängig auf beiden Achsen zu zentrieren. Verwenden Sie align-content nur, wenn der Container umbricht und zusätzlichen Platz zwischen den Zeilen hat.
  • Legen Sie min-inline-size: 0 (oder min-width: 0) für Flex-Elemente fest, die lange, nicht umbrechbare Inhalte (URLs, Code, lange Strings) enthalten. Flex-Elemente werden standardmäßig nicht unter ihre Inhaltsgröße verkleinert, was zu einem Überlauf führt.

Nicht empfohlen:

  • Verwenden Sie justify-self nicht für Flex-Elemente – es funktioniert nur bei Grid-, Block- und absolut positionierten Layouts. Verwenden Sie stattdessen automatische Ränder.
  • Verwenden Sie nicht order oder flex-direction: *-reverse, um interaktive Inhalte neu anzuordnen. Sie ändern nur die visuelle Reihenfolge. Die DOM-Reihenfolge bestimmt weiterhin den sequenziellen Fokus, sodass der Tab-Ablauf auf der Tastatur nicht mit dem übereinstimmt, was der Nutzer sieht.
  • Verwechseln Sie space-around (halber Abstand an den Enden) nicht mit space-evenly (gleiche Abstände davor, dazwischen und danach).
  • Vergessen Sie nicht, die Achsen umzukehren: Wenn flex-direction: column, wird justify-content an der Blockachse und align-items an der Inline-Achse ausgerichtet – das Gegenteil der Standardeinstellung.
  • Legen Sie die Größe des Containers und seiner untergeordneten Elemente nicht so fest, dass sie sich gegenseitig ausfüllen. Das ist eine häufige Ursache für Überlauf und unerwartete Ergebnisse. Geben Sie einer Seite eine feste Größe.
  • Legen Sie nicht sowohl flex-basis als auch width/inline-size für dasselbe Element fest. flex-basis hat in einem Flex-Kontext Vorrang und width wird ignoriert. Verwenden Sie flex-basis (oder die Kurzschreibweise flex) als einzige verlässliche Quelle für die Größenanpassung von Flex-Elementen.
.card-grid        { display: flex; flex-flow: row wrap; gap: 1rem; }
.card-item        { flex: 1 1 250px; }                  /* grow, shrink, basis */
.card-item-action { margin-inline-start: auto; }        /* Push to main-axis end. */
.toolbar          { display: flex; align-items: safe center; }
.tag-list         { display: flex; flex-wrap: wrap; flex-wrap: balance; gap: 0.5rem; }

3 Raster und Unterraster

Zweidimensionales Layout: Zeilen UND Spalten explizit definieren oder von der Engine ableiten lassen. Mit Subgrid kann ein verschachteltes Raster die Tracks des übergeordneten Rasters übernehmen, sodass Enkelkinder an Geschwistern ausgerichtet werden.

Rastermerkmale auswählen:

  • Wissen Sie genau, wie viele Spalten Sie benötigen?
    • Ja – explizite Tracks verwenden (grid-template-columns: 200px 1fr, repeat(3, 1fr) usw.)
    • Benötigen verschiedene Spalten unterschiedliche Größen (Seitenleiste + Hauptbereich, Header, der sich über alle Spalten erstreckt)? → Verwenden Sie grid-template-areas für benannte, lesbare Regionen.
    • Sind alle Spalten einheitlich oder nur nach Zeilennummer positioniert? → repeat(N, ...) oder benannte Linien verwenden
    • Nein (responsiv, unbekannte Anzahl von Elementen) – repeat(auto-fit, minmax(min, 1fr)) verwenden
    • Sollen Elemente in der letzten Zeile gestreckt werden, um den verbleibenden Platz auszufüllen? → auto-fit
    • Sollen leere Tracks in der letzten Zeile ihre Mindestgröße beibehalten (Spalten-Ghost-Slots beibehalten)? → auto-fill
  • Müssen Sie ein Element an einer bestimmten Stelle platzieren?
    • Ja – Verwenden Sie grid-column: <start> / <end> oder grid-area: <name>.
    • Nein (nur über mehrere Tracks, die Position im Ablauf ist egal) – grid-column: span <n> verwenden
  • Müssen untergeordnete Elemente die Spurgrößen des übergeordneten Rasters übernehmen (Ausrichtung mit unregelmäßigen Kanten über gleichgeordnete Elemente hinweg)?
    • Ja – Subgrid auf der betroffenen Achse verwenden
    • Ist die Anzahl der untergeordneten Elemente pro Zelle variabel? → Subgrid nur eine Achse; für die andere grid-auto-rows/grid-auto-columns verwenden
    • Ist die Anzahl der Kinder festgelegt? → Subgrid auf beiden Achsen ist in Ordnung
    • Nein – Standardraster, kein Unterraster erforderlich

Das sollten Sie tun:

  • Kontext mit display: grid herstellen.
  • Verwenden Sie grid-template-areas für komplexe Layouts auf Seitenebene. Die Bereichsnamen sind selbsterklärend und die Deklaration kann in Zeilen und Spalten ausgerichtet werden, um die Lesbarkeit zu verbessern.
  • Verwenden Sie repeat(auto-fit, minmax(200px, 1fr)) für responsive Kartenraster, bei denen gefüllte Tracks gestreckt werden, um die Zeile zu füllen, oder auto-fill, um leere wiederholte Tracks in ihrer Mindestgröße beizubehalten.
  • Verwenden Sie fr für die proportionale Trackverteilung und minmax(min, max) für flexible, aber begrenzte Tracks.
  • Positionieren Sie Elemente mit grid-column: span <n>, um die Größe über Tracks hinweg festzulegen, mit grid-column: <start> / <end>, um sie an bestimmten Linien zu platzieren, oder mit grid-area: <name> für benannte Bereiche.
  • Verwenden Sie ein Subgrid (grid-template-columns: subgrid oder grid-template-rows: subgrid), um das Problem der ungleichmäßigen Kanten in Kartenlisten zu lösen. Interne Elemente wie Titel, Metadaten und CTAs werden dann über gleichrangige Elemente hinweg ausgerichtet.
  • Kombinieren Sie eine Subgrid-Deklaration mit einer vorherigen expliziten grid-template-rows/-columns-Deklaration als Fallback für ältere Browser.

Nicht empfohlen:

  • Die Größe des auto-fit/auto-fill-Tracks wird nicht aus dem Elementinhalt, sondern aus dem Größenargument repeat() abgeleitet.
  • Verwenden Sie grid-auto-flow: dense nicht für interaktive Inhalte. Elemente werden effizient gepackt, aber visuell neu angeordnet, wodurch der Tab-Ablauf der DOM-Reihenfolge unterbrochen wird.
  • Wenden Sie das Subgrid nicht auf beide Achsen an, wenn die Anzahl der untergeordneten Elemente variabel ist. Zusätzliche Elemente landen im letzten Track. Verwenden Sie stattdessen grid-auto-rows/grid-auto-columns für die implizite Achse.
  • Verwechseln Sie justify-items/align-items (richtet den Elementinhalt innerhalb des Tracks aus) nicht mit justify-content/align-content (richtet die Grid-Tracks innerhalb des Containers aus). Wenn Sie den falschen verwenden, hat das keine Auswirkungen.
  • Verwenden Sie repeat(auto-fit/auto-fill, ...) nicht ohne eine definierte inline-size für den Container. Innerhalb von display: inline-grid oder einem Flex-Element ohne Größe hat der Container keine Breite, die aufgeteilt werden kann, was die Anzahl der Tracks unvorhersehbar macht.

3.1 Codebeispiel: Raster und Unterraster

Seiten-Shell: <main class="page-layout"> enthält <header>, <aside>, ein <section class="card-grid"> mit <div class="card"> untergeordneten Elementen und <footer>.

/* Align grid-template-areas in rows and columns for readability. */
.page-layout {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas:
    "header  header  header"
    "sidebar main    main"
    "footer  footer  footer";
  gap: 1.5rem;
}

header  { grid-area: header; }
aside   { grid-area: sidebar; }
footer  { grid-area: footer; }

.card-grid {
  grid-area: main;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  grid-template-rows: auto 1fr; /* title block, body block */
  gap: 1rem;
}

.card {
  grid-row: span 2;
  display: grid;
  /* Same-cascade fallback: ignored when subgrid is supported. */
  grid-template-rows: auto 1fr;
  grid-template-rows: subgrid;
}

4 Containerabfragen

Fragen Sie die Größe (oder den berechneten Stil) eines übergeordneten Containers anstelle des Darstellungsbereichs ab. Mentales Modell: Containerabfragen = Komponentenkontext; Medienabfragen = globales Seitenlayout und Nutzereinstellungen (prefers-color-scheme, prefers-reduced-motion).

Das sollten Sie tun:

  • Richten Sie mit container-type: inline-size (nur Abfragen zur Breite) oder container-type: size (beide Achsen) einen Containerkontext für einen Wrapper ein, bevor seine untergeordneten Elemente abgefragt werden können.
  • Benennen Sie Container mit container-name (oder der Kurzform container: container: card / inline-size), wenn verschachtelte Kontexte kollidieren könnten.
  • Containerabfrageeinheiten bei der Berechnung von flüssigem Typ und Abstand einbeziehen: cqi/cqb (logische Inline-/Blockeinheiten), cqw/cqh (physische Einheiten), cqmin/cqmax.
  • Geben Sie dem Container immer eine feste block-size, wenn container-type: size verwendet wird. Ohne diese werden untergeordnete Elemente minimiert, da die Größenbeschränkung den Container dazu zwingt, seinen Inhalt zu ignorieren.

Nicht empfohlen:

  • Verwenden Sie block-size nicht als container-type-Wert, da er ungültig ist. Verwenden Sie size für beide Achsen.
  • Nachdem Sie container-type deklariert haben, sollten Sie nicht erwarten, dass die intrinsische Größe der untergeordneten Elemente den Container beeinflusst. Der Container wird so berechnet, als hätte er keine untergeordneten Elemente, sobald die Eingrenzung aktiv ist.
  • Verlassen Sie sich nicht auf Containerabfrageeinheiten in Nachfolgern eines nicht qualifizierten Vorfahren. Sie greifen auf den kleinen Darstellungsbereich (svw/svh) zurück.

4.1 Codebeispiel: Flüssige Typografie mit Containerabfrageeinheiten

.card-wrapper {
  container: card / inline-size; /* shorthand for container-name / container-type */
}

@container card (inline-size > 400px) {
  .content {
    display: flex;
    gap: 2rem;
  }
}

.title {
  /* Fluid type bound to the container width, not the viewport. */
  font-size: clamp(1rem, 4cqi, 2rem);
}

Informationen zu komponentenorientierten responsiven Styling-Mustern finden Sie unter Größenabhängiges Styling und Fluide Skalierung.

5 Native Overlays, Ankerpositionierung und Stapelkontexte

Wann sollte welches Overlay-Primitive verwendet werden?

  • Verwenden Sie popover für vorübergehende, nicht modale UI-Elemente (Flyouts, Toasts, Kurzinfos). Diese befinden sich in der obersten Ebene und erfordern keine z-index-Verwaltung.
  • Verwenden Sie <dialog> mit .showModal() für modale Interaktionen, die ein Focus Trapping und einen inerten Hintergrund erfordern.
  • Kombinieren Sie popover und .showModal() nicht für dasselbe Element, da es sich um sich gegenseitig ausschließende Laufzeitstatus handelt.

Ankerpositionierung (räumliches Layout von Overlays):

  • Verwenden Sie position-area (oder anchor() bei Insets) und anchor-size(), um ein Overlay relativ zu seinem Trigger zu positionieren und seine Größe festzulegen.
  • Verwenden Sie position-try-fallbacks: flip-block (oder flip-inline), damit der Browser das Overlay neu positioniert, wenn es über den Darstellungsbereich hinausgeht.
  • Mischen Sie keine physischen und logischen Keywords in einem einzelnen position-area-Wert. Wählen Sie ein Koordinatensystem aus.
  • Führen Sie eine Feature-Erkennung mit @supports (anchor-name: --x) durch und stellen Sie ein Fallback mit absoluter Position bereit.

Ausführliche Informationen zur Implementierung, zu Polyfill-Strategien und zur Referenz für den Wert popover finden Sie unter Declarative Dialog and Popover Control und Position Aware Tooltips. Informationen zur Ankerpositionierung für Drop-down-Menüs, Flyouts und Tab-Indikatoren finden Sie unter Viewport-Aware Dropdowns und Anchor Positioning Tab Underline.

6 Überlauf-Tracking und Layoutstabilität

Layoutverschiebungen, Scrollleisten und Clipping vorhersehbar verwalten.

Das sollten Sie tun:

  • Verwenden Sie overflow: auto, damit Scrollleisten nur angezeigt werden, wenn der Inhalt tatsächlich überläuft.
  • Verwenden Sie overflow: clip, um Inhalte ohne Scrollcontainer zu beschneiden. Mit overflow-clip-margin können Sie Überlauf zulassen.
  • Verwende scrollbar-gutter: stable, um Platz für Scrollleisten zu reservieren und Layout Shifts zu verhindern, wenn Inhalte größer werden.
  • Verwenden Sie overscroll-behavior: contain (oder none) für verschachtelte Scrollbereiche (z. B. Seitenleisten, Modale und Listen), um zu verhindern, dass Scrollketten auf die Seite übertragen werden, oder overscroll-behavior: chain für Zwischen-Scroll-Wrapper (z. B. Swipe-to-Reveal-Tracks), wenn übermäßiges Scrollen an einen übergeordneten Scroller weitergegeben werden soll, ohne dass ein lokaler Rubber-Band-Bounce ausgelöst wird.
  • Verwenden Sie die Triade -webkit-line-clamp + display: -webkit-box + -webkit-box-orient: vertical für mehrzeilige Kürzungen. Trotz des Präfixes ist dieses Muster vollständig spezifiziert und nicht veraltet. Deklarieren Sie die Kurzschreibweise line-clamp ohne Präfix daneben. Browser, die sie noch nicht unterstützen, ignorieren das Attribut einfach. Nicht empfohlen:

  • Verwenden Sie nicht overflow: scroll, wenn auto ausreicht. scroll erzwingt Scrollleisten, auch wenn es nichts zu scrollen gibt.

  • Verwenden Sie overflow: hidden nicht, wenn Sie nur Inhalte abschneiden möchten. Mit hidden wird ein Scrollcontainer erstellt, der programmatisch gescrollt werden kann.

.scrollable-list {
  max-block-size: 400px;
  overflow-y: auto;
  scrollbar-gutter: stable;       /* Reserve scrollbar space. */
  overscroll-behavior: contain;   /* No scroll chaining into the page. */
}

.snippet {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  line-clamp: 3;                  /* Ignored where unsupported. */
  overflow: clip;
}

Ausführliche Informationen zu overflow: clip und overflow-clip-margin finden Sie unter Overflow Clipping Control. Informationen zur Übergabe von verschachteltem Scrollen mit overscroll-behavior: chain in einer Benutzeroberfläche, die durch Wischen eingeblendet wird, finden Sie unter Zum Einblenden wischen. Informationen zu Farbe, Größe und Design von Scrollleisten finden Sie unter Farbe oder Breite einer Scrollleiste anpassen, Dunkler Modus und Scrollleiste an Einstellungen für hohen Kontrast anpassen.

7 Viewport-Mechanismen und Track-Verteilung

  • Verwenden Sie dvh/dvw für Container mit mobilem Layout, die Änderungen der Browser-UI berücksichtigen müssen (URL-Leiste wird minimiert/maximiert).
  • Verwenden Sie 100vw nicht für Layouts mit voller Breite, da die Breite der Scrollleiste ignoriert wird und es zu einem horizontalen Überlauf kommt. Verwenden Sie stattdessen 100%, 100dvw oder 100svw.

Informationen zum Einstiegspunkt für responsives Design (dynamische Darstellungsbereichseinheiten, Platz für Medien reservieren, fließende Typografie) finden Sie unter Responsives Design.

8 Rasterspalten (auch Masonry genannt)

Die Spezifikation wird unter dem Namen „Grid Lanes“ (z.B. display: grid-lanes) entwickelt, während frühere experimentelle Prototypen grid-template-rows: masonry verwendeten.

Das sollten Sie tun:

  • Verwenden Sie heute ein Raster mit grid-auto-flow: dense für eine dichte Anordnung und akzeptieren Sie, dass die DOM-Reihenfolge möglicherweise nicht der visuellen Reihenfolge entspricht.
  • Verwenden Sie mehrspaltige Layouts (columns: 3; column-gap: 1rem) für inhaltsreiche, kachelartige Darstellungen, wenn die Elemente Dokumentfragmente und keine gleichwertigen Karten sind.
  • Behandle grid-template-rows: masonry nur als progressive Verbesserung – verwende @supports für die Feature-Erkennung.

Nicht empfohlen:

  • Machen Sie grid-template-rows: masonry erst dann zu einer harten Anforderung, wenn Ihr Baseline-Ziel erreicht ist.
.gallery       { columns: 3 200px; column-gap: 1rem; }
.gallery > *   { break-inside: avoid; margin-block-end: 1rem; }

@supports (grid-template-rows: masonry) {
  .gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-template-rows: masonry;
    gap: 1rem;
    columns: unset;
  }
}