12k
All articles

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.

OpenReplay Team
OpenReplay Team
Warum Ihr z-index nicht funktioniert

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 9999 gegen 2, 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, filter oder clip-path außer none sowie eine opacity unter 1 erzeugt einen neuen Stapelkontext, selbst wenn das Element weder position noch z-index hat.
  • Ein Dropdown, das am Rand seiner Card abgeschnitten wird, wird meist durch overflow beschnitten, 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

  1. Öffnen Sie die DevTools und drücken Sie Befehlstaste+Umschalt+P bzw. Strg+Umschalt+P.
  2. Geben Sie Layers ein und wählen Sie Show Layers panel. Alternativ gehen Sie zu More options > More tools > Layers panel.
  3. 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

  1. Öffnen Sie das Befehlsmenü und führen Sie Show 3D View aus.
  2. Wählen Sie den Tab Z-index.
  3. 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.

  1. Wählen Sie das Element aus, das verliert (das Modal, das Menü).
  2. Klicken Sie im Elements-Panel auf sein Elternelement.
  3. Öffnen Sie den Bereich Computed und geben Sie nacheinander jede Eigenschaft aus der Checkliste unten in das Filterfeld ein: transform, opacity, filter, position, z-index usw.
  4. Erzeugt keine davon einen Kontext, gehen Sie eine Ebene nach oben und wiederholen Sie den Vorgang, bis Sie <html> erreichen.
  5. Notieren Sie den ersten Vorfahren, der einen Kontext erzeugt.
  6. Wiederholen Sie die Schritte 1 bis 5 ausgehend von dem Element, das gewinnt.
  7. 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.

EigenschaftErzeugt einen Kontext, wennWorauf Sie im Computed-Bereich achten sollten
position: absolute / relativez-index nicht auto istBeide Werte am selben Element gesetzt
position: fixed / stickyImmerAllein der position-Wert
opacityUnter 1Werte wie 0.99, oft Überbleibsel einer Fade-Animation
transform, scale, rotate, translateJeder Wert außer noneEin matrix(...)-Wert, auch bei Identitätstransformationen
filter, backdrop-filterJeder Wert außer noneblur(), drop-shadow()
clip-path, perspectiveJeder Wert außer noneJede Form oder Länge
mask, mask-image, mask-borderJeder Wert außer noneBild- oder Gradient-Werte
will-changeEine Eigenschaft aufgeführt ist, die beim Setzen einen Kontext erzeugen würdewill-change: transform oder opacity
isolationisolateExpliziter Wert
containlayout, paint, strict oder contentHäufig in virtualisierten Listen
Flex- oder Grid-Kindz-index nicht auto istdisplay: 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

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).

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.

Open-source session replay

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

We use cookies to improve your experience. By using our site, you accept cookies.