CSS Stacking-Kontext und z-index

Man könnte glauben, der z-index ist ganz simpel zu verstehen, jedoch versteckt sich dahinter noch ein komplexeres Thema, welches vielen Webentwicklern nicht bekannt ist: Der Stacking-Kontext, der beim Stapeln von Elementen durchaus mal Probleme bereiten kann.

In diesem Artikel erkläre ich ausführlich, worum es sich bei einem Stacking-Kontext genau handelt, wie sie entstehen und welche Auswirkungen sie auf die Stapelung von Elementen haben.

z-index – ein ganz einfaches Konzept?

Zunächst einmal erscheint einem das Konzept hinter dem z-index völlig simpel und selbsterklärend: Ein Element, welches nicht statisch positioniert ist, kann mithilfe dieses Indexes auf der z-Achse angeordnet werden und somit das Standard-Verhalten für die Stapelung von Elementen überschreiben. Je höher dieser Index ist, desto weiter oben im Stapel liegt das Element.

So weit, so gut, dachte ich. Bis sich mir vor einigen Wochen ein seltsamer Fall darbot: Gegeben war ein Seitenlayout mit 2 Spalten, die rechte Spalte als Hauptspalte mit dem eigentlichen Inhalt, die linke Spalte als Sidebar mit einem Filter, welcher Popups öffnen kann. Diese Popups ragen nach rechts in die Hauptspalte hinein. In diesem Fall wurden die Popups von einigen Inhalten aus der Hauptspalte überlagert. Ein schneller Blick zeigte, dass die Popups einen z-index von 9 hatten, die Inhalte in der Hauptspalte aber 20. Soweit sah noch alles logisch aus, doch das Erhöhen des z-index der Popups behob das Problem nicht. Selbst mit einem Index von 1000 wurden die Popups weiterhin von den Inhalten der Hauptspalte überlagert, die nur einen Index von 20 hatten.

Der Grund hierfür ist denkbar simpel, jedoch wissen viele Leute nicht darüber Bescheid: Die Popups lagen wegen der Sidebar in einem anderen Stacking-Kontext, welcher weiter unten im Stapel eingeordnet wurde.

Stacking-Kontext

Ein Stacking-Kontext ist eine Art “Ebenengruppe”. Mindestens einen solchen Kontext gibt es auf jeder Seite: Der Kontext des html-Tags im DOM. Verschiedene CSS-Eigenschaften auf einem Element können einen weiteren Kontext aufmachen. Alle Kinderelemente dieses Elements gehören dann zu diesem neuen Kontext.

Alle Kontexte ordnen zunächst ihre eigenen Kinder auf der z-Achse an. Nachdem dies geschehen ist, ordnet sich der Kontext selbst relativ zu seinen Geschwister-Kontexten auf der z-Achse ein und nimmt dabei alle seine Kinder mit. Die Kinder eines niedrigeren Kontext liegen also automatisch unter den Kindern eines höheren.

Man kann den z-index eines Kinderelements auch als eine Art “Versionsnummer” verstehen, um es sich leichter vorstellen zu können: Ein Element “A” mit einem z-index von 5 innerhalb eines Kontexts mit einem z-index von 2 hat effektiv einen z-index von “2.5”, d. h. der z-index des Kontexts entspricht der Major-Versionsnummer und der z-index des enthaltenen Elements der Minor-Versionsnummer. Ein Element “B” mit z-index 1 innerhalb eines Kontexts mit z-index 3 entspricht dann einer Version “3.1”. Im Ergebnis liegt damit “B” über “A”, obwohl “B” allein einen niedrigeren z-index hat als “A”. Es liegt jedoch in einem höheren Kontext und wird deshalb weiter oben angeordnet.

Folgende CSS-Eigenschaften eröffnen einen zusätzlichen Stacking-Kontext (inkl. Doku-Links der weniger verbreiteten Eigenschaften):

  • position: relative/absolute in Kombination mit einem z-index
  • position: fixed (auch ohne z-index)
  • transform auf einem anderen Wert als none
  • perspective auf einem anderen Wert als none
  • isolation: isolate (Doku)
  • opacity kleiner als 1
  • filter auf einem anderen Wert als none
  • mix-blend-mode auf einem anderen Wert als normal (Doku)
  • -webkit-overflow-scrolling: touch (Doku)
  • will-change mit irgendeiner der oben genannten Eigenschaften (Doku)

Um nochmal auf mein konkretes Problem zurückzukommen: Unsere komplette Sidebar beinhaltete einen div-Container für den Filter, welcher relativ positioniert wurde sowie einen z-index von 3 hatte. Damit lag der Filter unterhalb der Elemente der Hauptspalte mit z-index 20. Ein Erhöhen des z-index der Popups hat nicht geholfen, da diese nur innerhalb des Filter-Kontext mit Index 3 galten, sie konnten also niemals die Elemente mit Index 20 überlagern.

Die Lösung bestand darin, den z-index und damit den Stacking-Kontext des Filters als Ganzes anzuheben, sodass er alle Elemente der Hauptspalte überlagert. Dann funktionieren auch die zugehörigen Popups wieder, wie sie es sollen. Sie benötigen außerdem keinen irrsinnig hohen z-index mehr, um alles zu überlagern, sondern können wieder bei 1 anfangen.

Im folgenden noch ein knappes Beispiel zur Veranschaulichung:

See the Pen Stacking-Context by Linda Grünwald (@lindagruenwald) on CodePen.

Man sieht z. B., dass das achte Element einen z-index von 1 hat, jedoch das zweite Element mit einem Index von 2 überlagert. Gleichermaßen überlagert das erste Element mit einem Index von 8 das siebte Element mit einem Index von 9. Beides kommt daher, dass das siebte und achte Element innerhalb eines Stacking-Kontexts mit Index 5 liegen, welcher vom fünften Element geöffnet wurde.