CSS-Positionierung und Koordinatensysteme
Die meisten Tutorials sagen dir, was position: absolute tut. Dieser zeigt dir, wie es denkt -- als Koordinatensystem mit Ursprung, Achsen und Containing Block.
Live-Demo: Positionierungsmodi Umschalten
Waehle einen position-Wert und ziehe die Schieberegler. Die rote Box positioniert sich neu und die Koordinatenanzeige zeigt dir genau, wo ihr Ursprungspunkt liegt und wie top/left sich in Pixel-Verschiebungen uebersetzen. Das ist der schnellste Weg, den Unterschied zwischen relative und absolute zu verinnerlichen.
Positionierung als Koordinatensystem, Nicht als Layout-Regel
Hier ist der mentale Wechsel, der CSS-Positionierung zum Klick bringt: top, right, bottom und left sind Koordinaten, keine Margins. Wenn du top: 50px schreibst, platzierst du das Element 50 Pixel unter einem spezifischen Ursprungspunkt. Die Frage ist: Ursprung von was?
Jedes positionierte Element lebt innerhalb eines Containing Blocks -- der Box, die seinen Koordinatenursprung definiert. Stell es dir als den (0, 0)-Punkt fuer dieses Element vor. Der Containing Block ist unterschiedlich, je nachdem, welchen position-Wert du verwendest, und das ist die einzige verwirrendste Sache an CSS-Positionierung.
Bei position: absolute und position: fixed wird das Element vollstaendig aus dem normalen Dokumentfluss entfernt. Seine Koordinaten werden gegen den Containing Block gemessen, nicht gegen seine Geschwister oder Eltern im normalen Fluss. Deshalb kann ein absolut positioniertes Element anderen Inhalt ueberlappen -- es hat das Koordinatensystem des Dokumentflusses verlassen.
Die Fuenf position-Werte, Aus Koordinaten-Perspektive
| Wert | Koordinatenursprung (Containing Block) | Im Normalfluss? | Haeufige Verwendung |
static (Standard) | Kein Ursprung -- top/left werden ignoriert | Ja, folgt der Dokumentreihenfolge | Alles, was keine explizite Positionierung braucht |
relative | Die urspruengliche Position des Elements im Fluss | Ja, aber visuell verschoben (urspruenglicher Raum bleibt erhalten) | Feinabstimmung, Containing Block fuer Kinder erstellen |
absolute | Naechster positionierter Vorfahr (beliebiger nicht-static), oder der anfaengliche Containing Block | Nein -- aus dem Fluss entfernt, andere Elemente fuellen seinen Platz | Tooltips, Abzeichen, Overlays relativ zum Eltern-Element |
fixed | Der Viewport (Browser-Fenster), es sei denn ein Vorfahr hat transform/filter/perspective | Nein -- aus dem Fluss entfernt | Fixe Navigationsleisten, schwebende Buttons, Cookie-Banner |
sticky | Naechster scrollender Vorfahr; wechselt zwischen fluss-relativ und fixed beim Scrollen | Ja, bis die Scroll-Schwelle erreicht ist | Tabellen-Ueberschriften, beim Scrollen haftende Bereichsnavigation |
Die obige Tabelle ist die Referenz. Nun entpacken wir die drei Dinge, die die meisten Verwirrung in der Praxis verursachen.
Containing Block: Das Konzept, das die Meisten Tutorials Ueberspringen
Wenn du eine Sache von dieser Seite mitnimmst, dann diese: position: absolute ist nicht relativ zum Eltern-Element. Es ist relativ zum naechsten positionierten Vorfahr -- dem ersten Vorfahren beim Hinaufsteigen im Baum, der position auf etwas anderes als static gesetzt hat.
Das legt jeden beim ersten Mal herein. Du setzt position: absolute auf ein Kind, setzt top: 0; left: 0;, und das Element fliegt in die Ecke der Seite statt in die Ecke des Eltern-Elements. Der Grund: Das Eltern-Element hat position: static (Standard), also geht der Browser im Baum nach oben, sucht einen positionierten Vorfahr, findet keinen und verwendet den Viewport als Containing Block.
Die Loesung ist immer dieselbe: Fuege position: relative zum Eltern-Element hinzu. Das etabliert das Eltern-Element als Containing Block, und jetzt werden top/left des Kindes von der Padding-Kante des Eltern-Elements gemessen.
Faustregel: Wenn du position: absolute bei einem Kind verwendest, braucht das Eltern-Element fast sicher position: relative ohne Versatz. Dieses Muster -- relative Eltern, absolute Kind -- ist das Arbeitstier der Overlay-, Tooltip- und Abzeichen-Positionierung.
Stacking Contexts: Das Z-Achsen-Koordinatensystem
Positionierung handhabt X und Y. z-index handhabt Z -- aber es funktioniert nicht so, wie die meisten denken. z-index funktioniert nur innerhalb eines Stacking Contexts (Schichtkontexts), und ein Stacking Context ist nicht dasselbe wie das Dokument.
Stell dir einen Stacking Context als isolierte Ebene vor. Elemente darin koennen relativ zueinander gestapelt werden, aber die gesamte Ebene wird als Einheit gegen andere Ebenen positioniert. Also kann ein z-index: 9999 innerhalb eines Stacking Contexts immer noch hinter einem z-index: 1 in einem anderen, hoeheren Stacking Context liegen.
Was erstellt einen Stacking Context?
- Das Wurzel-Element (
<html>)
position: absolute oder position: relative mit z-index ungleich auto
position: fixed oder position: sticky (immer, unabhaengig vom z-index)
- Jedes Element mit
opacity kleiner als 1
- Jedes Element mit
transform, filter, perspective, backdrop-filter oder will-change
mix-blend-mode ungleich normal
isolation: isolate
Die praktische Konsequenz: Wenn dein Modal mit z-index: 9999 hinter einem Dropdown mit z-index: 10 erscheint, liegt das fast sicher daran, dass das Modal in einem Stacking Context ist (erstellt durch transform oder opacity bei einem Vorfahr), der selbst unter dem Stacking Context des Dropdowns liegt. Du kannst das nicht beheben, indem du den z-index des Modals hoeher setzt -- du musst das Modal aus dem Stacking Context des Vorfahren herausbewegen oder die Eigenschaft entfernen.
Viewport-Einheiten und Responsives Positionieren
Wenn du position: fixed oder position: absolute mit Prozent-Versaetzen verwendest, sind diese Prozente relativ zu den Dimensionen des Containing Blocks, nicht zur Groesse des Elements selbst. top: 50% bedeutet 50% der Hoehe des Containing Blocks.
Viewport-Einheiten ermoeglichen Positionierung relativ zum tatsaechlichen Browser-Fenster:
| Einheit | Relativ Zu | Anwendungsfall |
vw / vh | 1% der Viewport-Breite / -Hoehe | Vollbild-Overlays, fixe Seitenleisten |
dvw / dvh | 1% des dynamischen Viewports (beruecksichtigt mobile URL-Leiste) | Mobile-first fixe Positionierung -- vermeidet den iOS Safari URL-Leisten-Sprung |
svw / svh | 1% des kleinstmoeglichen Viewports | Elemente, die nie hinter Browser-UI versteckt werden duerfen |
lvw / lvh | 1% des groesstmoeglichen Viewports | Wenn du randlose Hintergrundbilder willst |
Die dvh / dvw / svh / lvh-Einheiten sind neuer und loesen das mobile Safari-Problem, bei dem 100vh den Bereich hinter der URL-Leiste einschliesst, was dazu fuehrt, dass Inhalt abgeschnitten wird. Verwende 100dvh statt 100vh fuer Elemente, die den sichtbaren Viewport fuellen sollen.
Wirklich Funktionierende Praxis-Muster
Zentriertes Modal-Overlay
Das Muster fuer ein zentriertes Modal, das ueberall funktioniert. Der Schluessel ist die Kombination von position: fixed mit inset: 0 (Kurzform fuer alle vier Seiten) und place-items: center:
.modal-backdrop {
position: fixed;
inset: 0; /* top:0; right:0; bottom:0; left:0 */
background: rgba(0,0,0,.5);
display: grid;
place-items: center; /* centers the modal */
z-index: 1000;
}
.modal {
background: #fff;
border-radius: 12px;
padding: 2rem;
max-width: 90vw;
max-height: 90vh;
overflow: auto;
}
Sticky Tabellen-Kopfzeile
position: sticky auf einer Tabellen-Kopfzeile ist der sauberste Weg, Spalten-Beschriftungen beim Scrollen sichtbar zu halten. Der Trick: top: 0 definiert, wann sie haftet, und der Scroll-Container muss der Scroll-Parent der Tabelle sein:
.table-scroll {
max-height: 400px;
overflow: auto;
}
.table-scroll thead th {
position: sticky;
top: 0;
background: #fff; /* must be opaque */
z-index: 1;
}
Tooltip beim Hovern
Das relative-Eltern-absolute-Kind-Muster fuer Tooltips. Der Tooltip ist standardmaessig versteckt und wird beim Hovern eingeblendet:
.tooltip-trigger {
position: relative; /* containing block for tooltip */
display: inline-block;
}
.tooltip {
position: absolute;
bottom: 100%; /* sits above the trigger */
left: 50%;
transform: translateX(-50%); /* horizontal center */
margin-bottom: 8px;
padding: .5rem .75rem;
background: #1a1a1a;
color: #fff;
border-radius: 6px;
font-size: .85rem;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity .2s;
}
.tooltip-trigger:hover .tooltip {
opacity: 1;
}
Entscheidungsrahmen: Welche position Verwenden
Hoer auf, dir zu merken, was jeder Wert tut. Verwende stattdessen diesen Entscheidungsfluss:
Muss das Element anderen Inhalt ueberlappen oder aus dem normalen Fluss ausbrechen?
Nein → Verwende static (Standard). Nutze Flexbox oder Grid fuer Layout.
Ja, temporaer bei Interaktion (Dropdown, Tooltip) → relative beim Eltern-Element, absolute beim Kind.
Soll das Element sichtbar bleiben, wenn die Seite scrollt?
Ja, immer → fixed (pruefe auf transform bei Vorfahren!)
Ja, aber erst nach dem Scrollen ueber einen Punkt → sticky.
Willst du ein Element nur leicht aus seiner normalen Position verschieben?
Ja → relative mit kleinen top/left-Versaetzen.
FAQ
Warum erscheint mein z-index:9999-Element hinter einem z-index:1-Element?
Stacking Contexts. Dein z-index:9999-Element befindet sich wahrscheinlich in einem Stacking Context (erstellt durch transform, opacity oder position:fixed bei einem Vorfahr), der unter dem Stacking Context liegt, der das z-index:1-Element enthaelt. Eine hoehere Zahl hilft nicht. Bewege das Element aus dem Vorfahr heraus, der den Stacking Context erstellt, oder entferne diese Eigenschaft.
Warum scrollt mein position:fixed-Element mit der Seite?
Ein Vorfahr hat transform, filter, perspective, will-change oder contain. Diese Eigenschaften erstellen einen Containing Block fuer fixed-Positionierte Nachfahren und lassen sie wie absolute wirken. Finde und entferne die Eigenschaft beim Vorfahr oder bewege das fixed-Element hoeher im DOM.
Was ist der Unterschied zwischen position: relative und einem Margin-Versatz?
Beide verschieben das Element visuell, aber position: relative bewahrt den urspruenglichen Platz des Elements im Layout -- andere Elemente fliessen nicht nach, um die Luecke zu fuellen. Ein margin verschiebt das Element und bewirkt, dass umgebende Elemente neu fliessen. Verwende relative, wenn du verschieben willst, ohne Geschwister zu beeinflussen.
Sollte ich inset statt top/right/bottom/left verwenden?
Ja, fuer modernen Code. inset: 0 ist die Kurzform fuer top:0; right:0; bottom:0; left:0. Du kannst auch inset: 10px 20px fuer vertikal/horizontal schreiben. Es ist kuerzer, lesbarer und hat gute Browser-Unterstuetzung (97%+).
Beeinflusst position die Performance?
position: fixed und position: sticky koennen haeufigere Repaints beim Scrollen verursachen, da der Browser sie in jedem Frame neu positionieren muss. Das Hinzufuegen von will-change: transform kann dem Browser bei der Optimierung helfen, aber uebermaessige Verwendung von will-change erhoeht den Speicherverbrauch. Teste auf echten Geraeten, wenn du Scroll-Ruckeln siehst.
Wie positioniere ich etwas relativ zur Viewport-Mitte?
Verwende position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); oder den modernen Ansatz: position: fixed; inset: 0; margin: auto; width: deine-breite; height: deine-hoehe;. Beide zentrieren das Element. Der transform-Ansatz ist flexibler fuer unbekannte Groessen.