mitmario.dev

Margin Collapsing

CSS Im Browser 3 Min Lesezeit 3 BeispieleLektion 4 von 8

Du gibst dem einen Absatz 32 Pixel Abstand nach unten und dem nächsten 16 nach oben. Dazwischen liegen dann 48 Pixel. So würde man rechnen, und so ist es nicht.

Senkrechte Außenabstände benachbarter Elemente fallen zusammen. Es bleibt der größere der beiden übrig, hier also 32. Der Effekt heißt Margin Collapsing. Er steckt hinter vielen Abständen, die sich nicht so verhalten, wie man ausgerechnet hat.

Zwei Nachbarn, ein Abstand

32 und 16 ergeben 32
p {
  margin: 0;
  padding: 8px;
  background-color: #dbeafe;
}

.oben {
  margin-bottom: 32px;
}

.unten {
  margin-top: 16px;
}

Gibst du dem unteren Absatz 64px nach oben, wird der Abstand 64 und nicht 96. Stehen beide auf 32, bleiben 32.

Das passiert nur senkrecht. Waagerecht addieren sich Außenabstände ganz normal. Zwei Elemente nebeneinander mit je 16px Abstand haben 32px zwischen sich.

Und es passiert nur bei Außenabständen. Innenabstände fallen nie zusammen, und Rahmen erst recht nicht.

Der Abstand bricht nach außen aus

Der zweite Fall ist der unangenehmere, weil er nicht wie ein Abstandsproblem aussieht.

Der Abstand bricht nach außen aus
/* Die gelbe Hülle ist nur da, damit du siehst,
   wohin der Abstand des Absatzes gewandert ist.
   Das display: flow-root hält ihn bei ihr fest.
   Ohne die Zeile bräche er auch aus der Hülle aus,
   und dann wäre hier gar nichts Gelbes zu sehen. */
.huelle {
  display: flow-root;
  background-color: #fef9c3;
}

.box {
  background-color: #dbeafe;
}

p {
  margin-top: 32px;
  margin-bottom: 0;
}

Der Absatz steht in einem blauen Kasten und hat 32px Abstand nach oben. Du siehst den Abstand aber über dem blauen Kasten, im Gelb der Hülle. Der Kasten selbst ist um dieselben 32 Pixel nach unten gerutscht, und oben in ihm ist kein Blau mehr übrig.

Auf einem breiten Bildschirm musst du das nicht glauben, du kannst es ansehen. Im Inspector steht am Absatz im Auskunftsstreifen margin: 32px 0px 0px. Der Abstand gehört also dem Absatz. Das orange Band dafür liegt aber nicht im blauen Kasten. Es fängt ganz oben an der gelben Hülle an und hört da auf, wo der blaue Kasten beginnt. Der Abstand ist ausgewandert, und das Band zeigt, wohin.

Der Grund ist derselbe wie eben. Die obere Kante des Absatzes und die obere Kante seines Elternteils berühren sich, also fallen die beiden Abstände zusammen. Der Elternteil hat selbst keinen, und so wandert der des Kindes nach außen.

Bei der Hülle hört das übrigens nur auf, weil sie im Beispiel ein display: flow-root trägt. Ohne das bräche der Abstand genauso durch sie hindurch weiter nach oben, denn dieselbe Bedingung gilt auf jeder Ebene. Was ihn aufhält, kommt gleich im nächsten Beispiel.

Im echten Projekt sitzt dann eine Karte plötzlich weiter unten als gedacht, und du findest im Stylesheet an der Karte kein einziges margin-top, so lange du auch suchst.

Was das Ausbrechen aufhält

Gegen das Ausbrechen gibt es vier Wege. Bei allen kommt etwas zwischen die Kante des Elternteils und die des ersten Kindes.

Der erste ist ein Innenabstand oder Rahmen am Elternteil. Ein einziges Pixel reicht schon.

Der zweite ist overflow: auto, hidden oder scroll am Elternteil. Damit wird er ein eigener Formatierungskontext (formatting context). Das ist ein abgeschlossener Bereich, aus dem kein Abstand seiner Kinder hinausdringt.

Der dritte ist display: flow-root am Elternteil. Das macht dasselbe wie overflow, aber ohne Nebenwirkungen, und heißt auch danach.

Der vierte ist ein Flex- oder Grid-Container. Darin gibt es kein Margin Collapsing mehr. Ab den Abschnitten 9 und 10 verschwindet das ganze Thema deshalb aus deinem Alltag.

Zwischen zwei Nachbarn hilft von den vieren nur der letzte. Innenabstand, Rahmen, overflow und flow-root trennen Elternteil und Kind. Zwei Absätze untereinander im selben Elternteil fallen trotzdem zusammen.

Was das Ausbrechen aufhält
/* Wie im Beispiel darüber: ohne flow-root bräche der
   Abstand des ersten Absatzes aus der Hülle aus, und
   der obere Kasten stünde ohne Gelb da. */
.huelle {
  display: flow-root;
  background-color: #fef9c3;
}

.box {
  background-color: #dbeafe;
  margin-bottom: 16px;
}

.box p {
  margin: 24px 0;
}

/* Ein Innenabstand dazwischen: die Kanten berühren
   sich nicht mehr, also fällt nichts zusammen. */
.mit-padding {
  padding: 1px 0;
}

/* Ein eigener Formatierungskontext: derselbe Effekt,
   und das ist der sauberere Weg. */
.mit-overflow {
  overflow: auto;
}

Warum das gut gemeint ist

Das Ganze wirkt wie ein Fehler, ist aber Absicht. Mit dem richtigen Beispiel leuchtet es sofort ein.

Ein langer Fließtext besteht aus Überschriften und Absätzen. Jede Überschrift hätte gern Luft nach oben, jeder Absatz Luft nach unten. Wenn sich diese Abstände addierten, wären die Lücken zwischen den Blöcken doppelt so groß wie gedacht. Dann müsstest du an jedem Element abzählen, wer gerade daneben steht.

So gilt einfach der größere Wunsch. Für Fließtext passt das. Bei Komponenten, die du selbst zusammenbaust, steht es meistens im Weg. Deshalb setze ich dort Abstände nur in eine Richtung, meistens nach unten. Ab Lektion 9.5 übernimmt das ohnehin gap, und dann stellt sich das Problem gar nicht erst.

In der Aufgabe sollen zwischen zwei Absätzen wirklich 48 Pixel liegen, gemessen und nicht gerechnet.

Zum Mitnehmen

Senkrechte Außenabstände fallen zusammen, zwischen Nachbarn und zwischen Elternteil und erstem Kind. Ein Innenabstand, ein Rahmen oder flow-root am Elternteil hält den Abstand des Kindes drinnen.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

Was in dieser Lektion steckt

  • Artikel mit 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe im Editor, direkt im Browser geprüft

    Öffnet sich mit dem Basis Konto.