Stacking contexts

Popper.js — это библиотека для создания динамических всплывающих элементов, таких как тултипы, поповеры и дропдауны, с учётом позиции относительно целевого элемента. Одним из ключевых аспектов, влияющих на отображение всплывающих элементов, является управление стековыми контекстами (stacking contexts) в CSS. Неправильное понимание или использование этих контекстов часто приводит к тому, что элементы оказываются перекрытыми другими DOM-элементами.

Основы stacking context

Stacking context — это концепция CSS, определяющая порядок наложения элементов по оси Z. Каждый stacking context образует отдельный слой, внутри которого элементы располагаются относительно друг друга. Внешние элементы не влияют на внутренние уровни этого контекста. Ключевые моменты:

  • Новый stacking context создаётся при наличии у элемента свойств:

    • position со значением relative, absolute, fixed или sticky вместе с z-index отличным от auto.
    • opacity меньше 1.
    • transform, filter, perspective, clip-path и другие CSS-свойства.
    • will-change, указывающее на потенциальное изменение transform или opacity.
  • Элементы внутри одного stacking context сортируются по z-index, но не могут “вылезти” за пределы своего контекста относительно других внешних контекстов.

Влияние stacking context на Popper.js

Popper.js позиционирует всплывающие элементы с помощью абсолютного или fixed позиционирования. Однако появление нового stacking context у родителя может изменить визуальный порядок наложения:

  • Поппер может оказаться за другими элементами, даже если имеет высокий z-index, если его родитель находится внутри stacking context с меньшим z-index.
  • Popper.js учитывает корень документа и может создавать порталы для вывода элементов в body для обхода ограничений родительских stacking contexts.

Работа с z-index и modifiers в Popper.js

Для корректного управления наложением элементов Popper.js предоставляет инструменты:

  1. modifiers — модификаторы Popper.js позволяют влиять на поведение позиции и отображения:

    • zIndex — модификатор, позволяющий задавать z-index для popper-элемента. Пример:
    const popperInstance = Popper.createPopper(referenceElement, popperElement, {
      modifiers: [
        {
          name: 'zIndex',
          enabled: true,
          phase: 'write',
          fn({ state }) {
            state.elements.popper.style.zIndex = '9999';
          },
        },
      ],
    });
    • preventOverflow и flip — косвенно помогают избежать конфликтов наложения, корректируя позицию поппера внутри доступной области.
  2. appendTo или порталы — в современных версиях Popper.js возможно выводить popper в document.body, что минимизирует влияние родительских stacking contexts.

Отладка проблем с наложением

При возникновении ситуации, когда popper скрыт за другими элементами, необходимо:

  1. Проверить CSS-родителей на наличие z-index и свойств, создающих новый stacking context (transform, opacity, filter).
  2. Определить, можно ли вынести popper в глобальный контекст (body) через appendTo.
  3. Настроить модификатор для явного задания z-index.
  4. Использовать getComputedStyle для проверки реального порядка слоёв.

Особенности при сложной верстке

  • При наличии nested stacking contexts (вложенных контекстов) popper может «потеряться» внутри контекста родителя. Решение — портализация или создание отдельного контейнера с контролируемым z-index.
  • Для fixed-попперов необходимо учитывать контекст viewport, так как transform на предках влияет на позиционирование.
  • При использовании CSS-фреймворков с глобальными стилями (tailwind, bootstrap) часто добавляются transform или perspective к родителям, создавая неожиданные stacking contexts.

Практические рекомендации

  • Всегда проверять, не создаёт ли родитель новый stacking context через transform или opacity.
  • Для элементов, которые должны быть всегда сверху, лучше использовать порталы и управлять z-index через модификатор.
  • Минимизировать глубокую вложенность popper-элементов внутри контейнеров с собственным z-index, особенно при fixed или sticky позиционировании.

Пример: Popper с порталом и управлением stacking context

import { createPopper } from '@popperjs/core';

const reference = document.querySelector('#button');
const popper = document.createElement('div');
popper.textContent = 'Tooltip';
document.body.appendChild(popper); // портал в body

createPopper(reference, popper, {
  placement: 'top',
  modifiers: [
    {
      name: 'zIndex',
      enabled: true,
      fn({ state }) {
        state.elements.popper.style.zIndex = '10000';
      },
    },
    {
      name: 'preventOverflow',
      options: { boundary: 'viewport' },
    },
  ],
});

В этом примере popper создаётся на уровне body, что полностью исключает влияние родительских stacking contexts и гарантирует корректное наложение над всеми элементами страницы.


Stacking contexts — критический аспект, определяющий видимость и корректное наложение всплывающих элементов в Popper.js. Понимание их механизмов и использование порталов с модификаторами z-index и preventOverflow позволяет создавать стабильные и предсказуемые интерфейсы.