Проблемы с z-index

В основе проблем с z-index лежит модель наложения (stacking context), реализованная в браузерах. Каждый элемент на странице участвует в иерархии слоёв, где порядок отображения определяется не только значением z-index, но и структурой DOM и наличием контекстов наложения.

Контекст наложения создаётся в следующих случаях:

  • корневой элемент (<html>)
  • элементы с position (relative, absolute, fixed, sticky) и заданным z-index
  • элементы с opacity < 1
  • элементы с transform, filter, perspective
  • элементы с isolation: isolate
  • flex- и grid-контейнеры с заданным z-index

Каждый такой контекст изолирует дочерние элементы: их z-index сравнивается только внутри этого контекста и не влияет на соседние.

Как Tippy.js управляет слоями

Библиотека Tippy.js создаёт всплывающие подсказки (tooltip) как отдельные DOM-узлы, которые по умолчанию добавляются в document.body. Это делается через опцию:

appendTo: document.body

Таким образом, tooltip вырывается из локального контекста наложения и помещается на верхний уровень DOM, где проще контролировать отображение.

По умолчанию используется z-index: 9999, что достаточно для большинства сценариев. Однако в сложных интерфейсах этого может оказаться недостаточно.

Типичные проблемы с z-index

Перекрытие другими элементами

Tooltip оказывается под другими элементами интерфейса, например:

  • модальные окна
  • fixed-шапки
  • боковые панели
  • кастомные контейнеры с высоким z-index

Причина — более высокий z-index у этих элементов или нахождение tooltip в другом stacking context.

Влияние transform и overflow

Если родительский элемент имеет:

transform: translateZ(0);
overflow: hidden;

то создаётся новый контекст наложения и область обрезки. В этом случае tooltip может:

  • обрезаться
  • отображаться под другими элементами
  • некорректно позиционироваться

Ошибки при использовании appendTo

Если явно задано:

appendTo: someElement

и этот элемент находится внутри stacking context, tooltip наследует ограничения этого контекста.

Диагностика проблем

Проверка через DevTools

  • инспекция элемента tooltip (.tippy-box)
  • просмотр computed-стилей (z-index, position)
  • анализ родительских элементов на наличие transform, opacity, overflow

Визуальная проверка stacking context

В Chrome DevTools можно включить:

  • Layers panel
  • Paint flashing

Это помогает увидеть, какие элементы формируют контексты наложения.

Решения и подходы

Увеличение z-index

Самый простой способ:

tippy(element, {
  zIndex: 999999
});

или через CSS:

.tippy-box {
  z-index: 999999 !important;
}

Однако это не всегда работает, если проблема связана с контекстами наложения.

Использование appendTo: document.body

Гарантирует, что tooltip не ограничен родительскими элементами:

tippy(element, {
  appendTo: document.body
});

Это основной способ избежать проблем с overflow и transform.

Удаление transform у родителя

Если возможно, убрать:

transform: translateZ(0);

или заменить его на альтернативу. Это устраняет создание stacking context.

Работа с overflow

Если tooltip обрезается:

overflow: visible;

у родительского контейнера может решить проблему.

Если это невозможно — использовать appendTo: document.body.

Использование portal-подхода

Tippy.js фактически реализует портал (перемещение элемента в другую часть DOM). Это стандартный подход для UI-библиотек:

  • React Portal
  • Vue Teleport

Он позволяет избежать влияния локальных контекстов.

Изоляция слоёв интерфейса

Создание системного уровня слоёв:

:root {
  --z-tooltip: 1000;
  --z-modal: 2000;
  --z-overlay: 3000;
}

И использование:

tippy(element, {
  zIndex: 1000
});

Это помогает избежать хаотичного роста z-index.

Особенности взаимодействия с модальными окнами

При работе с модальными окнами (например, кастомными или из UI-библиотек):

  • модалка часто имеет z-index: 10000+
  • tooltip с 9999 оказывается под ней

Решение:

tippy(element, {
  zIndex: 11000
});

или динамическая установка в зависимости от контекста.

Проблемы в flex и grid контейнерах

Flex и grid могут создавать stacking context при наличии z-index. Это приводит к неожиданному поведению:

  • tooltip оказывается под соседними элементами
  • z-index не работает как ожидается

Решение — избегать задания z-index на контейнере без необходимости.

Влияние position: fixed

Tooltip с position: absolute внутри контейнера с position: fixed может вести себя нестабильно при прокрутке.

Tippy.js решает это через Popper.js, но при кастомных настройках возможны артефакты.

Рекомендуется использовать:

tippy(element, {
  strategy: 'fixed'
});

Интеграция с Popper.js

Tippy.js использует Popper.js для позиционирования. Popper учитывает:

  • границы viewport
  • overflow-контейнеры
  • clipping parents

Иногда проблема воспринимается как z-index, но на деле tooltip просто перемещается внутрь видимой области.

Настройки:

tippy(element, {
  popperOptions: {
    modifiers: [
      {
        name: 'preventOverflow',
        options: {
          boundary: document.body
        }
      }
    ]
  }
});

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

  • попытка решить всё через увеличение z-index
  • игнорирование stacking context
  • использование appendTo без понимания структуры DOM
  • наличие transform на layout-обёртках
  • глобальные overflow: hidden

Практическая стратегия

  1. Проверка наличия stacking context
  2. Перенос tooltip в document.body
  3. Увеличение z-index
  4. Устранение overflow и transform
  5. Настройка Popper.js при необходимости

Ключевые моменты

  • z-index работает только внутри одного stacking context
  • transform — частая причина скрытых проблем
  • appendTo: document.body — основной инструмент решения
  • большие значения z-index не гарантируют успех
  • структура DOM важнее числового значения z-index