Dlaczego z-index nie działa?
- Czas potrzebny na przeczytanie:6 minut
- Opublikowane:
Niech pierwszy rzuci kamieniem ten, który nigdy nie miał problemów z z-index
w CSS. Poprawne ułożenie elementów nie raz przyprawiło mnie o ból głowy. Masakra! Nic w tym dziwnego, ten mechanizm to jeden z "kruczków" CSS, które potrafią być naprawdę problematyczne.
Tworzysz prosty sklep e-commerce. Chciałbyś, żeby nawigacja była pod, a koszyk nad innymi elementami. Prościzna? Dopóki wszystko będzie szło po Twojej myśli. Zanim zaczniesz wyrywać włosy z głowy, z powodu niedziałającego z-index: 999999;
, zrozum jak ten mechanizm naprawdę działa.

Podstawy
Wszyscy wiemy jak ustawić dwa kwadraty tak, aby jeden nachodził na drugi. Wrzucamy dwa boxy do wspólnego rodzica. Dominującemu kwadratowi nadajemy większą wartość z-index
:
<style>
.wrapper {
position: relative;
}
.box-red {
position: absolute;
z-index: 2;
}
.box-pink {
position: absolute;
z-index: 1;
}
</style>
<div class="wrapper">
<div class="box-red"></div>
<div class="box-pink"></div>
</div>
Śmiga, można się rozejść. Rozwiązaliśmy problem upierdliwych z-index
!

Hola hola, to jeszcze nie wszystko. Weźmy teraz na tapet sytuację, w której nasz dominujący kwadrat, opakowujemy w dodatkowy wrapper:

Wszystko poszło się je... Wróćmy do kodu i przeanalizujmy co mogło pójść nie tak.
<div class="wrapper">
<div class="box-dashed">
<div class="box-red"></div>
</div>
<div class="box-pink"></div>
</div>
Na pierwszy ogień idzie HTML - od teraz nasz dominujący kwadrat jest opleciony w dodatkowego rodzica. Z perspektywy CSS, nasz rodzic co prawda ma mniejszy z-index
od kwadratu box-pink
, ale dominujący box-red
powinien to przebić.
Gdzie więc leży problem? 🤔
.wrapper {
position: relative;
}
.box-dashed {
position: relative;
z-index: 1;
}
.box-red {
position: absolute;
z-index: 3;
}
.box-pink {
position: absolute;
z-index: 2;
}
Podpowiedź: ustawienie z-index
na 999999999
nie pomoże 🙈
Stacking Context
Winowajcą tego całego zamieszania jest mistyczny mechanizm w CSS zwany stacking context.
Domyślnie, czysty dokument HTML tworzy context względem którego układa elementy. O stacking context możemy myśleć jak o warstwie, która pozwala nam układać elemeny względem siebie.
Poza domyślą warstwą, nadając elementom konkretne właściwości, tworzymy nowy stacking context. Dla przykładu - połączenie position: relative
/ position: absolute
+ z-index
tworzy nową warstwę.

Porównywać ze sobą elementy możemy tylko względem nadrzędnej warstwy. Właściwość z-index
nie jest właściwością globalną!
Tworząc dwa elementy, gdzie każdy element posiada position: relative
+ odpowiedni z-index
, tworzymy dwie warstwy. Obie warstwy posiadają w sobie dodatkowo po trzy elementy. Każdy z zagnieżdżonych elementów posiada własny z-index
+ position: absolute
.
Wizualizując sobie ten mechanizm, możemy myśleć o zagnieżdżonych warstwach, jak o wersjach nadrzędnego stacking context. Wersja 1.5
jest większa od 1.3
, ale nie ma szans na przebicie 2.4
. Nie ważne jak duży z-index
damy, mógłby on nawet wynosić 999999
, a i tak nie przebije wersji 2.0
:

Spróbujmy naprawić nasz poprzedni przykład. Zależy nam na tym, żeby kwadrat z klasą box-red
pojawił się nad box-pink
. Osiągamy to dzięki pozbyciu się warstwy, którą tworzył box-dashed
za pomocą dwóch wcześniej wspomnianych właściwości - position: relative
+ z-index
.
Chcemy nadal pozycjonować odpowiednio elementy, więc zostawiamy position: relative
. Usuwamy za to z-index
, który w tej kombinacji jest niezbędny do stworzenia warstwy.
/* Stacking Context: ❌ */
.wrapper {
position: relative;
}
/* Stacking Context: ❌ */
.box-dashed {
position: relative;
/* z-index: 1; */
}
/* Stacking Context: ✅ */
.box-red {
position: absolute;
z-index: 3;
}
/* Stacking Context: ✅ */
.box-pink {
position: absolute;
z-index: 2;
}
Usuwając stacking context box-dashed
zostajemy z trzema warstwami:
- Dokument HTML
box-red
box-pink
Zagnieżdżone w domyślym stacking context dokumentu HTML, warstwy box-red
oraz box-pink
pozostają na tym samym poziomie i mogą być porównane ze sobą.

