Flexbox: Reihe und Spalte
Kurz gesagt: Flexbox ordnet Items entlang der Hauptachse in einer Reihe oder Spalte an, richtet sie auf der Querachse aus und verteilt den freien Platz mit flex-grow.
Problem
Die Kopfzeile in drei Zeilen
Die neue Kopfzeile des Shops soll eine Zeile sein: links das Logo, daneben das Menü, rechts der Warenkorb. Am Computer sind es drei Zeilen untereinander.
Konsole
Das CSS hat keinen Fehler. <nav> ist ein Blockelement, und Blockelemente stehen untereinander, jedes in einer ganzen Zeile. Links sind Inline-Elemente und stehen mit dem Text um sie herum in einer Zeile. Also steht das Logo in der ersten Zeile, das Menü in der zweiten und der Warenkorb in der dritten.
Die Idee
Container und Items
Die Kinder der Kopfzeile brauchen eine andere Art der Anordnung. Sie heißt display: flex am Elternelement:
header { display: flex;}- Das Element mit
display: flexist ein Flex-Container. Seine Kinder (nur die direkten) sind Flex-Items. - Die Items stehen entlang der Hauptachse nebeneinander. Standardmäßig läuft sie von links nach rechts.
flex-direction: columndreht sie von oben nach unten. - Im rechten Winkel dazu liegt die Querachse.
Die Anordnung wird am Container festgelegt, nicht an den Kindern:
justify-contentordnet entlang der Hauptachse an:flex-start(am Anfang),center,flex-end,space-between(das erste am Anfang, das letzte am Ende, der Platz dazwischen).align-itemsrichtet entlang der Querachse aus:stretch(der Standard, alle so hoch wie die Zeile),center,flex-start,flex-end.gapsetzt gleich viel Platz zwischen die Items, nichts vor das erste und nichts hinter das letzte.
Ein Element kann gleichzeitig Flex-Item und Flex-Container sein. nav { display: flex; gap: 12px; } ordnet die Links im Menü an, und das Menü selbst ist ein Item der Kopfzeile.
Probier es aus
Logo links, Warenkorb rechts
Bring die Kopfzeile in eine Zeile: das Logo am linken Rand, den Warenkorb am rechten, das Menü dazwischen. Nimm justify-content. Die Prüfung misst, in wie vielen Zeilen die Kinder von <header> stehen.
Konsole
Lösung
index.html<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Der Shop</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<a class="logo" href="/">Der Shop</a>
<nav>
<a href="/bread">Brot</a>
<a href="/milk">Milch</a>
<a href="/fruit">Obst</a>
</nav>
<a class="cart" href="/cart">Warenkorb (2)</a>
</header>
</body>
</html>
style.css* { box-sizing: border-box; }
body { margin: 0; font-family: system-ui, sans-serif; }
header {
padding: 12px 16px;
background: #f3efe6;
display: flex;
justify-content: space-between;
align-items: center;
}
a { color: #1f2430; }
.logo { font-weight: 700; }
nav a { margin-right: 12px; }
display: flex an header stellt die drei Kinder in eine Reihe. justify-content: space-between schickt das erste an den Anfang, das letzte ans Ende, und der freie Platz kommt dazwischen. align-items: center zentriert sie senkrecht. Hier sind die drei gleich hoch, aber wenn der Warenkorb ein Button mit padding wird, sieht man den Unterschied.
Probier es aus
Finde den Fehler
Die Kopfzeile steht in einer Reihe, aber der Warenkorb rutscht nicht nach rechts. Warum?
Wähle die Zeile mit dem Fehler.
justify-content ordnet die Kinder eines Containers an, deshalb schreibt man es an den Container. In Zeile 6 steht es an .cart: Das hat keine Flex-Kinder, und die Eigenschaft bewirkt nichts. Richtig wäre justify-content: space-between in header oder margin-left: auto an .cart, worum es in der nächsten Karte geht.
Die Idee
Der freie Platz
In der Kopfzeile gibt es auch ein Suchfeld. Es soll den ganzen Platz zwischen Logo und Warenkorb nehmen, egal wie breit der Bildschirm ist. Dabei helfen drei Eigenschaften des Flex-Items:
flex-basisist die Basis: die Breite des Items, bevor irgendetwas verteilt wird. Standardmäßig ist sieauto: seinewidthoder so breit wie sein Inhalt.flex-growsagt, wie viele Teile des freien Platzes sich das Item nimmt. Standardmäßig ist es 0: Es wächst nicht.flex-shrinksagt, wie stark es schrumpft, wenn der Platz nicht reicht. Standardmäßig ist es 1.
Der Browser rechnet so:
- Er setzt jedes Item mit seiner Basis.
- Der freie Platz ist die Breite des Containers minus die Basen und die gaps.
- Er verteilt den freien Platz nach
flex-grow, und jedes Item wächst um seinen Anteil.
Die Eigenschaft flex setzt alle drei auf einmal. flex: 1 ist flex-grow: 1, flex-shrink: 1 und flex-basis: 0%: Das Item wächst, und seine Basis ist null. Deshalb werden drei Items mit flex: 1 gleich breit, egal welcher Text darin steht.
Sieh es dir an
So wird der Platz verteilt
Ein 600px breiter Balken mit drei Items. Achte auf die Summe oben und den grünen freien Platz.
.bar { display: flex; width: 600px; gap: 20px;}.logo { flex-basis: 120px; }.search { flex-basis: 100px; flex-grow: 2; }.cart { flex-basis: 100px; flex-grow: 1; }Schritt für Schritt, in Worten
- .bar ist ein Flex-Container, 600px breit. Seine Kinder sind Flex-Items: Sie stehen entlang der Hauptachse in einer Reihe, von links nach rechts.
- Zuerst bekommt jedes Item seine flex-basis: 120, 100 und 100px. Das ist seine Breite, bevor irgendetwas verteilt wird.
- Zwischen drei Items liegen zwei gap von je 20px. Belegt sind 120 + 100 + 100 + 2 × 20 = 360px.
- Was übrig bleibt, ist der freie Platz: 600 − 360 = 240px.
- flex-grow sagt, wie viele Teile sich jedes Item nimmt: logo 0, search 2, cart 1, zusammen drei. Ein Teil sind 240 : 3 = 80px.
- Jedes Item wächst um seine Teile: search um 160px, cart um 80px, logo bleibt, wie es ist. Die Breiten sind 120, 260 und 180px.
- 120 + 20 + 260 + 20 + 180 = 600: Die Reihe ist voll. Der Browser gibt genau diese Breiten. search ist nicht doppelt so breit wie cart: Nur der freie Platz wird im Verhältnis 2 : 1 verteilt.
search hat flex-grow: 2 und cart hat 1, aber search ist nicht doppelt so breit: 260 gegen 180. Verteilt wird nur der freie Platz, die Basen bleiben. Wenn du gleich breite Spalten willst, muss die Basis also null sein: flex: 1.
Probier es aus
Die Suche nimmt den Rest
Das Logo ist 120px breit, der Warenkorb 110px, und die Kopfzeile ist bis zu 640px breit. Lass das Suchfeld den ganzen Platz dazwischen nehmen. Die Prüfung misst seine Breite in Pixeln: 640 − 2 × 16 (padding) − 120 − 110 − 2 × 16 (gap) = 346px.
Konsole
Lösung
index.html<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Der Shop</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<a class="logo" href="/">Der Shop</a>
<input type="search" aria-label="Suchen" placeholder="Brot, Milch…">
<a class="cart" href="/cart">Warenkorb (2)</a>
</header>
</body>
</html>
style.css* { box-sizing: border-box; }
body { margin: 0; font-family: system-ui, sans-serif; }
header {
max-width: 640px;
padding: 12px 16px;
display: flex;
align-items: center;
gap: 16px;
background: #f3efe6;
}
a { color: #1f2430; }
.logo { width: 120px; font-weight: 700; }
.cart { width: 110px; text-align: right; }
input {
flex: 1;
padding: 6px 8px;
}
flex: 1 am Feld: Es ist das einzige Item mit flex-grow, also nimmt es den ganzen freien Platz. Logo und Warenkorb behalten ihre Breite. width: 100% funktioniert nicht: Das Feld verlangt die ganze Kopfzeile, der Platz reicht nicht, und flex-shrink schrumpft Logo und Warenkorb gleich mit.
Probier es aus
Eines nach rechts
Manchmal willst du Logo und Menü links und nur den Warenkorb rechts. space-between geht nicht: Das Menü würde in die Mitte rutschen. Dafür gibt es eine Eigenschaft des Items selbst: Ein margin mit dem Wert auto nimmt den ganzen freien Platz auf seiner Seite und schiebt das Item weg.
header { display: flex; gap: 16px; }
.cart { margin-left: ; }margin-left: auto: Der linke Außenabstand des Warenkorbs frisst den freien Platz, und der Warenkorb steht am rechten Rand. Logo und Menü bleiben links nebeneinander. In einem Flex-Container werden auto-Außenabstände vor justify-content berechnet.
Probier es aus
Eine neue Zeile, wenn es nicht passt
Standardmäßig hält ein Flex-Container alle Items in einer Zeile (flex-wrap: nowrap). Passen sie nicht, schrumpft er sie, und wenn das nicht mehr geht, ragt die Zeile aus dem Bildschirm. Mit flex-wrap: wrap rutschen die Items, die nicht passen, in eine neue Zeile.
Auf einem Handy (360px) sind die vier Produkte in eine Zeile gequetscht, und die Seite scrollt seitwärts. Setze sie zu zweit in eine Zeile, jedes mindestens 150px breit, und die zwei in einer Zeile gleich breit. Die Prüfung zählt die Zeilen und Spalten und misst die Breite der Karten.
Konsole
Lösung
index.html<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Angebote</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>Angebote</h1>
<div class="products">
<article class="card">
<h2>Weißbrot</h2>
<p>1,20 €</p>
</article>
<article class="card">
<h2>Frischmilch</h2>
<p>1,60 €</p>
</article>
<article class="card">
<h2>Weißkäse</h2>
<p>7,90 €</p>
</article>
<article class="card">
<h2>Äpfel</h2>
<p>2,10 €</p>
</article>
</div>
</main>
</body>
</html>
style.css* { box-sizing: border-box; }
body { margin: 0; font-family: system-ui, sans-serif; }
main { padding: 12px; }
h1 { margin: 0 0 12px; font-size: 1.4rem; }
.products {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.card {
flex: 1 1 150px;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
}
.card h2 { margin: 0 0 4px; font-size: 1rem; }
.card p { margin: 0; }
flex-wrap: wrap am Container erlaubt eine neue Zeile. flex: 1 1 150px an der Karte: Die Basis ist 150px, also passen zwei (150 + 12 + 150 = 312 ≤ 336) und drei nicht. Dann wird der freie Platz in der Zeile gleichmäßig verteilt: Jede wird (336 − 12) : 2 = 162px breit. flex-wrap: wrap allein macht Zeilen, aber jede Karte ist so breit wie ihr Text.
Die Idee
Der Name, der nicht schrumpft
Im Warenkorb steht der Name des Produkts neben dem Bild, und daneben stehen Menge und Preis. Der Name ist lang. Wir wollen ihn in einer Zeile mit „…“ abschneiden: white-space: nowrap, overflow: hidden und text-overflow: ellipsis. Der Block mit dem Namen hat flex: 1. Auf einem Handy ragt die Zeile trotzdem aus dem Bildschirm, und der Preis wird gequetscht:
Konsole
Schuld ist eine einzige Regel. Ein Flex-Item schrumpft nicht unter seinen schmalsten Inhalt: min-width ist standardmäßig auto, nicht 0. Für .info ist der schmalste Inhalt der ganze Name in einer Zeile. min-width: 0 am Flex-Item hebt diese Grenze auf. Dann schrumpft .info, und die Überschrift darin schneidet den Text ab.
Jemand hat min-width: 0 ergänzt, aber der Name wird immer noch nicht abgeschnitten. Welche Zeile ist falsch?
min-width: 0 in Zeile 4 steht an der Überschrift .name. Sie ist kein Flex-Item: Das Flex-Item ist .info, ihr Elternelement. Die Grenze liegt an .info, also gehört min-width: 0 dorthin.
Genauer, für Neugierige
Schrumpfen. Ergeben Basen und gaps zusammen mehr als der Container, ist der freie Platz negativ, und die Items schrumpfen. Der Anteil richtet sich nicht nur nach flex-shrink, sondern nach flex-shrink × Basis: Ein breites Item schrumpft stärker. Der Balken aus der Szene, aber nur 280px breit: 120 + 100 + 100 + 40 = 360, es fehlen 80px. Die Gewichte sind 120, 100 und 100. logo verliert 80 × 120 : 320 = 30px, die anderen je 25px: 90, 75 und 75px. Ein Item mit flex-shrink: 0 schrumpft nicht.
Die Werte von flex. flex: 1 ist 1 1 0%. flex: auto ist 1 1 auto: Es wächst, aber von der Breite seines Inhalts aus. flex: none ist 0 0 auto: Es wächst nicht und schrumpft nicht. flex: 0 0 64px ist ein Item von genau 64px.
Was nicht schrumpft. Bilder und Felder haben eine eigene Breite, und min-width: auto hindert sie am Schrumpfen. Deshalb wird im Warenkorb der Preis (<strong>) gequetscht und das Bild nicht.
Ein anderer Weg. Auch overflow: hidden am Flex-Item selbst hebt die Grenze auf: Ein Element, das seinen Inhalt abschneidet, darf bis auf 0 schrumpfen.
Ein Item anders. align-self richtet ein einzelnes Item auf der Querachse aus. order ändert seinen Platz auf dem Bildschirm, aber nicht im Baum: Tastatur und Screenreader folgen dem Baum. Darum verschieben wir mit order nichts, worauf Leute klicken.
Zeilen mit wrap. align-content ordnet die Zeilen selbst auf der Querachse an, wenn der Container höher ist als sie. display: inline-flex macht einen Container, der wie ein Wort im Text steht.
Übung
Eine Zeile im Warenkorb
Ordne eine Zeile des Warenkorbs auf einem Handy (360px) an: Bild, Name, Menge und Preis in einer Reihe, senkrecht zentriert, mit 12px dazwischen. Das Bild bleibt 64px, die Menge 56px, der Preis 72px. Der Name nimmt den Rest des Platzes (hier 108px), steht in einer Zeile und wird, wenn er lang ist, mit „…“ abgeschnitten. Die Seite scrollt nicht seitwärts.
Konsole
Lösung
index.html<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Warenkorb</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>Warenkorb</h1>
<div class="line">
<img class="thumb" src="img/bread.svg" alt="">
<div class="info">
<h2 class="name">Vollkornbrot mit Roggen und Samen, geschnitten, 500 g</h2>
<p class="unit">1,90 € / Stück</p>
</div>
<input class="qty" type="number" value="2" min="1" aria-label="Menge">
<strong class="price">3,80 €</strong>
</div>
</main>
</body>
</html>
style.css* { box-sizing: border-box; }
body { margin: 0; font-family: system-ui, sans-serif; }
h1 { margin: 0; padding: 12px; font-size: 1.4rem; }
.line {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
border-top: 1px solid #ddd;
}
.thumb { width: 64px; height: 64px; border-radius: 8px; }
.info {
flex: 1;
min-width: 0;
}
.name {
margin: 0 0 4px;
font-size: 1rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.unit { margin: 0; color: #6b7180; }
.qty { width: 56px; }
.price { width: 72px; text-align: right; }
.line { display: flex; align-items: center; gap: 12px;}.info { flex: 1; min-width: 0; }.name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis;}flex: 1 gibt .info die Basis 0 und den ganzen freien Platz: 336 − 64 − 56 − 72 − 36 = 108px. Mit flex-grow: 1 bleibt die Basis so lang wie der Name. Dann ist der Platz negativ, und flex-shrink quetscht den Preis. Ohne min-width: 0 schrumpft der Name nicht, und die Zeile ragt aus dem Bildschirm.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Das HTML-Dokument · Formulare · Selektoren und die Kaskade · Das Box-Modell
- Geht weiter in: Grid: Zeilen und Spalten · Responsive Design · Das Schaufenster des Shops
- Aufgaben zum Thema: CSS: Wie die Seite aussieht