Значок «хит» должен сидеть в углу карточки, а улетает в угол страницы. Шапку хочется закрепить сверху, а она уезжает при прокрутке. Оба случая решает одно свойство — position, если знать, от чего каждое его значение отсчитывает top, right, bottom и left.
position выбирает, как элемент встаёт на странице: static — в обычном потоке, relative — со сдвигом от своего места, absolute — от ближайшего предка с position, fixed — от окна, sticky — как обычный, пока прокрутка не доведёт его до края. Разберём все пять на одной странице меню кофейни.
Пример: значок, липкая шапка и кнопка в углу
Открой пример в песочнице HTML/CSS ссылкой под вторым блоком: разметка попадёт во вкладку index.html, стили — в styles.css. Прокрути результат: шапка останется наверху, кнопка «позвонить» — в правом нижнем углу, а значок «хит» уедет вместе со своей карточкой.
HTML:
<header class="top">Кофейня «Зерно» · меню</header>
<main class="menu">
<article class="item">
<span class="badge">хит</span>
<h2>Капучино</h2>
<p>Двойной эспрессо и молоко.</p>
</article>
<article class="item">
<h2>Раф</h2>
<p>Сливки, ваниль и эспрессо.</p>
</article>
<article class="item">
<h2>Какао</h2>
<p>Горячий шоколад с корицей.</p>
</article>
<article class="item">
<h2>Чай</h2>
<p>Чёрный, зелёный или травяной.</p>
</article>
</main>
<a class="call" href="#">позвонить</a>
CSS:
body {
margin: 0;
font-family: system-ui, sans-serif;
}
.top {
position: sticky;
top: 0;
z-index: 1;
padding: 0.75rem 1rem;
background: #3b2a20;
color: #fff8ef;
}
.menu {
display: grid;
gap: 1rem;
padding: 1rem;
}
.item {
position: relative;
min-height: 140px;
padding: 1rem;
border: 1px solid #e3d6c8;
border-radius: 0.75rem;
background: #ffffff;
}
.badge {
position: absolute;
top: 0.75rem;
right: 0.75rem;
padding: 0.125rem 0.5rem;
border-radius: 999px;
background: #ff006e;
color: #ffffff;
font-size: 0.875rem;
}
.call {
position: fixed;
right: 1rem;
bottom: 1rem;
padding: 0.75rem 1.25rem;
border-radius: 999px;
background: #2e7d32;
color: #ffffff;
text-decoration: none;
}
У каждого значения своя точка отсчёта
absolute отсчитывает от ближайшего предка с position, fixed — от окна, а sticky ведёт себя как обычный блок, пока прокрутка не доведёт его до порога из top.static и relative: обычный поток и сдвиг
static. Значение по умолчанию: элемент стоит в потоке, где ему положено, а top и left на него не действуют.
relative. Элемент остаётся в потоке, но его можно сдвинуть от своего места: top: 10px опустит его на 10 пикселей. Место, которое он занимал, при этом остаётся пустым, соседи не сдвигаются. Чаще relative ставят без всяких сдвигов — ради опоры для absolute, как у карточки в примере.
absolute: от ближайшего предка с position
Элемент с position: absolute выпадает из потока: остальные элементы ведут себя так, будто его нет. top, right, bottom и left отсчитываются от ближайшего предка, у которого position не static. У значка «хит» это карточка с position: relative, поэтому значок сидит в 12 пикселях от её верхнего и правого края, считая изнутри рамки, и ездит вместе с ней.
Если такого предка нет, absolute отсчитывает от начального блока страницы — прямоугольника размером с окно в самом верху документа. Тогда значок окажется в правом верхнем углу страницы, далеко от своей карточки.
Растянуть элемент на всего родителя помогает inset: 0 — это сокращение для top, right, bottom и left со значением 0. Так делают затемнение поверх картинки или кликабельную область на всю карточку.
fixed и sticky: от окна
fixed. Отсчитывает от окна браузера и не двигается при прокрутке: так сделаны кнопка «позвонить», чат в углу, всплывающее окно. Из потока он выпадает так же, как absolute.
sticky. Гибрид: пока элемент в поле зрения, он стоит в потоке как обычный, а когда прокрутка доводит его до порога, держится на месте. Порог задают через top, bottom, left или right: у шапки top: 0, то есть верхний край окна. Держится он только в пределах своего родителя: когда родитель уедет, уедет и sticky.
z-index: кто кого перекрывает
Элементы с position рисуются поверх обычных, а между собой — в порядке разметки: кто ниже в HTML, тот и сверху. Карточки в примере тоже с position, и они идут после шапки, поэтому при прокрутке без z-index наехали бы на неё. z-index: 1 у шапки поднимает её выше.
У z-index есть ловушка — контекст наложения. Если у родителя есть position и свой z-index, его дети сравниваются только друг с другом: даже z-index: 9999 не поднимет ребёнка выше соседей его родителя. Если элемент упорно остаётся снизу, смотри на z-index его предков.
Шпаргалка по position
| Значение | В потоке? | От чего считает top и left | Для чего |
|---|---|---|---|
static | да | не считает | обычный поток, по умолчанию |
relative | да, место остаётся | от своего места | небольшой сдвиг, опора для absolute |
absolute | нет | от ближайшего предка с position | значки, подписи, затемнение поверх |
fixed | нет | от окна | кнопка в углу, чат, всплывающее окно |
sticky | да | от края прокрутки, после порога | липкая шапка, заголовки таблиц |
Почему не работает: частые ошибки
absolute улетает в угол страницы. У родителя нет position. Добавь ему position: relative.
sticky не прилипает. Чаще всего не задан порог, top: 0. Вторая причина — у предка overflow: hidden или auto: тогда элемент прилипает внутри этого предка, а он сам не прокручивается.
fixed прокручивается вместе со страницей. Если у предка есть transform или filter, fixed считает уже от этого предка, а не от окна. Перенеси элемент выше по разметке.
z-index не работает. У элемента нет position, и z-index на него не действует. Исключение — элементы внутри Flexbox и Grid: там z-index работает и без position.
Три правки, чтобы закрепить
Открой пример в песочнице и перед каждой правкой предскажи результат:
- Убери у
.itemстрокуposition: relative. Значок «хит» пропадёт. Он улетел в правый верхний угол страницы, потому что отсчитывать его больше не от чего, и спрятался под шапкой: у неёz-index: 1. - Убери у
.topстрокуtop: 0. Шапка перестанет прилипать и уедет при прокрутке: без порогаstickyне знает, где остановиться. - Убери у
.topстрокуz-index: 1и прокрути результат. Карточки поедут поверх шапки: они тоже сpositionи стоят в разметке ниже.
Потренировать absolute без подсказок можно в игре «Повтори макет»: на уровне «Глаза» зрачки ставят как раз внутри глаза с position: relative. А выравнивать без позиционирования помогают статьи про выравнивание по центру и Flexbox.