Sie bauen ein Dashboard: Header, Sidebar, Hauptbereich, Footer. Sie greifen zu Flexbox, weil das nun mal das Layout-Werkzeug ist, das Sie kennen – und zwanzig Minuten später stapeln sich drei verschachtelte display: flex-Container, die Sidebar hält ihre Breite nur mit einer willkürlichen Pixelzahl, und der Footer schwebt mitten auf der Seite, sobald der Inhalt kurz ist. Das ist kein Flexbox-Bug. Sie wenden ein eindimensionales Werkzeug auf ein zweidimensionales Problem an.

Flexbox verteilt Platz entlang einer einzigen Linie, einer Zeile oder einer Spalte. Grid platziert Elemente gleichzeitig in Zeilen und Spalten, die zueinander in Beziehung stehen. Die meiste Verwirrung zwischen Grid und Flexbox entsteht, weil genau diese Unterscheidung übersprungen wird.

Flexbox ist eindimensional, Grid ist zweidimensional

Flex-Items leben auf einer Achse, festgelegt durch flex-direction. Mit flex-direction: row reiht sich jedes Kind-Element von links nach rechts auf (oder von rechts nach links, mit row-reverse); mit column stapeln sie sich von oben nach unten. Dazu kommt eine Querachse, auf der align-items wirkt. Aber Flexbox verlangt nie, dass sich ein Element in einer Zeile an einem Element in einer anderen Zeile ausrichtet. Selbst wenn Elemente umbrechen, wird jede umgebrochene Zeile für sich angeordnet.

1
2
3
4
5
6
7
.toolbar {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
}

Grid-Items sitzen in Zeilen und Spalten, die Sie vorab festlegen, und Sie können jedes Element gegenüber beiden Achsen gleichzeitig platzieren und dimensionieren.

1
2
3
4
5
6
.page {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr auto;
  gap: 1rem;
}

1fr ist eine fraction unit, eine Bruchteilseinheit. Der Browser zieht zuerst die festen Spuren (240px) ab und verteilt erst dann den Rest auf die fr-Spuren. grid-template-columns: 1fr 2fr gibt der zweiten Spalte also das Doppelte vom Anteil der ersten am übrig gebliebenen Platz – nicht das Doppelte ihrer Gesamtbreite.

Eine Navbar mit Flexbox bauen

Eine Navbar ist das Lehrbuchbeispiel für ein eindimensionales Layout: ein Logo, ein paar Links und ein Button in einer Zeile, mit Platz, der zwischen ihnen verteilt wird.

1
2
3
4
5
6
7
8
9
<nav class="navbar">
  <div class="logo">Acme</div>
  <ul class="links">
    <li><a href="/pricing">Pricing</a></li>
    <li><a href="/docs">Docs</a></li>
    <li><a href="/blog">Blog</a></li>
  </ul>
  <button class="cta">Sign up</button>
</nav>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.navbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 2rem;
}

.links {
  display: flex;
  gap: 1.5rem;
  list-style: none;
}

justify-content: space-between schiebt das Logo an den linken Rand und den Button an den rechten, mit der Linkliste in der Mitte. align-items: center hält alle drei vertikal zentriert, selbst wenn das Logo höher ist als der Button. Die Linkliste ist ihr eigener Flexbox-Container, ihr Abstand kommt also aus gap, und Sie fassen nie einen Margin an.

Verkleinern Sie den Viewport, passiert zunächst nichts, denn flex-wrap steht standardmäßig auf nowrap: Die Elemente schrumpfen bis auf ihre minimale Inhaltsbreite und laufen dann über die Navbar hinaus. Mit flex-wrap: wrap rutschen die Elemente, die nicht mehr passen, in eine zweite Zeile. Diese zweite Zeile wird unabhängig angeordnet und richtet sich an nichts in der ersten aus. Für eine Navbar ist das kein Problem. Bei einem Layout, in dem sich Zeilen aneinander ausrichten müssen, ist genau das das Zeichen, dass Sie an die Grenze dessen stoßen, was Flexbox leisten kann.

