Урок 10. Теория: position, inset, слои, прокрутка

📁 Раздел: Раскладка ⏱️ Время изучения: ~45 мин 🎯 Сложность: Средняя

⚡ Кратко

Абсолютно позиционированный элемент отсчитывается от ближайшего позиционированного предка — то есть от того, у кого стоит relative, absolute, fixed или sticky. Забыли relative у родителя — элемент улетит к краю страницы. z-index работает только внутри своего контекста наложения, поэтому «поднять повыше» большим числом обычно не помогает.

Пять режимов позиционирования

КлассПоведениеТипичный случай
staticобычный поток, смещения игнорируютсязначение по умолчанию
relativeостаётся в потоке, но можно сместить и стать «якорем»родитель для абсолютных детей
absoluteвыпадает из потока, отсчёт от позиционированного предкабейдж, крестик, подсказка
fixedпривязан к окну, не двигается при прокруткемодальное окно, панель уведомлений
stickyв потоке, пока не дойдёт до границы — потом «прилипает»шапка таблицы, боковая навигация

Обычный поток — это очередь: каждый занимает своё место и толкает соседей. absolute — человек, который вышел из очереди и встал у стены: очередь его больше не замечает, а место он выбирает относительно стены. Стена — ближайший предок с relative. Если стены нет, «стеной» становится вся страница.

Пара relative + absolute

Это самая частая конструкция во всём уроке:

🔔 7
Карточка с крестиком
<div class="flex gap-6">
  <div class="relative inline-flex size-12 items-center justify-center rounded-lg bg-slate-100 text-lg">
    🔔
    <span class="absolute -top-1 -right-1 flex size-5 items-center justify-center rounded-full bg-rose-600 text-[10px] font-semibold text-white">7</span>
  </div>
  <div class="relative w-56 rounded-lg border border-slate-200 bg-white p-4 text-sm text-slate-700">
    Карточка с крестиком
    <button class="absolute top-2 right-2 rounded p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-600" aria-label="Закрыть">✕</button>
  </div>
</div>
<div class="relative">
  <span class="absolute -top-1 -right-1">7</span>
</div>

inset: короткая запись смещений

  • inset-0 — все четыре стороны в ноль, элемент растянут на родителя;
  • inset-x-4 / inset-y-2 — по горизонтали / вертикали;
  • top-0, right-4, -bottom-2 — отдельные стороны;
  • start-0 / end-0 — логические стороны для RTL.
inset-0 — затемнение, inset-x-4 bottom-4 — подпись
<div class="relative h-32 overflow-hidden rounded-lg bg-linear-to-br from-sky-400 to-indigo-500">
  <div class="absolute inset-0 bg-slate-900/40"></div>
  <div class="absolute inset-x-4 bottom-4 text-sm font-medium text-white">inset-0 — затемнение, inset-x-4 bottom-4 — подпись</div>
</div>

sticky: липкие элементы

sticky — гибрид: элемент ведёт себя как обычный, пока не достигнет заданной границы, после чего фиксируется. Обязательно указывать, к чему липнуть: sticky top-0.

Липкая шапка — прокрутите блок
  • Строка 1
  • Строка 2
  • Строка 3
  • Строка 4
  • Строка 5
  • Строка 6
  • Строка 7
  • Строка 8
<div class="h-48 overflow-y-auto rounded-lg border border-slate-200">
  <div class="sticky top-0 border-b border-slate-200 bg-white/95 px-4 py-2 text-xs font-semibold text-slate-900">Липкая шапка — прокрутите блок</div>
  <ul class="divide-y divide-slate-100 text-sm text-slate-700">
    <li class="px-4 py-2">Строка 1</li>
    <li class="px-4 py-2">Строка 2</li>
    <li class="px-4 py-2">Строка 3</li>
    <li class="px-4 py-2">Строка 4</li>
    <li class="px-4 py-2">Строка 5</li>
    <li class="px-4 py-2">Строка 6</li>
    <li class="px-4 py-2">Строка 7</li>
    <li class="px-4 py-2">Строка 8</li>
  </ul>
</div>
Липкий элемент не работает, если у любого предка стоит overflow: hidden или overflow: auto — прокрутка «съедается» этим предком. Это причина 90% случаев «sticky не липнет».

Слои: z-index и контекст наложения

Классы z-0, z-10, z-20, …, z-50 задают порядок наложения. Но работает он только среди «соседей» внутри одного контекста наложения.

Новый контекст создаёт не только z-index у позиционированного элемента, но и transform, opacity меньше 1, filter, will-change. Поэтому z-50 внутри блока с opacity-90 никогда не окажется выше соседнего блока с z-10.

<!-- родитель создаёт контекст: transform -->
<div class="translate-y-0">
  <div class="z-50">Я не смогу перекрыть соседа родителя</div>
</div>
<div class="relative z-10">Я выше, потому что контекст у нас общий</div>
Что делать: не наращивать числа, а вынести элемент выше по дереву — например, отрисовать всплывающее окно в конце body. Именно так работают порталы в React и компоненты Dialog из shadcn/ui (урок 21). Класс isolate создаёт контекст намеренно — чтобы «запереть» слои внутри блока.

overflow: что делать с лишним

КлассПоведениеКогда
overflow-hiddenобрезатьскругления, обложки, анимации
overflow-autoпрокрутка при необходимостисписки, таблицы
overflow-x-autoгоризонтальная прокруткаширокие таблицы на мобильных
overflow-y-scrollвсегда показывать полосучтобы контент не «прыгал»
overflow-visibleпоказывать выходящееподсказки, выпадающие меню
overscroll-containне «пробрасывать» прокрутку наружумодальные окна, боковые панели
КлиентДоговорМенеджерСумма
ООО «Ромашка»2026-114Смирнова120 000
ИП Ковалёв2026-115Титов64 500
<div class="max-w-sm overflow-x-auto rounded-lg border border-slate-200">
  <table class="w-max text-sm">
    <thead class="bg-slate-50 text-xs text-slate-500">
      <tr><th class="px-4 py-2 text-left">Клиент</th><th class="px-4 py-2 text-left">Договор</th><th class="px-4 py-2 text-left">Менеджер</th><th class="px-4 py-2 text-right">Сумма</th></tr>
    </thead>
    <tbody class="divide-y divide-slate-100 text-slate-700">
      <tr><td class="px-4 py-2">ООО «Ромашка»</td><td class="px-4 py-2">2026-114</td><td class="px-4 py-2">Смирнова</td><td class="px-4 py-2 text-right tabular-nums">120 000</td></tr>
      <tr><td class="px-4 py-2">ИП Ковалёв</td><td class="px-4 py-2">2026-115</td><td class="px-4 py-2">Титов</td><td class="px-4 py-2 text-right tabular-nums">64 500</td></tr>
    </tbody>
  </table>
</div>

Таблица шире контейнера — и это нормально: overflow-x-auto даёт прокрутить её, не ломая раскладку страницы.