Warum Ihr z-index nicht funktioniert
Finden Sie heraus, warum z-index nicht funktioniert: Untersuchen Sie Stacking-Kontexte in den DevTools, erkennen Sie den begrenzenden Vorfahren und beheben Sie Probleme mit Modals, Menüs und Sticky-Headern.
Wenn ein Element mit z-index: 9999 immer noch hinter einem Element mit 2 dargestellt wird, hat ein Vorfahr des ersten Elements einen Stapelkontext (Stacking Context) erzeugt, und ein z-index wirkt niemals über den Kontext hinaus, zu dem er gehört.
Sie haben es schon mit 99999 versucht. Dann mit 2147483647. Das Modal liegt immer noch unter dem Header, und im Styles-Panel sieht alles korrekt aus.
Die Theorie hinter der Stapelreihenfolge erklärt der Artikel CSS: Demystifying the z-index property. Dieser Leitfaden widmet sich dem Debugging: wie Sie Stapelkontexte in den DevTools sichtbar machen, wie Sie sich im DOM nach oben arbeiten, bis Sie den Vorfahren finden, der Ihr Element einschließt, und wie Sie jeden der typischen Fälle beheben, ohne die Zahl zu erhöhen.
Die wichtigsten Erkenntnisse
- Ein z-index-Wert konkurriert nur mit Elementen im selben Stapelkontext. Deshalb verliert
9999gegen2, wenn ein Vorfahr des ersten Elements einen eigenen Stapelkontext besitzt. - Wenn ein höherer z-index verliert, vergleichen Sie die Vorfahren der beiden Elemente, die ein gemeinsames Elternelement haben. Die Werte der Elemente selbst sind nicht entscheidend.
- Jeder Wert von
transform,filteroderclip-pathaußernonesowie eineopacityunter 1 erzeugt einen neuen Stapelkontext, selbst wenn das Element wederpositionnochz-indexhat. - Ein Dropdown, das am Rand seiner Card abgeschnitten wird, wird meist durch
overflowbeschnitten, und daran ändert kein z-index-Wert etwas. - Wird ein Overlay zu einem direkten Kind von
<body>, verlässt es die Stapelkontexte sämtlicher Vorfahren.
Welche Grundregel steckt hinter einem wirkungslosen z-index?
Ein z-index ordnet ein Element nur gegenüber den anderen Mitgliedern seines eigenen Stapelkontexts ein. Ein Stapelkontext ist eine Gruppe, die der Browser als eine Einheit zeichnet. Der MDN-Leitfaden zum Stapelkontext listet die Eigenschaften auf, die einen solchen erzeugen. Erzeugt ein Vorfahr einen Kontext, gilt der z-index des Kindes nur innerhalb dieses Vorfahren. Liegt der Vorfahr also unter einem anderen Kontext, bleibt auch das Kind darunter, ganz gleich, welche Zahl Sie ihm geben.
Hier die minimale Version des Fehlers:
<header style="position: fixed; top: 0; z-index: 2; background: tomato;">Header</header>
<main style="opacity: 0.99;">
<div class="modal" style="position: fixed; inset: 0; z-index: 9999; background: rgb(0 0 0 / 0.6);">
Modal
</div>
</main>
Das Modal verliert, weil main den Wert opacity: 0.99 hat, wodurch main zu einem Stapelkontext wird. Innerhalb von main funktioniert der Wert 9999 des Modals einwandfrei. Doch main selbst wird auf Root-Ebene mit dem Header verglichen, und dessen 2 ist höher. Wenn ein höherer z-index verliert, betrachten Sie die Vorfahren: Suchen Sie für jedes Element den nächstgelegenen Vorfahren, der ein gemeinsames Elternelement mit dem anderen teilt, und vergleichen Sie diese beiden.
Wie macht man Stapelkontexte in den DevTools sichtbar?
Die Chromium-DevTools bieten visuelle Werkzeuge zur Untersuchung von Ebenenproblemen, mit denen Sie oft schneller ans Ziel kommen als durch Raten. Sowohl Chrome als auch Edge öffnen diese Ansichten über das Befehlsmenü (Command Menu), unter macOS mit Befehlstaste+Umschalt+P, unter Windows und Linux mit Strg+Umschalt+P. Weitere oft ungenutzte DevTools-Panels stellt der Artikel Hidden Gems in Chrome DevTools vor.
Chrome: Das Layers-Panel
- Öffnen Sie die DevTools und drücken Sie Befehlstaste+Umschalt+P bzw. Strg+Umschalt+P.
- Geben Sie
Layersein und wählen Sie Show Layers panel. Alternativ gehen Sie zu More options > More tools > Layers panel. - Drehen Sie die Zeichenfläche und wählen Sie eine Ebene aus, um deren Details zu sehen – einschließlich der Begründung, warum der Browser ihr eine eigene Ebene zugewiesen hat.
Das Layers-Panel zeigt links einen Baum der Ebenen der Seite und daneben ein 3D-Diagramm. Mit dem Panel können Sie untersuchen, wie der Browser die Seite in Compositing-Ebenen aufgeteilt hat. Beachten Sie jedoch, dass diese nicht mit Stapelkontexten identisch sind. Um den Vorfahren zu finden, der für einen Stapelkontext verantwortlich ist, verwenden Sie die unten beschriebene Walk-up-Methode. Das Panel spielt auch beim Debugging ruckelnder CSS-Animationen eine Rolle.
Edge: 3D View
- Öffnen Sie das Befehlsmenü und führen Sie Show 3D View aus.
- Wählen Sie den Tab Z-index.
- Aktivieren Sie die Option Show all, um positionierte Elemente zusammen mit den Stapelkontexten anzuzeigen.
Die 3D View von Edge hat drei Tabs: Composited Layers, Z-index und DOM. Der Tab Z-index stellt die Stapelkontexte der Seite in 3D dar, sodass ein eingeschlossenes Element als in eine tiefer liegende Ebene verschachtelt erscheint.
Wenn Sie in Chrome oder Firefox eine Baumansicht der Stapelkontexte wünschen, ergänzt die Erweiterung CSS Stacking Context inspector ein Panel mit dem Kontextbaum sowie eine Seitenleiste im Elements-Panel, die erklärt, warum das ausgewählte Element einen Kontext erzeugt. Laut ihrer Dokumentation kann sie bislang nicht in iframes von Drittanbietern hineinschauen.
Die Walk-up-Methode: Vorfahren schrittweise prüfen
Die Walk-up-Methode findet den einschließenden Vorfahren allein mit dem Elements-Panel und dem Computed-Bereich. Sie funktioniert in jedem Browser gleich.
- Wählen Sie das Element aus, das verliert (das Modal, das Menü).
- Klicken Sie im Elements-Panel auf sein Elternelement.
- Öffnen Sie den Bereich Computed und geben Sie nacheinander jede Eigenschaft aus der Checkliste unten in das Filterfeld ein:
transform,opacity,filter,position,z-indexusw. - Erzeugt keine davon einen Kontext, gehen Sie eine Ebene nach oben und wiederholen Sie den Vorgang, bis Sie
<html>erreichen. - Notieren Sie den ersten Vorfahren, der einen Kontext erzeugt.
- Wiederholen Sie die Schritte 1 bis 5 ausgehend von dem Element, das gewinnt.
- Die beiden Ketten treffen sich bei einem gemeinsamen Vorfahren. Dessen zwei Kinder auf diesen Ketten sind die Elemente, die tatsächlich miteinander verglichen werden.
Arbeiten Sie mit einem Bug-Report statt mit einer lokalen Reproduktion, zeigt Session Replay das Symptom aus Sicht der Nutzer, etwa einen Klick, der auf einem unsichtbaren Overlay landet, oder ein Menü, das sich hinter dem Inhalt öffnet. So wissen Sie, welches Element Sie in Schritt 1 auswählen müssen.
Checkliste: Eigenschaften, die einen Stapelkontext erzeugen
Prüfen Sie jeden Vorfahren anhand dieser Tabelle. Eine einzige zutreffende Zeile genügt, um seine Nachfahren einzuschließen.
| Eigenschaft | Erzeugt einen Kontext, wenn | Worauf Sie im Computed-Bereich achten sollten |
|---|---|---|
position: absolute / relative | z-index nicht auto ist | Beide Werte am selben Element gesetzt |
position: fixed / sticky | Immer | Allein der position-Wert |
opacity | Unter 1 | Werte wie 0.99, oft Überbleibsel einer Fade-Animation |
transform, scale, rotate, translate | Jeder Wert außer none | Ein matrix(...)-Wert, auch bei Identitätstransformationen |
filter, backdrop-filter | Jeder Wert außer none | blur(), drop-shadow() |
clip-path, perspective | Jeder Wert außer none | Jede Form oder Länge |
mask, mask-image, mask-border | Jeder Wert außer none | Bild- oder Gradient-Werte |
will-change | Eine Eigenschaft aufgeführt ist, die beim Setzen einen Kontext erzeugen würde | will-change: transform oder opacity |
isolation | isolate | Expliziter Wert |
contain | layout, paint, strict oder content | Häufig in virtualisierten Listen |
| Flex- oder Grid-Kind | z-index nicht auto ist | display: flex oder grid am Elternelement. Kein position erforderlich |
Die vollständige Liste bei MDN umfasst außerdem mix-blend-mode mit einem anderen Wert als normal, Elemente im Top Layer sowie Elemente, deren kontexterzeugende Eigenschaften mit animation-fill-mode: forwards animiert werden. MDN führt zudem noch container-type: size und inline-size auf, doch die CSS Working Group hat die Layout-Containment aus container-type entfernt, sodass dadurch kein Stapelkontext mehr entsteht. Chrome hat diese Änderung in Version 129 umgesetzt, Firefox und Safari sind inzwischen nachgezogen.
Drei typische Fälle: Symptom, Ursache, Lösung
Modal hinter dem Header
Symptom: Das Overlay dunkelt die Seite ab, aber der Header bleibt hell darüber. Ursache: Das Modal wird innerhalb eines Wrappers gerendert, der ein Transform, eine Opacity oder einen positionierten z-index besitzt, und der Kontext des Headers rangiert über diesem Wrapper. Lösung: Verschieben Sie das Modal nach <body> (siehe die Lösungen unten).
Dropdown in einer Card mit Transform abgeschnitten
Ein Dropdown in einer transformierten Card hat eines von zwei Problemen – Stapelung oder Beschneidung (Clipping). Klären Sie daher zuerst, welches vorliegt. Wird das Menü hinter der nächsten Card gezeichnet, handelt es sich um ein Stapelproblem: Die transformierte Card ist ein Kontext, und Cards, die später im DOM folgen, werden darüber gezeichnet. Heben Sie die geöffnete Card an:
.card.is-open { position: relative; z-index: 1; }
Wird das Menü am Rand der eigenen Card abgeschnitten, handelt es sich um Clipping. Prüfen Sie overflow an der Card und ihren Vorfahren. Jeder Wert außer visible schneidet das Menü ab, und kein z-index-Wert holt es zurück.
Sticky-Element, das nicht oben bleibt
Symptom: Cards schieben sich beim Hovern oder Scrollen über einen Sticky-Header. Ursache: Jedes Element mit position: fixed oder sticky erzeugt einen Kontext. Ohne z-index liegt es jedoch auf Ebene 0, und später folgende Geschwisterelemente, die Kontexte erzeugen (etwa Cards mit Hover-Transforms), werden darüber gezeichnet, weil sie im DOM später kommen. Lösung:
.site-header { position: sticky; top: 0; z-index: var(--z-sticky); }
Bleibt der Header überhaupt nicht haften, liegt die Ursache meist im overflow eines Vorfahren. Eine vollständige Anleitung für einen Sticky-Header finden Sie unter Building a responsive sticky navbar with CSS.
Drei Wege, ein eingeschlossenes Element zu befreien
Jede Lösung für ein eingeschlossenes Element verändert die Struktur der Stapelkontexte statt der z-index-Zahl: Verschieben Sie das Element, verschieben Sie die Eigenschaft, oder setzen Sie bewusst eine Grenze dort, wo Sie sie haben möchten.
Element im DOM verschieben. Ein Overlay, das ein direktes Kind von <body> ist, hat keinen Vorfahrenkontext über sich, sodass sein z-index auf Root-Ebene verglichen wird. Deshalb funktionieren Portals so gut für Overlays, die die gesamte Seite überdecken sollen:
const modal = document.querySelector('.modal');
function openModal() {
document.body.appendChild(modal); // escape every ancestor context
modal.hidden = false;
}
Eigenschaft entfernen oder verlagern. Andy Blum von Lullabot beschrieb einen Fall, in dem ein Right-to-Left-Layout einen Container mit Hintergrundvideo per Transform spiegelte, wodurch der Play/Pause-Button des Videos verdeckt wurde. Er löste das Problem, indem er das Transform vom Container auf das Video-iframe verlagerte:
/* Before: the wrapper becomes a context */
[dir="rtl"] .hero { transform: scaleX(-1); }
/* After: only the media is mirrored */
[dir="rtl"] .hero iframe { transform: scaleX(-1); }
Gezielt isolieren. Wenn Sie isolation: isolate auf dem Root-Element einer Komponente setzen, erzeugen Sie bewusst einen Kontext, sodass jeder z-index innerhalb der Komponente lokal bleibt:
.card { isolation: isolate; }
.card .badge { position: absolute; z-index: 1; } /* never competes with the site header */
Vorbeugen mit einer z-index-Skala
Ein z-index von 9999 bedeutet fast immer, dass jemand den Stapelkontext nie gefunden hat – nicht, dass eine größere Zahl nötig war. Ersetzen Sie Einzelwerte durch eine kleine, dokumentierte Skala:
:root {
--z-base: 0;
--z-dropdown: 100;
--z-sticky: 200;
--z-overlay: 300;
--z-modal: 400;
--z-toast: 500;
}
.modal { z-index: var(--z-modal); }
Die Tokens der z-index-Skala ordnen nur Elemente, die sich einen Stapelkontext teilen. Deshalb entfalten sie ihre Wirkung am besten in Kombination mit Portals für seitenweite Ebenen und isolation: isolate auf den Root-Elementen von Komponenten.
Fazit
Ein wirkungsloser z-index ist fast immer ein Problem des Stapelkontexts, nicht der Zahl. Wenn 9999 das nächste Mal verliert, lassen Sie den Wert in Ruhe. Öffnen Sie das Layers-Panel oder die 3D View, um zu prüfen, was wo gezeichnet wird, und arbeiten Sie sich dann mithilfe der Checkliste von beiden Elementen aus nach oben, bis sich die beiden Ketten treffen. Die beiden Kinder des gemeinsamen Vorfahren sind die Elemente, die verglichen werden, und an einem von ihnen setzt die Lösung an.
FAQs
Warum hat z-index überhaupt keine Wirkung auf mein Element?
z-index gilt nur für positionierte Elemente, also solche mit position: relative, absolute, fixed oder sticky, sowie für Flex- und Grid-Items. Bei einem Element mit dem Standardwert position: static, das kein Flex- oder Grid-Kind ist, ignoriert der Browser z-index vollständig. Fügen Sie position: relative hinzu, damit der Wert greift, und prüfen Sie anschließend, ob ein Stapelkontext eines Vorfahren ihn weiterhin einschränkt.
Entkommt ein mit showModal() geöffneter Dialog den Stapelkontexten?
Ja. Der Aufruf von showModal() auf einem dialog-Element hebt es in den Top Layer, der über allen Stapelkontexten des Dokuments liegt. Transforms und z-index-Werte von Vorfahren beeinflussen seine Reihenfolge dann nicht mehr. Popovers, die mit showPopover() angezeigt werden, werden auf die gleiche Weise hochgestuft. Innerhalb des Top Layers hat z-index keine Wirkung: Elemente werden in der Reihenfolge gestapelt, in der sie hochgestuft wurden, sodass das zuletzt geöffnete oben erscheint.
Warum wird mein Overlay mit position: fixed relativ zu seinem Elternelement statt zum Viewport positioniert?
Ein Vorfahr mit transform, perspective oder filter ungleich none wird zum Containing Block für fixiert positionierte Nachfahren. Ein fixiertes Overlay darin wird dann relativ zu diesem Vorfahren positioniert und dimensioniert, nicht relativ zum Viewport. Dieselbe Eigenschaft erzeugt zudem einen Stapelkontext, sodass das Overlay gleichzeitig hinter anderen Inhalten liegen und den falschen Bereich abdecken kann. Wenn Sie das Overlay nach body verschieben, sind beide Probleme gelöst.
Kann ein negativer z-index ein Kind hinter sein Elternelement legen?
Nur, wenn das Elternelement keinen Stapelkontext erzeugt. Innerhalb eines Stapelkontexts zeichnet der Browser zuerst Hintergrund und Rahmen des Kontext-Roots selbst, dann Kinder mit negativem z-index und anschließend die Inhalte im normalen Fluss. Hat das Elternelement eine opacity unter 1, ein Transform oder einen positionierten z-index ungleich auto, wird ein Kind mit z-index: -1 dennoch über dem Hintergrund des Elternelements gezeichnet, allerdings unter dessen normalem Inhalt.
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Star on GitHub12k