Ein Dashboard-Layout mit CSS-Grid bauen

Benannte Grid-Areas lassen Sie ein Seitenlayout lesen, ohne Zeilen- und Spaltennummern im Kopf übersetzen zu müssen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
.dashboard {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: 64px 1fr 48px;
  grid-template-areas:
    "sidebar header"
    "sidebar main"
    "sidebar footer";
  min-height: 100vh;
}

.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }

Jeder Name in grid-template-areas muss ein Rechteck bilden, und die Strings sind ein Abbild der Seite: Sie sehen direkt im CSS, dass die Sidebar über alle drei Zeilen geht. Die Sidebar hat eine feste Breite von 240px, Header und Footer bekommen explizite Höhen, und main nimmt sich, was übrig bleibt, weil seine Zeile 1fr ist. Der Footer sitzt auch bei einer kurzen Seite am unteren Rand des Viewports, ganz ohne absolute Positionierung und ohne JavaScript, denn min-height: 100vh auf dem Container plus eine 1fr-Zeile erledigen das bereits.

Die Flexbox-Variante derselben Seite braucht zwei Container: eine Flex-Row für Sidebar und Content, dann eine Flex-Column innerhalb des Content-Bereichs für Header, Main und Footer, mit flex: 1 auf Main, um den Footer nach unten zu drücken. Es funktioniert. Sie stapeln zwei eindimensionale Layouts aufeinander, um ein zweidimensionales vorzutäuschen, und jede Änderung an der Seitenstruktur bedeutet, an beiden Stellen etwas anzufassen.

Ein responsives Card-Grid ohne Media Queries

Dieses eine Pattern ersetzt eine ganze Menge Media-Query-Boilerplate: ein Grid aus Cards, das Spalten hinzufügt oder weglässt, je nachdem, wie sich der Viewport ändert.

1
2
3
4
5
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1.5rem;
}

repeat(auto-fit, minmax(220px, 1fr)) sagt dem Browser, so viele Spalten mit mindestens 220px unterzubringen, wie in die Zeile passen, und sie dann so zu strecken, dass sie die Zeile ausfüllen. Ein 1400px breiter Container bekommt fünf Spalten von jeweils etwa 260px, ein Smartphone eine einzige Spalte in voller Breite. Nirgendwo ein Breakpoint.

auto-fit und auto-fill unterscheiden sich nur, wenn Sie weniger Cards haben, als eine Zeile füllen würden. auto-fit kollabiert die leeren Spuren, sodass sich die vorhandenen Cards über die ganze Zeile strecken. auto-fill behält die leeren Spuren, also bleiben die Cards so breit, wie sie in einer vollen Zeile wären, und der Rest der Zeile bleibt leer. Nehmen Sie auto-fill, wenn die Card-Breite unabhängig von der Anzahl konstant bleiben soll, etwa bei einem Produkt-Grid, in dem drei Ergebnisse nicht gleich auf ein Drittel der Seite aufgeblasen werden sollen. Nehmen Sie auto-fit, wenn eine halbleere Zeile schlechter aussieht als breite Cards, etwa bei einem Dashboard mit zwei oder drei Summary-Tiles.

Grid und Flexbox zusammen verwenden

Für eine ganze Seite entscheiden Sie sich selten nur für eines von beiden. Grid übernimmt das Skelett, Flexbox den eindimensionalen Inhalt innerhalb jedes Bereichs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.page {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr;
}

