Освоение z-index в CSS
z-index — свойство, используемое для управления порядком расположения слоёв в документе. Элементы с большим значением z-index располагаются над элементами с меньшим значением. Подобно тому, как оси x и y на странице определяют расположение элементов по горизонтали и вертикали, z-index определяет, как они располагаются друг над другом по оси z.Наложение по умолчанию
При написании HTML элементы, расположенные ниже в документе, естественно, располагаются над элементами, расположенными выше.
<body>
  <header class="site-header"></header>
  <main class="site-content"></main>
  <footer class="site-footer"></footer>
</body>В этом фрагменте HTML footer будет располагаться поверх области содержимого main, которая будет располагаться поверх header, если все они будут расположены так, чтобы перекрывать друг друга.
Перекрытие элементов может осуществляться с помощью комбинации свойств position и смещения top, right, bottom и left.
Если для каждого из этих элементов установить position: absolute, то все они будут располагаться друг над другом. footer занимает последнее место в документе, поэтому по умолчанию он располагается поверх двух предыдущих элементов.
Если я использую свойства смещения, top и left, то мы можем увидеть порядок более наглядно.
.site-header, .site-content, .site-footer {
  position: absolute;
  width: 400px;
  padding: 20px;
}
.site-header {top: 0; left: 0;}
.site-content {top: 50px; left: 50px;}
.site-footer {top: 100px; left: 100px;}Контекст наложения
Хотя использование position: absolute и создаёт элементы, перекрывающие друг друга, мы ещё не создали так называемый контекст наложения (stacking context).
Контекст наложения создаётся любым из следующих способов:
- элемент с абсолютной или относительной позицией и значением z-index, отличным отauto
- элемент flexboxсо значениемz-index, отличным отauto
- элемент с opacityменьше1
- элемент, для которого установлено значение transform, отличное отnone
Наиболее распространённым способом создания и использования контекста наложения является первый пример в этом списке, поэтому остановимся на нем подробнее.
Возвращаясь к предыдущему примеру, отметим, что у нас есть три элемента, расположенные друг над другом, но в данный момент y них нет значения css свойства z-index.
CSS свойство z-index позволяет управлять порядком наложения.
Если я установлю z-index: 1 для footer, z-index: 2 для main и z-index: 3 для header, то порядок наложения по умолчанию может быть полностью изменён на противоположный.
На первый взгляд это выглядит довольно просто: чем выше z-index, тем выше располагается элемент — так, z-index: 9999 всегда будет находиться поверх z-index: 9. К сожалению, все несколько сложнее.
z-index в контексте наложения
<header class="site-header blue">header</header>
<main class="site-content green">content
  <div class="box yellow"></div>
</main>
<footer class="site-footer pink">footer</footer>Если я добавлю .box внутри контейнера site-content и расположу его чуть дальше правого нижнего угла, то мы увидим, что он находится над зелёным боксом и под розовым боксом.
.box {
  position: absolute;
  bottom: -25px;
  right: -25px;
  z-index: 4; /* won't work :( */
  width: 75px;
  height: 75px;
  border: 1px solid #000;
}
.site-header {top: 0; left: 0; z-index: -1;}
.site-content {top: 50px; left: 50px;}
.site-footer {top: 100px; left: 100px; z-index: 3;}Основываясь на наших знаниях о z-index, мы можем подумать, что для того, чтобы жёлтый блок оказался выше розового, мы можем просто установить большее значение z-index.
Если я установлю z-index: 4, что выше, чем z-index: 3, мы не увидим никаких изменений. Обычно люди пытаются принудительно сложить значения, задавая огромное число, например 9999, но это также не даёт никакого эффекта. Подобные значения z-index, встречающиеся в кодовой базе, являются своеобразным запахом кода
, поэтому старайтесь избегать их, если это возможно.
Причина, по которой мы не можем получить желаемый результат — жёлтый блок поверх розового, заключается в том, как ведёт себя z-индекс в контексте наложения.
Для того чтобы продемонстрировать это, давайте рассмотрим несколько более сложный пример, который я позаимствовал с сайта MDN.
<header class="site-header blue">
  <h1>Header</h1>
  <code>position: relative;<br/>
  z-index: 5;</code>
</header>
<main class="site-content pink">
  <div class="box1 yellow">
    <h1>Content box 1</h1>
    <code>position: relative;<br/>
    z-index: 6;</code>
  </div>
  <h1>Main content</h1>
  <code>position: absolute;<br/>
  z-index: 4;</code>
  <div class="box2 yellow">
    <h1>Content box 2</h1>
    <code>position: relative;<br/>
    z-index: 1;</code>
  </div>
  <div class="box3 yellow">
    <h1>Content box 3</h1>
    <code>position: absolute;<br/>
    z-index: 3;</code>
  </div>
</main>
<footer class="site-footer green">
  <h1>Footer</h1>
  <code>position: relative;<br/>
  z-index: 2;</code>
