Grid: Zeilen und Spalten
Kurz gesagt: CSS Grid ordnet Items gleichzeitig in Zeilen und Spalten an: Spalten mit fr und gap, ein Katalog mit repeat(auto-fill, minmax()) und ein Seitenlayout mit grid-template-areas.
Problem
Das letzte Produkt ist riesig
Der Katalog nutzt flex-wrap aus der letzten Lektion. Vier Produkte: Drei passen in die erste Zeile, und das vierte steht allein in der zweiten Zeile und ist so breit wie die ganze Zeile.
Konsole
Das ist kein Fehler von Flexbox. Jede Zeile eines Flex-Containers verteilt ihren eigenen freien Platz und weiß nichts von der Zeile darüber. Die zweite Zeile hat ein Produkt, also nimmt es alles: 768px. Wir brauchen etwas, das die Spalten einmal für den ganzen Katalog zeichnet.
Die Idee
Zeilen und Spalten
Grid ist ein Layout in zwei Dimensionen: Der Container zeichnet Spalten und Zeilen, und die Items kommen der Reihe nach in die Zellen, Zeile für Zeile.
.catalog { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 24px;}display: gridmacht das Element zum Grid-Container, und seine Kinder sind Grid-Items.grid-template-columnssagt, wie viele Spalten es gibt und wie breit jede ist:200px 1frist eine 200px-Spalte und eine Spalte, die den Rest nimmt.frist ein Anteil am freien Platz, nachdem die Spalten mit fester Größe und die gaps abgezogen sind.1fr 2fr 1frist 1 : 2 : 1.repeat(3, 1fr)ist dasselbe wie1fr 1fr 1fr.gapist der Abstand zwischen den Spalten und zwischen den Zeilen.
Der Unterschied zu Flexbox: Bei Flex entscheiden die Items, wie breit sie sind, und jede Zeile steht für sich. Bei Grid entscheidet der Container über die Spalten, und die Items füllen sie nur. Darum bleibt das letzte Produkt in seiner Spalte.
Probier es aus
Drei Spalten
Mach den Katalog zu einem Grid mit drei gleich breiten Spalten und 24px gap. Der Katalog ist bis zu 720px breit, also muss jede Karte, auch die letzte, (720 − 2 × 24) : 3 = 224px breit sein.
Konsole
Lösung
index.html<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Katalog</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Katalog</h1>
<div class="catalog">
<article class="card">
<img src="img/bread.svg" alt="" width="48" height="48">
<h2>Weißbrot</h2>
<p>1,20 €</p>
</article>
<article class="card">
<img src="img/bread.svg" alt="" width="48" height="48">
<h2>Vollkornbrot</h2>
<p>1,90 €</p>
</article>
<article class="card">
<img src="img/milk.svg" alt="" width="48" height="48">
<h2>Frischmilch</h2>
<p>1,60 €</p>
</article>
<article class="card">
<img src="img/milk.svg" alt="" width="48" height="48">
<h2>Joghurt</h2>
<p>1,10 €</p>
</article>
<article class="card">
<img src="img/cheese.svg" alt="" width="48" height="48">
<h2>Weißkäse</h2>
<p>7,90 €</p>
</article>
</div>
</body>
</html>
style.css* { box-sizing: border-box; }
body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; }
h1 { margin: 0 0 12px; font-size: 1.4rem; }
.catalog {
max-width: 720px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 24px;
}
.card {
flex: 1 1 200px;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
}
.card h2 { margin: 8px 0 4px; font-size: 1rem; }
.card p { margin: 0; }
display: grid und grid-template-columns: 1fr 1fr 1fr (oder repeat(3, 1fr)). gap: 24px steht schon da, und bei Grid gilt es zwischen den Spalten und zwischen den Zeilen. flex: 1 1 200px an den Karten bewirkt nichts mehr: Sie sind keine Flex-Items. 1fr 2fr 1fr ergibt drei Spalten, aber die mittlere ist doppelt so breit.
Die Idee
So viele, wie passen
Drei Spalten sind am Computer in Ordnung. Auf einem Handy sind sie je 100px breit, und das Produkt lässt sich nicht lesen. Wir wollen nicht die Zahl angeben, sondern eine Regel: „Spalten von mindestens 200px, so viele, wie passen.“
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));minmax(200px, 1fr): Die Spalte ist mindestens 200px und höchstens ein Anteil breit.auto-fillstatt einer Zahl: Der Browser legt so viele Spalten der kleinsten Breite an, wie mit den gaps passen.- Dann wird der Rest gleichmäßig verteilt (
1fr), und die Spalten wachsen bis ans Ende der Zeile.
Sieh es dir an
So wird gezählt
Dieselbe Regel in einem Katalog von 960, 680 und 328px.
.catalog { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px;}Schritt für Schritt, in Worten
- Der Katalog ist ein Grid. Die Spalten sind nicht mit einer Zahl angegeben, sondern mit einer Regel: so viele, wie passen, jede mindestens 200px.
- Ein Katalog von 960px. Der Browser legt Spalten zu je 200px mit 16px gap an, solange sie passen: 4 × 200 + 3 × 16 = 848px. Eine fünfte würde 1064px erreichen: Sie passt nicht.
- Es bleiben 960 − 848 = 112px. 1fr teilt sie gleichmäßig auf: Jede Spalte ist (960 − 3 × 16) : 4 = 228px breit.
- Bei 680px passen drei Spalten (3 × 200 + 2 × 16 = 632). Eine vierte bräuchte 848. Jede wird (680 − 32) : 3 = 216px breit.
- Bei 328px, einem Handy, passt eine: Zwei bräuchten 416px. Sie nimmt die ganze Breite: 328px.
- Eine Regel, drei Anordnungen. Die Anzahl der Spalten folgt der Breite des Katalogs, ohne Media Queries. Die Produkte füllen die Spalten Zeile für Zeile.
Derselbe Katalog als Seite, zuerst auf einem Handy (360px), dann auf einem Computer (1000px). Der Code ist derselbe.
Konsole
Konsole
Die Spalten hängen von der Breite des Katalogs ab, nicht von der des Bildschirms. Ein Katalog in einer schmalen Spalte der Seite hat weniger Spalten, auch auf einem großen Bildschirm.
Probier es aus
Eine Regel für beide Stellen
Die Seite hat einen Katalog und eine schmale Spalte „Auch gekauft“ (220px). Beide sind .catalog mit flex-wrap, und das letzte Produkt im Katalog ist zu breit. Ändere die Regel für .catalog: Grid, Spalten von mindestens 200px, so viele, wie passen, bis zum Ende gestreckt. Die Prüfung erwartet 3 Spalten im Katalog, jede 208px breit, und 1 Spalte von 220px rechts.
Konsole
Lösung
index.html<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Katalog</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="page">
<main>
<h1>Katalog</h1>
<div class="catalog">
<article class="card">
<img src="img/bread.svg" alt="" width="48" height="48">
<h2>Weißbrot</h2>
<p>1,20 €</p>
</article>
<article class="card">
<img src="img/bread.svg" alt="" width="48" height="48">
<h2>Vollkornbrot</h2>
<p>1,90 €</p>
</article>
<article class="card">
<img src="img/milk.svg" alt="" width="48" height="48">
<h2>Frischmilch</h2>
<p>1,60 €</p>
</article>
<article class="card">
<img src="img/milk.svg" alt="" width="48" height="48">
<h2>Joghurt</h2>
<p>1,10 €</p>
</article>
<article class="card">
<img src="img/cheese.svg" alt="" width="48" height="48">
<h2>Weißkäse</h2>
<p>7,90 €</p>
</article>
</div>
</main>
<aside>
<h2>Auch gekauft</h2>
<div class="catalog">
<article class="card">
<img src="img/product.svg" alt="" width="48" height="48">
<h2>Butter</h2>
<p>3,20 €</p>
</article>
<article class="card">
<img src="img/tomatoes.svg" alt="" width="48" height="48">
<h2>Tomaten</h2>
<p>3,60 €</p>
</article>
</div>
</aside>
</div>
</body>
</html>
style.css* { box-sizing: border-box; }
body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; }
h1 { margin: 0 0 12px; font-size: 1.4rem; }
.page {
max-width: 900px;
display: grid;
grid-template-columns: 1fr 220px;
gap: 24px;
}
aside h2 { margin: 0 0 12px; font-size: 1.1rem; }
.catalog {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
.card {
flex: 1 1 200px;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
}
.card h2 { margin: 8px 0 4px; font-size: 1rem; }
.card p { margin: 0; }
display: grid und grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)). Der Katalog ist 900 − 220 − 24 = 656px breit: Drei Spalten passen (632px), und der Rest macht sie je 208px breit. In die 220px-Spalte passt eine, und sie wird 220px breit. repeat(3, 1fr) macht auch in der schmalen Spalte drei Spalten, in denen die Karten gequetscht werden. Mit repeat(auto-fill, 200px) wachsen die Spalten nicht, und rechts bleiben 20px leer.
Die Idee
Bereiche der Seite
Grid ordnet auch die Seite selbst an: die Kopfzeile, die Filter links, den Katalog, die Fußzeile. Die Bereiche lassen sich mit Namen zeichnen:
.page { display: grid; grid-template-columns: 220px 1fr; grid-template-areas: "header header" "filters main" "footer footer"; gap: 16px;}header { grid-area: header; }.filters { grid-area: filters; }- Jede Zeile in Anführungszeichen ist eine Zeile des Grids, und jedes Wort ist eine Zelle. Ein Wort in zwei benachbarten Zellen ergibt einen Bereich, der beide nimmt: Die Kopfzeile geht über zwei Spalten.
grid-area: headersetzt das Element in den Bereich mit diesem Namen.- Jede Zeile muss so viele Zellen haben, wie es Spalten gibt, und ein Bereich muss ein Rechteck sein.
Ohne Namen wird ein Item über die Nummern der Linien zwischen den Spalten platziert: grid-column: 1 / 3 geht von der ersten bis zur dritten Linie (zwei Spalten), und grid-column: 1 / -1 geht bis zur letzten Linie. grid-column: span 2 nimmt zwei Spalten, wo auch immer der Browser es hinsetzt.
Probier es aus
Die Seite
Ordne die Seite (900px) mit grid-template-areas an: oben die Kopfzeile über die ganze Breite, darunter links die Filter (220px) und rechts der Katalog, unten die Fußzeile über die ganze Breite. Der gap ist 16px.
Konsole
Lösung
index.html<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Katalog</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="page">
<header>
<a href="/">Der Shop</a>
</header>
<aside class="filters">
<h2>Filter</h2>
<label><input type="checkbox"> Nur Verfügbares</label>
<label><input type="checkbox"> Bis 5 €</label>
</aside>
<main>
<h1>Katalog</h1>
<p>Weißbrot, Vollkornbrot, Frischmilch, Joghurt, Weißkäse, Äpfel</p>
</main>
<footer>Lieferung in 2 Werktagen</footer>
</div>
</body>
</html>
style.css* { box-sizing: border-box; }
body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; }
h1 { margin: 0 0 12px; font-size: 1.4rem; }
.page {
max-width: 900px;
display: grid;
grid-template-columns: 220px 1fr;
grid-template-areas:
"header header"
"filters main"
"footer footer";
gap: 16px;
}
header { grid-area: header; }
.filters { grid-area: filters; }
main { grid-area: main; }
footer { grid-area: footer; }
header, footer { padding: 12px 16px; background: #f3efe6; }
.filters { padding: 12px; border: 1px solid #ddd; border-radius: 8px; }
.filters h2 { margin: 0 0 8px; font-size: 1.1rem; }
.filters label { display: block; }
Zwei Spalten, 220px 1fr, und drei Zeilen von Bereichen: "header header", "filters main", "footer footer". Jedes der vier Elemente bekommt ein grid-area mit seinem Namen. Der Katalog wird 900 − 220 − 16 = 664px breit. Ein Element ohne grid-area liegt in keinem Bereich: Der Browser setzt es selbst in die erste freie Zelle. Die Kopfzeile ohne grid-area landet oben links und ist 220px breit.
Probier es aus
Finde den Fehler
Die Seite ist auseinandergefallen: Kopfzeile, Filter, Katalog und Fußzeile liegen übereinander. In der Konsole steht kein Fehler. Welche Zeile ist die Ursache?
Wähle die Zeile mit dem Fehler.
Zeile 7 hat eine Zelle, und es gibt zwei Spalten. Ein solcher Wert ist ungültig, und der Browser überspringt das ganze grid-template-areas, nicht nur diese Zeile. Dann gibt es keine Bereiche namens header, filters, main und footer, und die vier Elemente mit grid-area landen an ein und derselben Stelle außerhalb der Spalten, übereinander. Richtig wäre "footer footer". In den Entwicklertools (F12) ist die Eigenschaft durchgestrichen.
Genauer, für Neugierige
Implizite Zeilen. grid-template-columns und grid-template-rows beschreiben das explizite Grid. Gibt es mehr Items, legt der Browser selbst Zeilen an. Sie sind implizit und auto, so hoch wie der Inhalt. Ihre Höhe setzt man mit grid-auto-rows.
auto-fill und auto-fit. Der Unterschied zeigt sich, wenn es weniger Items als Spalten gibt. In einem 960px breiten Katalog mit zwei Produkten behält auto-fill die vier Spalten, und die Produkte sind je 228px breit. auto-fit lässt die leeren Spalten weg, und die zwei Produkte werden je 472px breit.
fr und der Inhalt. 1fr ist minmax(auto, 1fr): Die Spalte wird nicht schmaler als das längste Wort darin. Zieht ein langes Wort die Spalte auseinander, hält minmax(0, 1fr) sie auf.
Grid oder Flex. Flexbox ist für eine Achse: eine Kopfzeile, eine Reihe von Buttons, eine Zeile im Warenkorb. Grid ist für zwei: ein Katalog, eine Seite, ein Dashboard. Oft gehören sie zusammen: Die Seite ist ein Grid, und die Kopfzeile darin ist Flex.
Mehr. grid-auto-flow: dense füllt leere Zellen mit späteren Items, bringt aber die Reihenfolge auf dem Bildschirm gegenüber dem Baum durcheinander. subgrid lässt ein verschachteltes Grid die Spalten seines Elternelements nutzen.
Übung
Das Produkt der Woche
Mach den Katalog (900px) zu einem Grid: Spalten von mindestens 200px, so viele, wie passen, gap 16px. Das Produkt der Woche steht an erster Stelle und nimmt zwei Spalten. Alle Zeilen sind 160px hoch, auch die, die der Browser selbst anlegt. Die Prüfung erwartet 4 Spalten von 213px, das Produkt der Woche 442px breit und das letzte Produkt 160px hoch.
Konsole
Lösung
index.html<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Katalog</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Katalog</h1>
<div class="catalog">
<article class="card featured">
<p class="badge">Produkt der Woche</p>
<h2>Kaschkawal</h2>
<p>9,50 €</p>
</article>
<article class="card">
<img src="img/bread.svg" alt="" width="48" height="48">
<h2>Weißbrot</h2>
<p>1,20 €</p>
</article>
<article class="card">
<img src="img/bread.svg" alt="" width="48" height="48">
<h2>Vollkornbrot</h2>
<p>1,90 €</p>
</article>
<article class="card">
<img src="img/milk.svg" alt="" width="48" height="48">
<h2>Frischmilch</h2>
<p>1,60 €</p>
</article>
<article class="card">
<img src="img/milk.svg" alt="" width="48" height="48">
<h2>Joghurt</h2>
<p>1,10 €</p>
</article>
<article class="card">
<img src="img/cheese.svg" alt="" width="48" height="48">
<h2>Weißkäse</h2>
<p>7,90 €</p>
</article>
<article class="card">
<img src="img/apples.svg" alt="" width="48" height="48">
<h2>Äpfel</h2>
<p>2,10 €</p>
</article>
</div>
</body>
</html>
style.css* { box-sizing: border-box; }
body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; }
h1 { margin: 0 0 12px; font-size: 1.4rem; }
.catalog {
max-width: 900px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-rows: 160px;
gap: 16px;
}
.card {
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
}
.card h2 { margin: 8px 0 4px; font-size: 1rem; }
.card p { margin: 0; }
.featured {
grid-column: span 2;
background: #fff4e0;
}
.badge { color: #c2531a; font-weight: 700; }
.catalog { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); grid-auto-rows: 160px; gap: 16px;}.featured { grid-column: span 2; }Vier Spalten von (900 − 48) : 4 = 213px. Das Produkt der Woche ist 2 × 213 + 16 = 442px breit, weil es auch den gap zwischen seinen zwei Spalten einnimmt. Es und sechs Produkte brauchen acht Zellen: zwei Zeilen. grid-template-rows: 160px gibt nur der ersten Zeile 160px, und die zweite ist implizit und so hoch wie ihr Inhalt.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Das HTML-Dokument · Selektoren und die Kaskade · Das Box-Modell · Flexbox: Reihe und Spalte
- Geht weiter in: Responsive Design · Das Schaufenster des Shops
- Aufgaben zum Thema: CSS: Wie die Seite aussieht