$ sudo teach IT

Когда элементы выходят из потока и начинают перекрывать друг друга, возникает вопрос: кто окажется сверху? Выпадающее меню должно лечь поверх контента, модальное окно — поверх всего, а подсказка — над кнопкой. Порядком слоёв по глубине управляет свойство z-index. Но иногда оно ведёт себя неожиданно: элемент с огромным z-index всё равно оказывается под другим. Причина — контекст наложения, и понимание этого механизма избавит вас от одной из самых загадочных проблем вёрстки.

После этого урока вы сможете управлять тем, какие элементы лежат выше, а какие ниже, и понимать, почему z-index иногда «не работает», — а значит, уверенно строить меню, модальные окна и подсказки.

Что уже умеем / что добавим

Вы умеете выводить элементы из потока через absolute, fixed и sticky — то есть создавать перекрытия. Теперь научимся управлять порядком этих перекрытий. В сквозном проекте мы наведём порядок в слоях: прилипающая шапка ляжет поверх контента, а бейдж и всплывающие элементы получат правильную глубину.

Что вы узнаете в рамках статьи

  • Как браузер по умолчанию решает, что лежит сверху.
  • Как свойство z-index управляет порядком слоёв.
  • Почему z-index работает только у позиционированных элементов.
  • Что такое контекст наложения и почему z-index иногда «не срабатывает».

Ключевые термины

  • z-index — числовой порядок элемента по глубине (по оси Z).
  • Ось Z — воображаемая ось «к пользователю»: чем больше z-index, тем ближе слой.
  • Позиционированный элемент — элемент с position не static.
  • Контекст наложения — независимая группа слоёв, внутри которой сравниваются z-index.

Порядок слоёв по умолчанию

Когда элементы перекрываются, браузер по умолчанию рисует их в порядке появления в коде: то, что идёт ниже в HTML, ложится поверх того, что выше. Позиционированные элементы (с position не static) при этом рисуются над обычными элементами потока.

То есть без всякого z-index уже действует логика: более поздний в коде и позиционированный элемент оказывается сверху. Часто этого достаточно. Но когда нужно явно управлять глубиной — например, поднять элемент над тем, что идёт после него, — в дело вступает z-index.

Свойство z-index

z-index задаёт числовой порядок элемента по глубине. Чем больше число, тем ближе к пользователю (выше) находится элемент:

.dropdown {
  position: absolute;
  z-index: 10;
}

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

Здесь .dropdown с z-index: 10 ляжет поверх .content с z-index: 1, независимо от их порядка в коде. Значения можно задавать любые целые — важно лишь их соотношение: больше — выше. Отрицательные значения (z-index: -1) уводят элемент под другие. На практике удобно оставлять между слоями запас (10, 20, 100), чтобы потом было куда вставить промежуточный слой.

z-index работает не всегда

Важнейшая деталь: z-index действует только на позиционированных элементах — тех, у кого position равен relative, absolute, fixed или sticky. У обычного элемента в потоке (position: static) свойство z-index просто игнорируется.

/* z-index не сработает: position по умолчанию static */
.box {
  z-index: 100;
}

/* А так сработает */
.box {
  position: relative;
  z-index: 100;
}

Это частая причина недоумения: элементу задали большой z-index, а он не поднимается. Первое, что нужно проверить, — задано ли ему position. Без позиционирования z-index бессилен. Запомните это правило — оно экономит массу времени при отладке.

Контекст наложения

Второй источник загадок — контекст наложения. Это независимая группа слоёв: z-index элементов сравниваются только внутри одного контекста, а не по всей странице. Новый контекст создаёт, например, позиционированный элемент с собственным z-index (а также некоторые современные свойства вроде opacity меньше 1 или transform).

Смысл в том, что дочерний элемент не может «выпрыгнуть» выше соседей своего родителя, даже с гигантским z-index. Если родитель находится ниже другого блока, все его дети — тоже ниже, каким бы большим ни был их z-index.

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

.card__badge {
  position: absolute;
  z-index: 999;
}

Здесь бейдж с z-index: 999 поднимется над содержимым своей карточки, но не над элементами, лежащими выше самой карточки в её контексте. Если модальное окно упорно оказывается под чем-то, несмотря на большой z-index, почти всегда виноват контекст наложения родителя. Решение — поднимать не сам элемент, а его контекст (родителя) или размещать модальные окна ближе к концу body.

Применяем в проекте

Наведём порядок в слоях сайта. Прилипающая шапка должна лежать поверх прокручиваемого контента, а бейдж — над содержимым карточки. Зададим им осмысленные z-index.

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background-color: #fff;
}

.card {
  position: relative;
}

.card__badge {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 2;
}

Обновите страницу и прокрутите её: прилипшая шапка уверенно ложится поверх любого контента, который под неё заезжает, а бейдж «Хит» остаётся над содержимым карточки. Мы задали шапке заметно больший z-index, чем внутренним элементам, — так она гарантированно наверху. Заметьте, что z-index мы задали только позиционированным элементам: без position он бы не сработал.

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

  • z-index без position.
    У staticэлемента свойство игнорируется; сначала задайте relative, absolute, fixed или sticky.
  • Борьба с контекстом наложения.
    Огромный z-index не поднимет элемент выше чужого контекста; поднимайте родителя или меняйте структуру.
  • Гонка значений.
    Ставить z-index: 99999 вслепую — плохая привычка; задавайте осмысленную шкалу слоёв с запасом.
  • Забыть про фон у верхнего слоя.
    Слой сверху без фона пропускает то, что под ним; для шапок и модальных окон задавайте фон.

Итоги

Поздравляем — вы завершили раздел о позиционировании и разобрались с самой коварной его частью! Теперь вы понимаете, как браузер укладывает слои по умолчанию, умеете управлять глубиной через z-index, помните, что он работает только у позиционированных элементов, и знаете про контекст наложения — причину большинства «необъяснимых» перекрытий. Слои вашего сайта выстроены осознанно: шапка сверху, бейджи на местах, ничего не налезает друг на друга случайно.

Отличная работа! Впереди — заключительный раздел курса, самый практичный: адаптивная вёрстка. Мы научимся делать сайт, который безупречно выглядит и на телефоне, и на десктопе, начиная с мета-тега viewport и подхода mobile-first.

На следующий урок