.card {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

Jede .card sitzt in einer Grid-Zelle und streckt sich auf deren Höhe, weil Grid-Items standardmäßig gestreckt werden. Innerhalb der Card bilden Header, Body und Footer eine einzige Spalte, sodass justify-content: space-between den Footer bei jeder Card ganz unten platziert, egal wie viel Body-Text sie enthält. Das ist die Faustregel in der Praxis: Grid für alles, was gleichzeitig zu einer Zeile und einer Spalte in Beziehung steht, Flexbox für Inhalt, der entlang einer Linie läuft, verschachtelt dort, wo Grid ihn platziert hat.

Wenn Sie mit Grid zwischen einer Card-Ansicht und einer Listenansicht wechseln, speichern Sie die Wahl der Besucherin oder des Besuchers in localStorage. Das ist ein einziger setItem-Aufruf, und die Wahl überlebt Reloads und geschlossene Tabs, ohne dass ein Server-Roundtrip nötig ist.

Wann sich Subgrid für verschachtelte Grids lohnt

Ein Grid innerhalb eines Grid-Items beginnt seine eigenen Spuren, unabhängig von denen des Eltern-Grids. Das bricht genau dann, wenn sich die internen Zeilen einer Card (Bild, Titel, Beschreibung, Preis) über eine ganze Reihe von Cards hinweg ausrichten sollen. Setzen Sie grid-template-rows: subgrid (oder grid-template-columns: subgrid) auf das verschachtelte Grid, nutzt es die Spuren des Eltern-Grids wieder, statt eigene zu definieren – die Titelzeile jeder Card richtet sich dann an ihren Nachbarn aus, obwohl jede Card weiterhin ihr eigener Grid-Container ist. Subgrid funktioniert in allen aktuellen gängigen Browsern, Sie können es also direkt verwenden, statt gleiche Höhen mit festen Werten zu erzwingen.

Wann Flexbox die bessere Wahl ist, selbst wenn Grid es auch könnte

Grid kann mit grid-auto-flow: column auch eine einzelne Zeile anordnen. Für wirklich eindimensionalen Inhalt hat Flexbox aber etwas, das Grid nicht hat: flex-grow und flex-shrink lassen jedes Element mehr oder weniger vom übrig gebliebenen Platz beanspruchen, je nach eigenem Inhalt. Genau das brauchen Sie bei einer Suchleiste, die sich den Platz nehmen soll, den ein Button und ein Icon mit fester Breite übrig lassen. Greifen Sie standardmäßig zu Grid, wenn Zeilen und Spalten zueinander in Beziehung stehen müssen; nehmen Sie Flexbox, wenn Sie eine einzelne Zeile anordnen und pro Element Wachsen und Schrumpfen wollen, nicht nur Ausrichtung.

Der Fehler passiert auch andersherum. Manche bauen ein mehrspaltiges Layout aus flex-wrap: wrap plus einer prozentualen width auf jedem Element, um ein Grid vorzutäuschen. Das hält, bis die letzte Zeile weniger Elemente hat als die anderen. Dann schiebt space-between die letzten beiden Elemente an die gegenüberliegenden Ränder, oder flex-grow streckt sie breiter als alles darüber. Der Browser verteilt immer noch Elemente entlang von Linien, er füllt kein Grid. Wenn Sie Spalten zählen, nehmen Sie Grid.

Wenn Sie beim Resize trotzdem JavaScript brauchen, etwa um ein Tooltip neben einer Card neu zu positionieren, debouncen Sie den Handler, statt ihn bei jedem Resize-Event auszuführen, das der Browser feuert.

Grid oder Flexbox: wie Sie sich entscheiden

Eine Frage entscheidet das: Müssen sich die Elemente in Zeilen und Spalten aneinander ausrichten, oder ist es eine einzelne Linie von Elementen?

SituationVerwenden
Navbar, Button-Gruppe, ToolbarFlexbox
Seitenskelett: Header, Sidebar, Main, FooterGrid
Responsives Card- oder Produkt-GridGrid (auto-fit/auto-fill + minmax)
Ein Element zentrieren oder wenige gleichmäßig verteilenFlexbox
Formular-Layout mit ausgerichteten Labels und InputsGrid
Inhalt innerhalb einer einzelnen Grid-Zelle (Header/Body/Footer einer Card)Flexbox, verschachtelt im Grid

Wenn Sie das nächste Mal für ein neues Layout ein Stylesheet aufmachen, skizzieren Sie zuerst die Boxen. Zeichnen Sie Linien, die sich kreuzen, ist es Grid. Sitzt jede Box auf einer einzigen Linie, ist es Flexbox.

Verwandte Artikel