mitmario.dev

position: absolute und der Bezugsrahmen

CSS Im Browser 5 Min Lesezeit 4 BeispieleLektion 3 von 8

Ein Badge bekommt top: 8px; right: 8px, damit es in die Ecke einer Karte rutscht, und landet in der Ecke der Seite. position: absolute nimmt ein Element aus dem Fluss und richtet es an seinem Bezugsrahmen aus. Um diesen Bezugsrahmen geht es hier vor allem.

Der Platz verschwindet

Aus dem Fluss genommen
.reihe {
  position: relative;
  padding: 16px;
  font-family: system-ui, sans-serif;
  background-color: #f4f4f5;
}

.kasten {
  padding: 16px;
  margin-bottom: 8px;
  background-color: #dbeafe;
}

/* Der mittlere Kasten ist aus dem Fluss. Sein Platz
   verschwindet, eins und drei rücken zusammen, und er
   selbst liegt jetzt über ihnen. */
.mitte {
  position: absolute;
  top: 16px;
  right: 16px;
  background-color: #fde68a;
}

Anders als bei relative bleibt hier nichts reserviert. Das Element ist aus dem Fluss, und sein Platz verschwindet. Die Nachbarn rücken zusammen, als hätte es das Element nie gegeben.

Im Beispiel stehen die Kästen eins und drei direkt untereinander, obwohl im Markup der Kasten zwei zwischen ihnen steht.

Ein absolut positioniertes Element schiebt nichts weg und wird von nichts weggeschoben. Es liegt über allem, was nicht selbst positioniert ist. Wird der Inhalt darunter länger, wandert es nicht mit. Für ein Badge in einer Kartenecke ist das richtig. Für den Hauptinhalt einer Seite ist es eine Katastrophe, die erst auffällt, wenn jemand einen längeren Text einträgt.

Der Bezugsrahmen

Die Angaben top, right, bottom und left beziehen sich auf den nächsten Vorfahren mit einer Position ungleich static. Er ist der Bezugsrahmen (containing block) des Elements, und das muss nicht der Elternteil sein.

static ist der Standardwert, den jedes Element hat, solange niemand etwas anderes hinschreibt. Setzt also keiner der Vorfahren eine Position, bleibt als Bezug der Anfangs-Bezugsrahmen (initial containing block) übrig. Das ist ein Rechteck in der Größe des Viewports am Anfang der Seite. Neben einer Position macht auch ein transform am Vorfahren einen Rahmen auf. Das kommt in der nächsten Lektion bei fixed noch einmal.

Ohne Bezugsrahmen landet das Badge woanders
body {
  font-family: system-ui, sans-serif;
}

.projekt {
  max-width: 340px;
  margin: 32px;
  padding: 24px;
  border: 1px solid #d4d4d8;
  border-radius: 12px;
}

.badge {
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 4px 8px;
  border-radius: 999px;
  font-size: 14px;
  color: #ffffff;
  background-color: #2563eb;
}

.projekt h3 {
  margin: 0 0 8px 0;
}

.projekt p {
  margin: 0;
}

/* Diese eine Zeile ist der ganze Unterschied. */
.mit {
  position: relative;
}

Ein Element mit position: relative wird zum Bezugsrahmen für alle absolut positionierten Nachfahren.

Im Beispiel siehst du beide Fälle. Zweimal dasselbe Markup, zweimal dasselbe Badge mit top: 8px; right: 8px. Bei der oberen Karte fehlt der Bezugsrahmen, und das Badge landet in der Ecke der Seite. Bei der unteren steht eine einzige zusätzliche Zeile, und es sitzt dort, wo es hingehört.

Der Unterschied ist ein Wort, und im Inspector siehst du es. An der oberen Karte steht im Reiter „Werte“ position: static, an der unteren position: relative. An den beiden Badges steht dagegen beide Male position: absolute. Auch im Reiter „Regeln“ steht bei beiden dieselbe Regel mit top: 8px und right: 8px. Das Badge macht also nichts anders. Verändert hat sich nur, worauf es sich bezieht.

In der zusätzlichen Zeile steht kein top, kein left, nichts, denn die Karte selbst soll sich nicht bewegen. Im Reiter „Regeln“ steht an der unteren Karte deshalb eine Regel mit einer einzigen Deklaration. position: relative ohne weitere Angaben verschiebt nichts und macht nur den Rahmen auf. Eine einsame Zeile davon heißt fast immer, dass ein Kind absolut positioniert werden soll, auch wenn sie beim ersten Lesen wie ein Rest von etwas Altem aussieht.

Das obere Badge bleibt in der Ecke der Seite nicht kleben. Scrollt die Seite, scrollt es mit ihr weg, denn sein Bezug liegt fest am Anfang der Seite und nicht im Viewport. Ein bottom: 0 landet aus demselben Grund nicht am Ende der Seite, sondern am unteren Rand des ersten Bildschirms. In Lektion 8.4 geht es mit fixed an dieser Stelle weiter.

Die Suche geht dabei den Baum nach oben, nicht nach unten. Es zählt der nächste positionierte Vorfahre, nicht der äußerste. Soll ein Overlay über einem Bild liegen, gehört position: relative an den Bildkasten und nicht an die ganze Karte.

Aufspannen oder anheften

Aufspannen oder anheften
.karte {
  position: relative;
  height: 200px;
  padding: 24px;
  font-family: system-ui, sans-serif;
  border: 1px solid #d4d4d8;
  background-color: #ffffff;
}

