Главная идея
Пока элементы не перекрываются, порядок слоёв не важен. Но как только блоки накладываются друг на друга (через position, отрицательные отступы, наложение), возникает вопрос: кто окажется сверху? За это отвечает третья ось — ось z, направленная «из экрана к зрителю». Свойство z-index двигает элемент вдоль неё.
Ключевой и неочевидный момент: z-index — это не абсолютная высота элемента над всей страницей. Число сравнивается только с «соседями» внутри одного контекста наложения (stacking context). Контекст — это локальный «мир слоёв»: потомки внутри него сортируются между собой, но во внешний мир весь контекст выходит как один слой. Поэтому ребёнок с z-index: 9999 не перепрыгнет чужой блок, если его родитель «заперт» в своём контексте с маленьким числом.
Представьте многоэтажный дом. Этаж — это контекст наложения, а квартиры на этаже — элементы внутри него. Можно бесконечно нумеровать квартиры (101, 999, 9999) — но квартира на 2-м этаже всё равно ниже любой квартиры 3-го этажа. Номер квартиры (z-index) решает порядок только внутри своего этажа. Чтобы подняться выше соседнего блока, нужно сменить этаж — то есть выйти из своего контекста наложения, а не наращивать число.
Как работает: порядок слоёв по оси z
В пределах одного контекста браузер раскладывает элементы примерно в таком порядке (снизу вверх): фон контекста → блоки с отрицательным z-index → обычный поток → плавающие элементы → строчный контент → позиционированные элементы с z-index: auto/0 → элементы с положительным z-index. Схематично ось z выглядит так:
z-index, тем ближе слой к зрителю — но только в пределах одного контекста наложения.position + z-index. Новый контекст порождают также opacity меньше 1, любой transform/filter/perspective, isolation: isolate, will-change, элементы внутри flex/grid с заданным z-index и др. Полный и актуальный список — в MDN «Stacking context».Базовый пример: бейдж поверх картинки
Чтобы бейдж лёг поверх изображения, оба должны быть позиционированы (без position z-index игнорируется), а бейдж получает большее число.
.card { position: relative; }
.badge {
position: absolute;
top: 8px; right: 8px;
z-index: 2; /* поверх картинки */
}
Когда применять
z-index нужен везде, где элементы намеренно перекрываются: бейджи и метки на карточках, выпадающие меню, всплывающие подсказки, «липкие» шапки, модальные окна с затемнением, тосты-уведомления. Хорошая практика — не плодить случайные числа, а завести единую шкалу слоёв (base → dropdown → sticky → overlay → modal → toast) и хранить её в CSS-переменных.
Частая ошибка: ребёнок не может перепрыгнуть соседа
Самая болезненная ловушка: вы ставите ребёнку z-index: 9999, а он всё равно под соседним блоком. Причина — родитель создал свой stacking context (например, transform или opacity), и весь ребёнок «заперт» внутри него. Сравнивается не 9999 с соседом, а контекст родителя с соседом.
- Сначала найдите позиционирование:
positionи offsets (top/right/bottom/left). - Поднимитесь по родителям и проверьте, кто создаёт контекст (
transform,opacity<1,filter,isolation). - Сравнивайте
z-indexтолько среди элементов одного контекста. - Если элемент не поднимается — чините не число, а границу контекста (выносите элемент выше по дереву или поднимайте сам родитель).