Позиционирование отвечает за элементы, живущие «поверх» обычного потока: выпадающие меню, бейджи на карточках, прилипающие шапки, модальные окна. Значений у position пять, и главное — понимать не сами значения, а относительно чего двигается элемент и что происходит с его местом в потоке.
static и relative: точка отсчёта
static — умолчание: элемент стоит в потоке, свойства top/right/bottom/left игнорируются.
relative — элемент остаётся в потоке (его место сохраняется!), но визуально сдвигается относительно своей исходной позиции:
.badge-icon { position: relative; top: -2px; } /* приподнять на 2px */
Сдвиги relative используются редко. Настоящая работа relative другая — быть якорем: позиционированный элемент становится точкой отсчёта для absolute-потомков. position: relative без top/left — самая частая строчка позиционирования в реальном коде.
absolute: относительно кого?
absolute делает две вещи: выдёргивает элемент из потока (место схлопывается, соседи его больше не видят) и позиционирует относительно ближайшего позиционированного предка — первого родителя вверх по дереву, у которого position не static.
.card { position: relative; } /* якорь */
.card .badge {
position: absolute;
top: 8px;
right: 8px; /* правый верхний угол КАРТОЧКИ */
}
Забыли relative у карточки — бейдж отсчитается от следующего позиционированного предка, а если таких нет — от страницы целиком, и улетит в правый верхний угол окна. «Absolute-элемент оказался не там» почти всегда означает «не тот якорь».
Полезная идиома — растянуть на весь якорь: inset: 0 (сокращение для top/right/bottom/left: 0) — оверлеи, подложки, кликабельные области поверх карточки. И заметьте: с заданными top+bottom или left+right absolute-элемент умеет растягиваться, а не только прибиваться к углу.
fixed и sticky: прибитые к экрану
fixed — как absolute, но точка отсчёта — окно браузера: элемент не прокручивается вместе со страницей. Шапки, плавающие кнопки, куки-баннеры. Из потока выдернут — контенту под фиксированной шапкой нужен компенсирующий отступ. Нюанс, который однажды сэкономит вам час: transform (а также filter) у предка превращает его в точку отсчёта для fixed — «фиксированный» элемент вдруг начинает скроллиться вместе с анимированным родителем.
sticky — гибрид: элемент едет в потоке как обычный, пока не доезжает до заданного порога — и прилипает:
.section-header {
position: sticky;
top: 0; /* порог обязателен — без него sticky не работает! */
}
В отличие от fixed, место в потоке сохраняется (ничего не перекрывается), и липнет элемент только в пределах своего родителя — доехал конец секции, заголовок уехал вместе с ней. Это и фича (заголовки таблиц, буквенные разделители списков), и источник главной загадки: «sticky не работает». Чек-лист: (1) задан ли top; (2) не является ли родитель ровно по высоте элемента — липнуть некуда; (3) нет ли на предках overflow: hidden/auto — они ломают прилипание.
z-index и контексты наложения
Кто кого перекрывает? Базово: позиционированные элементы выше обычных, при равенстве — позже в разметке значит выше. Точная настройка — z-index (работает только на позиционированных элементах и во flex/grid-детях).
Если бы всё было так просто, не существовало бы z-index: 999999. Настоящее правило: z-index сравнивается внутри одного контекста наложения (stacking context). Контекст создают: позиционированный элемент с z-index, opacity меньше 1, transform, filter, will-change и ещё десяток свойств. Внутри контекста дети сортируются между собой, но весь контекст целиком участвует во внешней сортировке как один слой:
<div class="dropdown-wrap"> <!-- opacity: 0.99 → создал контекст -->
<div class="dropdown" style="z-index: 9999">...</div>
</div>
<div class="toolbar" style="position: relative; z-index: 10">...</div>
Дропдаун с z-index 9999 окажется под тулбаром с z-index 10: его девять тысяч действуют только внутри родительского контекста, а контекст-родитель против тулбара — слой без числа. Лечится не наращиванием нулей, а поиском какой предок создал контекст (DevTools в помощь) — либо переносом элемента наверх дерева (порталы модалок в React существуют ровно поэтому).
Гигиена: держите короткую шкалу слоёв проекта (например, 10 — липкие шапки, 100 — дропдауны, 1000 — модалки, 10000 — тосты) в CSS-переменных — и войн z-index не будет.
Коротко
relative— сдвиг от своего места и, главное, якорь для absolute-детей. Место в потоке сохраняет.absolute— вне потока, отсчёт от ближайшего позиционированного предка. «Улетел» — ищи, где забыт relative.inset: 0— растянуть на весь якорь.fixed— прибит к окну; transform у предка ломает это.sticky— едет и прилипает в пределах родителя; нужен top, ломается overflow'ом предков.- z-index работает внутри контекста наложения; контексты создают opacity/transform/filter и позиционирование с z-index. «9999 не помогает» — виноват контекст предка.
- Шкала слоёв в переменных проекта — прививка от войн z-index.
Что почитать дальше
- Поток документа — что именно значит «выдернут из потока».
- Flexbox — следующая тема: раскладка, в которой позиционирование нужно всё реже.
- Анимации: CSS и оптимизации — transform, который создаёт контексты наложения, в своей главной роли.