/* Zwei gegenüberliegende Seiten auf 0: das Element
   wird über den ganzen Bezugsrahmen gezogen. */
.overlay {
  position: absolute;
  inset: 0;
  padding: 16px;
  color: #ffffff;
  background-color: rgb(37 99 235 / 35%);
}

/* Nur zwei Seiten: das Element bleibt so groß wie
   sein Inhalt und hängt sich an die Ecke. */
.badge {
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 4px 8px;
  border-radius: 999px;
  font-size: 14px;
  color: #ffffff;
  background-color: #2563eb;
}

Aus den vier Angaben ergeben sich zwei Muster, die du immer wieder brauchst.

  • Zwei gegenüberliegende Seiten ziehen das Element auseinander. inset: 0 setzt alle vier auf null und spannt damit über den gesamten Bezugsrahmen. Das ist das Overlay, der Farbschleier über einem Bild, die Klickfläche über einer Karte.
  • Zwei benachbarte Seiten heften das Element an eine Ecke. top und right ergeben oben rechts, und das Element bleibt so groß wie sein Inhalt. Das ist das Badge, das Schließkreuz, der Zähler an einem Symbol.

Die beiden Muster stehen im Streifen als zwei Zahlenpaare nebeneinander. Bei der Breite, mit der die Spalte startet, misst das Overlay 362 × 248 und füllt damit den Rahmen bis auf den Rand aus. Das Badge daneben misst rund 44 × 24, also gerade so viel, wie das Wort braucht. Im Reiter „Regeln“ steht am einen inset: 0, am anderen top und right. Derselbe Wert bei position, zwei Ergebnisse.

Das dritte Muster ist die Mitte, und dafür gibt es zwei Wege. Beim älteren kombinierst du inset: 0 mit margin: auto. Das Element braucht dann eine Größe, sonst spannt inset: 0 es auf. Der jüngere kommt aus Flexbox und Grid und braucht keine. Deshalb kommt er in Abschnitt 11 noch einmal ausführlich vor.

So breit wie der Inhalt

Ohne Breite, und trotzdem mittig
.feld {
  position: relative;
  height: 120px;
  margin-bottom: 16px;
  font-family: system-ui, sans-serif;
  background-color: #f4f4f5;
}

.schild {
  padding: 8px 16px;
  color: #ffffff;
  background-color: #2563eb;
}

/* Ohne Breitenangabe schrumpft ein absolutes Element
   auf seinen Inhalt, statt die Breite des Elternteils
   zu nehmen. Ein Block im Fluss macht das nicht. */
.schrumpf {
  position: absolute;
  top: 12px;
  left: 12px;
}

/* Alle vier Seiten auf 0 und margin: auto verteilt den
   übrigen Platz gleichmäßig. Das zentriert in beide
   Richtungen, verlangt dafür aber Breite und Höhe.
   Ohne sie löst margin: auto zu 0 auf, und das Element
   füllt den ganzen Rahmen. */
.mittig {
  position: absolute;
  inset: 0;
  width: 220px;
  height: 40px;
  margin: auto;
}

Ein absolut positioniertes Element ohne Breitenangabe ist so breit wie sein Inhalt, nicht so breit wie sein Elternteil. Ein Block im Fluss verhält sich andersherum, und darüber stolpert man gleich nach dem Bezugsrahmen.

Ein absolut positioniertes Etikett mit langem Text wächst deshalb in die Breite, bis es an den Rand seines Bezugsrahmens stößt. Erst dort bricht es um. Für ein Etikett ist das meistens zu breit, und dann gehört eine max-width dazu.

Wonach du zuerst suchst

Sitzt ein absolut positioniertes Element nicht dort, wo es hin soll, gibt es meist zwei Ursachen. Ich prüfe sie in dieser Reihenfolge.

  1. Gibt es überhaupt einen Bezugsrahmen? Irgendwo darüber im Elementbaum muss eine Position ungleich static stehen. Steht nirgends eine, ist das die Antwort.
  2. Ist es der richtige? Manchmal gibt es einen, aber er sitzt zu weit oben, weil jemand vor einem Jahr position: relative an einen Container geschrieben hat. Dann sitzt das Badge in der Ecke dieses Containers, und das kann drei Ebenen zu weit außen sein.

Mit dem Inspector geht beides schnell. Du zeigst nacheinander auf die Vorfahren des Elements und liest im Reiter „Werte“ die Zeile position. Der erste, an dem dort nicht static steht, ist der Rahmen.

Einen Vorfahren triffst du dort, wo ihn seine Kinder nicht verdecken, meistens in seinem Innenabstand. Eine Hülle ohne eigene Höhe lässt sich gar nicht antippen. Ihre Regel steht trotzdem im Reiter „Regeln“ des Kindes, blass und mit dem Vermerk „Selektor trifft nicht“.

In der Aufgabe liegen Badge und dunkle Fläche der Projektkarte noch im Fluss.

Zum Mitnehmen

Ohne position: relative an der Karte sucht der Browser weiter außen nach einem Bezug und nimmt am Ende die Seite selbst. inset: 0 macht aus dem Element eine Fläche über dem ganzen Rahmen, top zusammen mit right ein Etikett in der Ecke.

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 4 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe im Editor, direkt im Browser geprüft

    Öffnet sich mit dem Basis Konto.