Позиционирование в CSS: частые ошибки новичков
Что реально делают position, z-index и transform, где новичок ломает вёрстку — и как за пять минут понять, почему блок «не хочет» вставать на место.

Вы поставили блоку position: absolute, и он улетел в угол экрана вместо того, чтобы встать под кнопкой. Или z-index: 9999 не поднял модалку над шапкой. Или position: fixed внезапно перестал прилипать к окну, когда вы добавили родителю красивую тень. Это не баги браузера — это три-четыре правила позиционирования, которые почти все узнают через боль. Разберём их так, чтобы вы больше не гадали. Актуально для всех современных браузеров (2024, CSS уровня 3).
Что нужно понимать до первой строки
У свойства position пять значений, и путаница между ними — источник большинства проблем:
static— значение по умолчанию. Элемент стоит в обычном потоке. Свойстваtop/left/right/bottomиz-indexна него не действуют.relative— элемент остаётся в потоке (место под него сохраняется), но его можно сдвинуть смещениями относительно исходной позиции.absolute— элемент выпадает из потока и позиционируется относительно ближайшего предка сpositionне равнымstatic.fixed— выпадает из потока и цепляется к окну браузера (viewport), обычно.sticky— гибрид: ведёт себя какrelative, пока не доскроллит до заданного порога, потом «прилипает».
Держите эту таблицу перед глазами — половина ошибок отсюда.
Ошибка 1. absolute без relative-родителя
Самый частый провал. Вы хотите поставить бейдж «-30%» в правый верхний угол карточки товара. Пишете:
.badge {
position: absolute;
top: 8px;
right: 8px;
}
Бейдж уезжает в угол всей страницы, а не карточки. Причина: absolute ищет ближайшего предка с позиционированием. Если такого нет — точкой отсчёта становится корневой блок (по сути окно). Карточке нужно задать position: relative, даже без смещений — так она станет «системой координат» для бейджа.
.card {
position: relative; /* без этого бейдж не привяжется к карточке */
}
.badge {
position: absolute;
top: 8px;
right: 8px;
}
Как проверить: бейдж встал в угол именно карточки. Если убрать relative у .card — бейдж снова улетит. Это самый быстрый способ убедиться, что связь работает.
Ошибка 2. z-index, который ничего не поднимает
Вы ставите модалке z-index: 9999, а её всё равно перекрывает шапка сайта. Тут две отдельные причины, и их путают.
Причина A: z-index без position
z-index работает только на позиционированных элементах — то есть relative, absolute, fixed или sticky (а также на flex- и grid-элементах). На static-элементе он игнорируется молча, без ошибки в консоли.
.modal {
position: fixed; /* без position z-index не сработает */
z-index: 100;
}
Причина B: контекст наложения
Даже с огромным z-index элемент не может вылезти выше «потолка» своего контекста наложения.1 Если родитель модалки сам создаёт такой контекст с низким z-index, потомок заперт внутри него — хоть 99999 ставьте.
z-index сравнивается только между соседями внутри одного контекста наложения. Число 9999 у вложенного элемента проигрывает числу 2 у чужого родителя, если эти родители на одном уровне.
Контекст наложения создают, среди прочего: любой элемент с position и заданным z-index; opacity меньше 1; transform, filter, will-change; position: fixed и sticky. Практический вывод: держите модалки, тултипы и выпадашки как можно ближе к <body>, а не глубоко внутри карточек с анимациями.
Ошибка 3. transform ломает fixed
Вы сделали кнопку «наверх» с position: fixed, она прилипла к углу окна. Потом добавили родительскому контейнеру плавное появление через transform: translateY(0) — и кнопка перестала быть фиксированной, начала скроллиться вместе со страницей.
Это не баг. По спецификации, если у предка задан transform, filter или perspective, он становится точкой отсчёта для потомков с fixed — они цепляются уже к нему, а не к окну. То же касается will-change: transform.
Как проверить и починить: в DevTools пройдитесь вверх по дереву предков фиксированного элемента и найдите тот, у кого в стилях есть transform. Варианты решения:
- вынести фиксированный элемент из этого поддерева ближе к
<body>; - убрать
transformу предка, если он там не обязателен; - анимировать не сам контейнер, а его содержимое.
Ошибка 4. sticky «не липнет»
position: sticky — источник тихого отчаяния, потому что он ломается без единой ошибки. Три частые причины, почему шапка таблицы или сайдбар отказываются прилипать:
- Не задан порог.
stickyтребует хотя бы одно изtop/bottom/left/right. Безtop: 0прилипать не к чему. - overflow у предка. Если любой родитель имеет
overflow: hidden,autoилиscroll, залипание отсчитывается внутри этого контейнера — и часто визуально просто не работает. - Родитель низкий. Элемент липнет только в пределах высоты родителя. Если родитель по высоте равен самому элементу, липнуть некуда — скроллить внутри нечего.
.table-head {
position: sticky;
top: 0; /* обязательный порог */
z-index: 2; /* чтобы строки не наезжали сверху */
}
Как проверить: откройте DevTools, поднимитесь по предкам и в разделе computed styles посмотрите overflow. Если у кого-то стоит не visible — вероятный виновник найден.
Ошибка 5. top и margin делают одно и то же по-разному
Новичок сдвигает блок то через margin-top, то через top, не понимая разницы. Для position: relative она важна:
margin-topдвигает сам элемент и сдвигает соседей — место в потоке меняется.topприrelativeдвигает только визуально: место под элементом остаётся на старом месте, и он может наехать на соседа.
Если вам нужно сдвинуть блок и раздвинуть соседей — это margin. Если нужно сместить чисто визуально, не трогая раскладку — top при relative. Путаница между ними даёт «дырки» в макете.
Быстрая таблица: чем управлять смещением
| Значение position | В потоке? | Точка отсчёта top/left | z-index работает |
|---|---|---|---|
| static | Да | — | Нет |
| relative | Да (место сохраняется) | Своя исходная позиция | Да |
| absolute | Нет | Ближайший позиционированный предок | Да |
| fixed | Нет | Окно (или предок с transform) | Да |
| sticky | Да, до порога | Ближайший скролл-контейнер | Да |
Что делать дальше
Когда позиционирование перестаёт «глючить», следующий шаг — понять, где оно вообще не нужно. Большинство раскладок, ради которых новички хватаются за absolute, чище решаются через Flexbox и Grid: центрирование, колонки, прижатый к низу футер. absolute оставьте для наложений — бейджей, тултипов, оверлеев.
В проде смотрите на две вещи: сколько контекстов наложения вы плодите (лишние transform и opacity на контейнерах усложняют отладку z-index) и не сломает ли fixed-элемент любой предок с анимацией. Раздел про изоляцию через свойство isolation: isolate — отдельная тема, здесь мы её только называем: оно позволяет создать контекст наложения без побочных смещений.
Тест и проверочная: Быстрые ответы с объяснением, почему остальные варианты неверны
Спросить за 10 ₽Источники: MDN — CSS position, MDN — Stacking context
Частые вопросы
Почему z-index не работает, хотя число большое?
Мой fixed-элемент скроллится вместе со страницей — это баг браузера?
У меня старый браузер, sticky точно сработает?
Что выбрать для центрирования блока — absolute или Flexbox?
Sticky не липнет, хотя я задал top. Что ещё проверить?
В чём разница между top при relative и margin-top?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.