Позиционирование: выходим из потока
static, relative, absolute, fixed и sticky: от чего считаются координаты, как работает z-index и что такое контекст наложения.
Что ты научишься делать
- Поставишь бейдж точно на угол карточки
- Сделаешь шапку, которая прилипает к верху при прокрутке
- Разберёшься, почему z-index иногда «не работает»
#Пять значений position
Свойство position определяет, участвует ли элемент в обычном потоке и от чего отсчитываются его координаты top, right, bottom, left.
Важно: пока position равен static (а это значение по умолчанию), координаты не работают вообще. Написал top: 20px и ничего не произошло — почти всегда причина в этом.
#relative: сдвиг от самого себя
relative сдвигает элемент относительно его собственного исходного места. Важная деталь: место в потоке за ним сохраняется. Соседи ведут себя так, будто элемент никуда не двигался.
Но главная роль relative другая — он делает элемент точкой отсчёта для абсолютно спозиционированных детей. Именно так его используют в девяти случаях из десяти.
#absolute: от ближайшего позиционированного предка
absolute вынимает элемент из потока: соседи занимают его место, будто его нет. Координаты считаются от ближайшего предка, у которого position не static.
Если такого предка нет, точкой отсчёта становится вся страница — и элемент часто уезжает совсем не туда, куда ожидалось.
.card { position: relative; /* точка отсчёта */}.card__badge { position: absolute; /* считает координаты от .card */ top: 12px; right: 12px;}#fixed: привязка к окну
fixed тоже выходит из потока, но точка отсчёта у него всегда одна — окно браузера. При прокрутке элемент остаётся на месте.
Так делают кнопку «наверх», плавающую кнопку заказа, модальные окна.
#sticky: липкий заголовок
sticky — гибрид. Элемент ведёт себя как обычный, пока прокрутка не дойдёт до заданного края. Тогда он «прилипает» и едет вместе с экраном — но только пока виден его родитель.
Для работы обязательно нужны две вещи: значение sticky и хотя бы одна координата, например top: 0. Без координаты ничего не произойдёт.
#z-index и контекст наложения
Когда элементы накладываются, порядок определяет z-index: больше число — выше слой. Но работает он только у позиционированных элементов, то есть у тех, где position не static.
Дальше начинается самое интересное. z-index сравнивается не по всей странице, а внутри своего контекста наложения.
Новый контекст создаёт любой из этих признаков:
- позиционированный элемент с любым
z-index, кромеauto; opacityменьше единицы;transform,filter,backdrop-filter;will-change,isolation: isolate.
Так не надо
.modal {
z-index: 999999;
}
/* всё равно под шапкой */Число ничего не решает, если элемент лежит внутри чужого контекста наложения. Всё, что было создано родителем, сравнивается как единое целое.
Так надо
/* Выносим модалку в конец body,
чтобы у неё не было «дома»-родителя */
body > .modal {
position: fixed;
z-index: 100;
}Элемент на верхнем уровне сравнивается с остальными напрямую. Небольших значений хватает, когда контекст правильный.
Где спотыкаются почти все
У родителя забыт position: relative
Почему Абсолютный ребёнок начинает считать координаты от всей страницы и уезжает в её угол.
Как надо Всегда ставь пару: relative родителю, absolute ребёнку.
z-index задан элементу со static
Почему Свойство просто игнорируется: слои работают только для позиционированных элементов.
Как надо Добавь position: relative — этого достаточно, элемент даже не сдвинется.
position: sticky без координаты
Почему Браузеру не сказали, к какому краю прилипать. Ошибки нет, эффекта тоже.
Как надо Обязательно задай top, bottom, left или right.
Раскладка страницы сделана на absolute
Почему Абсолютные элементы не знают друг о друге и не подстраиваются под содержимое. При любом изменении текста вёрстка наезжает сама на себя.
Как надо Позиционирование — для точечных вещей: бейджей, подсказок, оверлеев. Раскладка — флексы и гриды.
- Позиционирование positioning
- Управление положением элемента через
positionи координаты. - Содержащий блок containing block
- Элемент, от которого считаются координаты абсолютно спозиционированного потомка.
- Выход из потока out of flow
- Элемент перестаёт занимать место, соседи ведут себя так, будто его нет.
- Контекст наложения stacking context
- Область, внутри которой сравниваются значения
z-index. - Липкое позиционирование sticky
- Элемент прилипает к краю при прокрутке, пока виден его родитель.
Проверь себя
Если такого предка нет, точкой отсчёта становится вся страница — отсюда и уехавшие бейджи.
Без <code>top</code> или другой координаты браузер не знает, к чему прилипать, и ничего не делает.
Слои сравниваются внутри контекста. Родитель с <code>opacity</code> или <code>transform</code> создаёт свой «дом», из которого ребёнку не выбраться.
<code>relative</code> только рисует элемент со смещением — его исходное место остаётся занятым.
Верных вариантов несколько.
Отступы к слоям отношения не имеют. А прозрачность, трансформации и позиционирование с явным <code>z-index</code> — создают.
Задание
Бейдж и липкая шапка
Две задачи в одном примере:
- поставь бейдж
.badgeв правый верхний угол карточки.card— используй пару relative/absolute; - сделай шапку
.headлипкой: при прокрутке списка она должна оставаться сверху.
Подсказка 1
Сначала родителю: .card { position: relative; }. Только после этого бейдж сможет считать координаты от карточки.
Подсказка 2
Липкая шапка — это два свойства сразу: .head { position: sticky; top: 0; }
Показать решение
<div class="box"> <h2 class="head">Меню на сегодня</h2> <article class="card"> <span class="badge">новинка</span> <h3>Раф на кокосовом</h3> <p>Мягкий и сладкий.</p> </article> <p>Эспрессо</p> <p>Американо</p> <p>Капучино</p> <p>Латте</p> <p>Флэт уайт</p> <p>Мокко</p> <p>Раф классический</p></div>* { box-sizing: border-box; }.box { height: 260px; overflow-y: auto; border: 1px solid #ddd; border-radius: 10px;}.head { position: sticky; top: 0; margin: 0; padding: 10px 14px; font-size: 16px; color: #fff; background: #6544e0;}.card { position: relative; margin: 14px; padding: 14px; background: #f5f5fa; border-radius: 12px;}.badge { position: absolute; top: -8px; right: -8px; padding: 4px 10px; font-size: 12px; color: #fff; background: #e0396a; border-radius: 999px;}.box p { margin: 0; padding: 10px 14px; border-bottom: 1px solid #eee;}