HTML/CSSMiddleTechnical

Что такое CSS z-index и как работает контекст наложения (stacking context)?

z-index работает только внутри stacking context. Новый контекст создаётся при position+z-index, opacity<1, transform, filter и др. Элемент с z-index:9999 внутри контекста с низким z-index будет перекрыт.

z-index и контекст наложения (stacking context)

z-index определяет порядок отрисовки элементов вдоль оси Z (от экрана к зрителю). Но работает он не глобально — только внутри контекста наложения (stacking context), к которому принадлежит элемент.

Базовый порядок наложения без z-index

Без явного z-index элементы отрисовываются в следующем порядке (снизу вверх):

  1. Фоны и границы корневого элемента
  2. Блочные дочерние элементы (block)
  3. Floating-элементы
  4. Строчные дочерние элементы (inline)
  5. Positioned-элементы (position: relative/absolute/fixed/sticky) без z-index

Как работает z-index

.box-a {
  position: relative;
  z-index: 1; /* выше box-b */
}
.box-b {
  position: relative;
  z-index: 0;
}

z-index работает только на positioned элементах (position не static) или flex/grid children.

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

  • position: relative/absolute/fixed/sticky + z-index не auto
  • opacity < 1
  • transform, filter, clip-path, mask (любое значение кроме none)
  • will-change: transform (или любое animatable свойство)
  • isolation: isolate
  • Flex/grid item с z-index не auto

Классический баг: z-index не работает

<!-- Проблема: modal перекрыт sidebar -->
<div class="sidebar" style="position: relative; z-index: 1; transform: translateZ(0);">
  <!-- transform создаёт новый stacking context! -->
  <div class="modal" style="position: fixed; z-index: 9999;"></div>
  <!-- modal с z-index:9999 ограничен контекстом sidebar с z-index:1 -->
</div>
<div class="header" style="position: relative; z-index: 2;"></div>
<!-- header с z-index:2 перекрывает весь sidebar вместе с modal -->

Решение: вынос portal за пределы контекста

<!-- В React: portal в document.body -->
{ReactDOM.createPortal(
  <div className="modal">...</div>,
  document.body
)}

<!-- В Vue: Teleport -->
<Teleport to="body">
  <div class="modal">...</div>
</Teleport>

<!-- В ванильном JS -->
document.body.appendChild(modalElement);

Инструмент: isolation: isolate

.card {
  isolation: isolate; /* создаёт stacking context без z-index/opacity/transform */
  /* теперь pseudo-элементы и дочерние элементы не вступают в конфликт с внешними */
}

Дебаггинг stacking context

/* Chrome DevTools: Elements → Computed → показывает stacking context */
/* или: расширение CSS Stacking Context Inspector */

/* В коде: проверить через getComputedStyle */
const style = getComputedStyle(el);
console.log(style.transform, style.opacity, style.zIndex);

Подводные камни

  • transform: translateZ(0) и will-change: transform создают новый stacking context — распространённый паттерн «GPU layer hack» неожиданно ограничивает z-index дочерних модалок.
  • opacity: 0.99 (иногда используется вместо visibility) тоже создаёт stacking context.
  • Fixed-элементы (position: fixed) позиционируются относительно viewport, но если родитель имеет transform, fixed работает относительно этого родителя — баг в спецификации, не в браузере.
  • Гонка z-index: избегайте глобальных z-index: 9999 — используйте именованные токены (--z-modal: 300; --z-tooltip: 400) и документируйте уровни.
  • Flex-дочерние элементы принимают z-index без position — неожиданно отличается от поведения block-элементов.
  • Backdrop-filter на родителе ограничивает fixed-позиционирование у некоторых браузеров (Safari).

Common mistakes

  • Смешивать «z-index и stacking context» с похожим механизмом без критерия выбора.
  • Игнорировать риск: неверно оценить границы применения темы «z-index и stacking context» и получить хрупкое решение.
  • Показывать только синтаксис и не объяснять поведение в runtime или сборке.

What the interviewer is testing

  • Объясняет почему большой z-index иногда не выводит элемент наверх.
  • Показывает на примере, как работает: z-index сравнивается внутри своего stacking context; новые контексты создают positioned elements, opacity меньше 1, transform, filter, isolation и другие свойства.
  • Называет production-нюанс и граничный случай для темы «z-index и stacking context».

Sources

Related topics