z-index

z-index управляет порядком наложения элементов по оси Z (глубина). Работает только на позиционированных элементах (position != static). Ключевая концепция — stacking context (контекст наложения).

Зачем нужно

Когда элементы перекрываются (модальные окна, выпадающие меню, тултипы, фиксированные шапки), z-index определяет, какой элемент окажется сверху. Без понимания stacking context невозможно отладить проблемы наложения.

Где используется

  • Модальные окна и оверлеи
  • Выпадающие меню и тултипы
  • Фиксированные шапки и sidebar
  • Toast-уведомления
  • Sticky-элементы

Предпосылки

  • Position — позиционирование элементов
  • Display — поток документа

Базовый синтаксис

.below  { position: relative; z-index: 1; }
.above  { position: relative; z-index: 2; } /* Сверху */

/* Значения */
.auto    { z-index: auto; }     /* Не создаёт stacking context */
.zero    { z-index: 0; }        /* Создаёт stacking context */
.neg     { z-index: -1; }       /* Ниже нормального потока */
.high    { z-index: 9999; }     /* Высокий приоритет */

z-index работает только при position: relative | absolute | fixed | sticky или в flex/grid-потомках.


Stacking Context (контекст наложения)

Stacking context — это «слой», внутри которого z-index действует локально. Элемент с z-index: 999 внутри контекста с z-index: 1 не перекроет элемент с z-index: 2 вне этого контекста.

Что создаёт новый stacking context

/* 1. position + z-index */
.ctx1 { position: relative; z-index: 0; }

/* 2. position: fixed или sticky (всегда) */
.ctx2 { position: fixed; }

/* 3. opacity < 1 */
.ctx3 { opacity: 0.99; }

/* 4. transform */
.ctx4 { transform: translateZ(0); }

/* 5. filter */
.ctx5 { filter: blur(0); }

/* 6. will-change */
.ctx6 { will-change: transform; }

/* 7. isolation: isolate */
.ctx7 { isolation: isolate; }

/* 8. contain: layout или paint */
.ctx8 { contain: paint; }

/* 9. mix-blend-mode */
.ctx9 { mix-blend-mode: multiply; }

/* 10. Flex/Grid потомок с z-index */
.flex-container { display: flex; }
.flex-container > .ctx10 { z-index: 1; }

Визуализация проблемы

<div class="parent-a" style="position: relative; z-index: 1;">
  <div class="child" style="position: absolute; z-index: 999;">
    Я z-index: 999, но внутри контекста с z-index: 1
  </div>
</div>

<div class="parent-b" style="position: relative; z-index: 2;">
  Я z-index: 2, и я СВЕРХУ child с z-index: 999!
</div>

Порядок наложения (Stacking Order)

Внутри одного stacking context элементы располагаются в таком порядке (снизу вверх):

  1. Фон и границы элемента-контекста
  2. Потомки с отрицательным z-index
  3. Блочные элементы в потоке (не позиционированные)
  4. Float-элементы
  5. Inline-элементы в потоке
  6. Позиционированные с z-index: auto или z-index: 0
  7. Потомки с положительным z-index
/* Элементы без z-index сортируются по порядку в DOM */
/* Последний в HTML — сверху */

Система z-index токенов

Для больших проектов — централизованная шкала:

:root {
  --z-dropdown: 100;
  --z-sticky: 200;
  --z-fixed: 300;
  --z-modal-backdrop: 400;
  --z-modal: 500;
  --z-popover: 600;
  --z-tooltip: 700;
  --z-toast: 800;
}

.dropdown  { z-index: var(--z-dropdown); }
.header    { z-index: var(--z-fixed); }
.modal-bg  { z-index: var(--z-modal-backdrop); }
.modal     { z-index: var(--z-modal); }
.tooltip   { z-index: var(--z-tooltip); }
.toast     { z-index: var(--z-toast); }

isolation: isolate

Создаёт stacking context без побочных эффектов:

/* Изолировать компонент — его z-index не повлияет на внешний мир */
.component {
  isolation: isolate;
}

/* Внутри компонента z-index работает локально */
.component .overlay {
  position: absolute;
  z-index: 10; /* Только внутри .component */
}

isolation: isolate — лучший способ создать stacking context, не меняя opacity, transform и т.д.


Отладка z-index

DevTools

  1. Chrome: Elements → Computed → ищи z-index
  2. Firefox: Layout → Stacking Context (показывает дерево контекстов)
  3. 3D View: Chrome DevTools → More tools → Layers

Чеклист при проблемах

  1. Есть ли position != static?
  2. Не создал ли предок новый stacking context?
  3. Какие z-index у конкурирующих элементов?
  4. Не ограничивает ли overflow: hidden видимость?
/* Отладочный приём — подсветить все stacking contexts */
* {
  outline: 1px solid rgba(255, 0, 0, 0.2);
}
[style*="z-index"],
[class] {
  outline: 2px solid red;
}

Частые ошибки

  1. z-index без position — не работает:

    /* НЕ РАБОТАЕТ */
    .box { z-index: 100; }
    
    /* РАБОТАЕТ */
    .box { position: relative; z-index: 100; }
    
  2. «Война z-index»z-index: 999999 вместо понимания stacking context:

    /* ПЛОХО */
    .modal { z-index: 999999; }
    .tooltip { z-index: 9999999; }
    
    /* ХОРОШО — используйте токены */
    .modal { z-index: var(--z-modal); }
    .tooltip { z-index: var(--z-tooltip); }
    
  3. Неожиданный stacking context от opacity/transform — элемент с opacity: 0.99 создаёт новый контекст

  4. z-index на flex-потомке без position — работает! Но неожиданно для многих:

    .flex-parent { display: flex; }
    .flex-child { z-index: 1; } /* Работает без position! */
    
  5. Отрицательный z-index уходит за фон родителя — если у родителя нет stacking context:

    .child { position: relative; z-index: -1; } /* За фоном родителя! */
    /* Решение: isolation: isolate на родителе */
    

Практика

  • Создать 3 перекрывающихся блока и управлять порядком через z-index
  • Воспроизвести проблему stacking context (z-index: 999 не работает)
  • Реализовать систему z-index токенов
  • Использовать isolation: isolate для компонента
  • Отладить z-index через Firefox Layout panel

Связанные темы

Ресурсы