Co tworzy Stacking Context?
Warstwy możemy tworzyć na wiele różnych sposóbów. Powiedzieliśmy sobie, że połącznenie position: relative
/ position: abosolute
+ z-index
tworzy nowy stacking context, czy jest coś jeszcze?
Z popularniejszych metod możemy wymienić:
- Dokument HTML
- Element z pozycją
relative
/abosolute
+z-index
(inne niżauto
) - Element z pozycją
sticky
/fixed
- Dziecko elementu, który ma
display
ustawiony naflex
/grid
- Element z
opacity
mniejszym niż1
Całą listę znajdziesz w dokumentacji MDN.
Kolejność ułożenia w Stacking Context
Udało nam się stworzyć nową warstwę, ale nadal coś nie działa? 🤬
Elementy znajdujące się w obrębie danej warstwy układane będą względem danej kolejności:
- Wypozycjonowane elementy z negatywnym
z-index
- Elementy z
position: static
- Wypozycjonowane elementy z
z-index: auto
- Wypozycjonowane elementy z pozytywnym lub zerowym
z-index

Praktyczny przykład
Wróćmy do naszego początkowego problemu. W naszym sklepie nawigacja ma się znajdować pod, a koszyk nad resztą elementów.

Tworzymy odpowiednią strukturę HTML i nadajemy elementom odpowiednie z-index
. Elementowi z klasą .main
przypisujemy większy z-index
, niż elementowi z klasą .header
. Header jest rodzicem koszyka, więc dodatkowo nadajemy mu position: relative
- dzięki temu będziemy mogli swobodnie umiejscowić koszyk za pomocą position: absolute
.
<style>
.header {
position: relative;
z-index: 1;
}
.cart {
position: abosolute;
top: 5rem;
right: 5rem;
z-index: 999999;
}
.main {
position: relative;
z-index: 10;
transform: translateY(-50px);
}
</style>
<body>
<header class="header">
<nav></nav>
<div class="cart"></div>
</header>
<main class="main">
<aside></aside>
<ul></ul>
</main>
</body>
Widzisz problem? 🤔
Element z klasą .header
stworzył nowy stacking context. Aby poprawić wygląd naszego sklepu, potrzebujemy pozbyć się nowo utworzonej warsty. Kasujemy zbędny z-index: 1
z header i wyszystko zaczęło śmigać!
Od teraz warstwy koszyka i elementu z klasą <main>
są na tym samym poziomie - dlatego możemy je bez obaw z sobą porównywać.
.header {
position: relative;
}
.cart {
position: absolute;
top: 5rem;
right: 5rem;
z-index: 2;
}
.main {
position: relative;
z-index: 1;
transform: translateY(-50px);
}
Uprościłem również wartości z-index
- dbaj o swoje z-indexy i nie nadawaj im nieskończonych wręcz wartości. Zobacz jak utrzymywać w porządku swoje z-index
- Managing CSS Z-Index In Large Projects

Warstwy w izolacji
Wyobraź sobie sytuację, w której tworzysz jakiś reużywaly kawałek strony. W naszym przypadku niech to będzie kafelek przedmiotu. Występuje on na stronie głównej, ale wyświetla się również na stronie z daną kategorią. Od dziś każda taka karta będzie miała w tle grafikę SVG.
Potrzebujemy wypozycjonować SVG tak, żeby znajdowało się pod resztą elementów. Jazda, dodajemy negatywny z-index
i gotowe! Nie do końca, nasza grafika zniknęła z pola naszego widzenia - znajduje się pod samą kartą.

Jeśli pomyślałeś o stacking context, to masz w 100% racje, musimy go stworzyć!
Mamy naprawdę mnóstwo metod, dzięki którym stworzymy nową warstę. Problem jest tylko taki, że karta ma być reużywalna. Nie chcemy jej wprost podać z-index
, bo nie wiemy jak będzie się zachowywała na innych stronach.
Całe na biało w takich przypadkach wjeżdża isolation: isolate
w CSS. Pozwala nam ono stworzyć nowy stacking context bez żadnych skutków ubocznych. Świetna alternatywa dla innych sposóbów, które w przeciwieństwie do isolation
, posiadają nieporządane efekty :)

Podsumowanie
Ustawianie elementów za pomocą z-index
potrafi być naprawdę upierdliwe... Zrozumienie konceptu stacking context to podstawa do uniknięca drogich wizyt u psychologa.
Do usłyszenia!