</footer>.blue {background: hsla(190,81%,67%,0.8); color: #1c1c1c;}
.purple {background: hsla(261,100%,75%,0.8);}
.green {background: hsla(84,76%,53%,0.8); color: #1c1c1c;}
.yellow {background: hsla(61,59%,66%,0.8); color: #1c1c1c;}
.pink {background: hsla(329,58%,52%,0.8);}
header, footer, main, div {
  position: relative;
  border: 1px dashed #000;
}
h1 {
  font: inherit;
  font-weight: bold;
}
.site-header, .site-footer {
  padding: 10px;
}
.site-header {
  z-index: 5;
  top: -30px;
  margin-bottom: 210px;
}
.site-footer {
  z-index: 2;
}
.site-content {
  z-index: 4;
  opacity: 1;
  position: absolute;
  top: 40px;
  left: 180px;
  width: 330px;
  padding: 40px 20px 20px;
}
.box1 {
  z-index: 6;
  margin-bottom: 15px;
  padding: 25px 10px 5px;
}
.box2 {
  z-index: 1;
  width: 400px;
  margin-top: 15px;
  padding: 5px 10px;
}
.box3 {
  z-index: 3;
  position: absolute;
  top: 20px;
  left: 180px;
  width: 150px;
  height: 250px;
  padding-top: 125px;
  text-align: center;
}Здесь у нас, как и прежде, есть header, footer и main, но внутри site-content у нас есть три блока, которые были позиционированы и им был присвоен z-index.
Рассмотрим сначала три основных контейнера — header, footer и main.
header имеет z-index 5, поэтому отображается расположенным над main контентом, который имеет z-index: 4. footer имеет z-index 2, поэтому отображается под main с более высоким z-index 4. Все хорошо? Хорошо.
С тремя блоками внутри контейнера main все становится несколько запутанным.
box1 имеет z-index 6, но оказывается под header с меньшим z-index 5.
box2 имеет z-index 1, но появляется над footer, который имеет более высокий z-index 2.
Итак, что происходит?
Все это объясняется тем, что все значения z-index определяются в контексте их родительского наложения. Поскольку родительский контейнер .site-content имеет более высокий z-index, чем footer, любые позиционированные элементы внутри .site-content оцениваются в этом контексте.
Хорошим способом представления порядка наложения в контексте наложения является представление о нем как о подпункте во вложенном упорядоченном списке.
- header: z-index: 5
- main: z-index: 4- box1: z-index: 4.6
- box2: z-index: 4.1
- box3: z-index: 4.3
 
- box1: 
- footer: z-index: 2
Поэтому, несмотря на то, что header имеет z-index: 5, а содержимое box1 имеет z-index: 6, порядок рендеринга равен 4,6, что все равно меньше, чем 5. Таким образом, содержимое box1 отображается ниже header.
Поначалу это немного запутанно, но с практикой все становится понятно!
z-index работает только для позиционированных элементов
Если необходимо управлять порядком расположения элементов, это можно сделать с помощью z-index. Однако z-index будет действовать только в том случае, если элемент также имеет position со значением absolute, relative или fixed.
Точное размещение элементов с помощью position отлично подходит для создания сложных макетов или интересных моделей пользовательского интерфейса, но часто возникает необходимость управлять порядком наложения элементов, не перемещая их с исходного места на странице.
В этом случае можно просто задать position: relative, но не указывать значения top, right, bottom или left. Элемент останется на прежнем месте на странице, поток документа не будет прерван, а значения z-index вступят в силу.
Вы можете использовать отрицательный z-index
Наложение элементов часто используется для создания сложных форм или компонентов пользовательского интерфейса. Часто это означает наслоение элементов друг на друга с постоянно увеличивающимся значением z-index. Чтобы поместить элемент на слой ниже другого, ему достаточно иметь меньшее значение z-index, причём это меньшее значение может быть и отрицательным.
Одна из областей, где это полезно, — использование псевдоэлементов и желание расположить их за содержимым родительского элемента.
В связи с тем, что контекст наложения работает, для элементов :before или :after требуется отрицательное значение z-index, если они должны располагаться за текстовым содержимым своего родительского элемента.
Посмотрите на следующий Codepen и поэкспериментируйте с различными значениями z-index.
Стратегия z-index
В заключение приведём простую стратегию применения z-index по всему проекту.
Ранее мы использовали одноразрядные инкременты для значений z-index, но что делать, если необходимо добавить новый элемент между двумя, имеющими значения z-index: 3 и z-index: 4? Пришлось бы менять множество значений — возможно, во всей кодовой базе, что могло бы стать проблематичным и привести к поломке CSS в других частях сайта.
Для задания z-index используйте шаг 100
При работе с z-index нередко можно встретить код, подобный этому:
.modal {
  z-index: 99999;
}На мой взгляд, это выглядит просто халтурно (и становится ещё хуже, если добавить к этому значению !important). Подобные значения часто являются симптомом того, что разработчик не понимает контекста наложения слоёв и пытается заставить один слой быть поверх другого.
Вместо того чтобы использовать произвольные числа типа 9999, 53 или 12, мы можем систематизировать шкалу z-index и привнести в процесс немного больше порядка. Это не потому, что у меня OCD разработчика. Честно.
Вместо того чтобы использовать для z-index увеличение на единицу, я использую увеличение на 100.
.layer-one {z-index: 100;}
.layer-two {z-index: 200;}
.layer-three {z-index: 300;}Я делаю это для того, чтобы сохранить порядок, а также для того, чтобы не забывать о множестве различных слоёв, используемых в проекте. Ещё одно преимущество заключается в том, что если необходимо добавить новый слой между двумя другими, то между ними можно выбрать 99 потенциальных значений.
При построении системы z-index этот ручной подход является достаточно надёжным, но его можно сделать более гибким, если использовать возможности такого препроцессора, как Sass/SCSS.