3 minute read

Проблема: при смене новой группы хедера в теме Dawn (Shopify) даже идентичной, мобильное меню начинало показываться под hero блоком.


Это действительно выглядит как мистика, но у этого есть логичное объяснение.

Анализ проблемы

Смотрим на CSS в base.css:

/* section-header */
.section-header.shopify-section-group-header-group {
  z-index: 3;
}

.shopify-section-group-header-group {
  z-index: 4;
}

.section-header ~ .shopify-section-group-header-group {
  z-index: initial;
}

Что происходит

1. Когда используете {% sections 'header-group' %}:

  • Shopify генерирует CSS-класс .shopify-section-group-header-group
  • Этот класс получает z-index: 4 из CSS выше
  • Меню работает правильно

2. Когда меняете на {% sections 'custom-header-group' %}:

  • Shopify генерирует CSS-класс .shopify-section-group-custom-header-group
  • В CSS НЕТ стилей для этого класса!
  • z-index остается по умолчанию (auto/0)
  • Меню “проваливается” под hero

Решение

Добавьте CSS для вашей кастомной группы:

/* Добавьте в ваш кастомный CSS файл или в base.css */
.shopify-section-group-custom-header-group {
  z-index: 4;
}

.section-header.shopify-section-group-custom-header-group {
  z-index: 3;
}

Почему Shopify так делает

  • Shopify автоматически генерирует CSS-классы на основе имени группы секций
  • В базовых стилях прописаны только стандартные имена (header-group, footer-group)
  • Кастомные имена требуют дополнительных CSS-правил

Вывод

Проблема не в JSON-файле (он действительно идентичный), а в отсутствии CSS-стилей для кастомного имени группы секций. Добавьте CSS выше — и всё заработает!


Давайте разберем механику stacking contexts (контексты наложения) в CSS и почему именно z-index: 4 на родительском элементе решил проблему.

Анализ DOM структуры

Смотря на theme.liquid, видим:

<body>
  {% sections 'custom-header-group' %}
  <!-- header здесь -->
  <main>
    {{ content_for_layout }}
    <!-- hero здесь -->
  </main>
</body>

Что происходило БЕЗ CSS правил

1. Shopify генерирует контейнер для группы секций:

<div class="shopify-section-group-custom-header-group">
  <header class="header">
    <div id="menu-drawer">...</div>
  </header>
</div>

2. По умолчанию у этого контейнера:

  • position: static (обычный поток)
  • z-index: auto (нет собственного stacking context)

3. В то же время у hero-блока:

.banner__box {
  position: relative;
  z-index: 1;
}

Ключевой момент: Порядок в DOM + Stacking Context

Правило CSS: Если элементы находятся в разных stacking context или в обычном потоке, то элемент, который идёт позже в DOM, отображается поверх предыдущего.

В нашем случае:

  1. Header группа идёт первой в DOM
  2. Main с hero идёт после header группы
  3. У .banner__box есть position: relative + z-index: 1 → создаёт stacking context
  4. У header группы нет своего stacking context → она остаётся в обычном потоке

Результат: Hero отображается поверх header’а, даже если у menu-drawer поставить z-index: 999999, потому что menu-drawer “заперт” внутри обычного потока, а hero создал свой stacking context поверх.

Что происходит С CSS правилами

.shopify-section-group-custom-header-group {
  z-index: 4; /* Создаёт stacking context для всей группы */
}

Теперь:

  1. Header группа получает z-index: 4 → создаёт свой stacking context
  2. Hero имеет z-index: 1 в своём stacking context
  3. 4 > 1 → Header группа отображается поверх hero

Почему z-index: -1 для hero работал

.banner {
  z-index: -1;
}

Это “утапливало” hero ниже обычного потока (ниже z-index: 0), поэтому header снова становился видимым. Но это “костыль”, а не правильное решение.

Визуализация Stacking Contexts

БЕЗ CSS правил:

Document (z-index: auto)
├── Header группа (z-index: auto) ← обычный поток
│   └── menu-drawer (z-index: любой) ← "заперт" в обычном потоке
└── Main (z-index: auto)
    └── Hero (position: relative, z-index: 1) ← создаёт stacking context, идёт после в DOM

С CSS правилами:

Document (z-index: auto)
├── Header группа (z-index: 4) ← создаёт stacking context
│   └── menu-drawer (z-index: любой)
└── Main (z-index: auto)
    └── Hero (z-index: 1) ← в своём stacking context

Вывод

Проблема была не в том, что header “уходил под hero по z-index”, а в том, что:

  1. Header группа не имела своего stacking context и оставалась в обычном потоке
  2. Hero создавал stacking context и шёл после в DOM
  3. DOM порядок + отсутствие stacking context у header’а = hero поверх

Добавление z-index: 4 к header группе создало равноправный stacking context, где уже работают правила сравнения z-index между header’ом и hero.

Это классическая проблема CSS: элементы в разных stacking context не сравнивают свои z-index напрямую — важен z-index их родительских контекстов!