Position

Схема позиционирования, привязка к краям и порядок наложения.

Опциональный модуль packages/utils/scss/_position.scss

Схема позиционирования

КлассCSSНазначение
.gr-staticposition: staticОбычный поток — значение по умолчанию
.gr-relativeposition: relativeСистема отсчёта для вложенных абсолютных элементов
.gr-absoluteposition: absoluteИзъят из потока, считается от ближайшего позиционированного предка
.gr-fixedposition: fixedПривязан к окну
.gr-stickyposition: stickyЛипнет к краю при прокрутке; требует хотя бы одного значения top/bottom

Привязка к краям

КлассCSS
.gr-inset-0inset: 0 — растянут по всем четырём краям
.gr-inset-autoinset: auto
.gr-inset-x-0inset-inline: 0 — оба края строки
.gr-inset-y-0inset-block: 0 — верх и низ
.gr-top-0, .gr-bottom-0Верхний / нижний край — 0
.gr-top-auto, .gr-bottom-autoТо же — auto
.gr-start-0, .gr-end-0inset-inline-start / inset-inline-end0
.gr-start-auto, .gr-end-autoТо же — auto

Боковая привязка — только логическая. Пара .gr-start-0 / .gr-end-0 держится начала и конца строки: в LTR это левый и правый край, в RTL они меняются местами сами. Физические .gr-left-* / .gr-right-* удалены вместе с остальными физическими сторонами; краю, который обязан быть буквальным — например кнопке «закрыть», держащей угол окна при любом направлении письма, — нужен свой CSS. Верх и низ (.gr-top-0, .gr-bottom-0) физические: блочная ось в RTL не разворачивается. Осевые .gr-inset-x-0 и .gr-inset-y-0 одинаковы в обоих направлениях — у них обе стороны равны нулю.

Порядок наложения

КлассCSS
.gr-z-0.gr-z-50z-index с шагом 10
.gr-z-autoz-index: auto

Шаг 10 оставляет место между ступенями: своё промежуточное значение вписывается обычным CSS, не трогая библиотеку.

Адаптивные варианты

Суффикс брейкпоинта есть только у самой схемы позиционирования: .gr-static-md, .gr-relative-lg, .gr-absolute-sm, .gr-fixed-xl, .gr-sticky-md. У inset и z-index адаптивных вариантов нет — на практике они не нужны.

<!-- в потоке на мобильном, липнет от 768px -->
<nav class="gr-static gr-sticky-md gr-top-0">…</nav>

Примеры использования

Ходовые задачи позиционирования, собранные из утилит этого модуля вместе с соседями по библиотеке: флекс- и грид-выравниванием ядра, фонами, прозрачностью и pointer-events. Новых классов ни один рецепт не требует — и в этом суть: несколько осевых утилит в сочетаниях дают больше вариантов, чем алфавит готовых точечных классов, по одному на каждый случай.

Бейдж в углу картинки

Классическая пара: родителю — .gr-relative, бейджу — .gr-absolute с привязкой .gr-top-0 .gr-end-0 и отступом от угла .gr-m-2. Родитель зауживается до картинки классом .gr-w-fit — без него блок растянулся бы на всю колонку, и бейдж повис бы у её дальнего края, а не у края картинки.

<div class="gr-relative gr-w-fit">
  <img class="gr-radius-4" src="chair.jpg" alt="Кресло из ротанга" width="280" height="200">
  <span class="gr-badge gr-badge-danger gr-absolute gr-top-0 gr-end-0 gr-m-2">−30%</span>
</div>
Бейдж — .gr-absolute .gr-top-0 .gr-end-0 внутри .gr-relative
Кресло из ротанга −30%

Размещение по девяти точкам

Сетка размещений, знакомая по большим библиотекам, — углы, середины сторон и центр. Углы держит привязка к краям. Всё, где участвует центр, делает слой .gr-absolute .gr-inset-0 с выравниванием: слой растянут по родителю, .gr-flex-center и .gr-flex-items-* ставят содержимое в нужную точку, а центр обеих осей ещё короче — .gr-grid-place-center. Слой перекрывал бы клики по картинке, поэтому ему — .gr-pointer-events-none; если внутри слоя есть кнопка или ссылка, клики ей возвращает .gr-pointer-events-auto.

<div class="gr-relative gr-w-fit">
  <img class="gr-radius-4" src="chair.jpg" alt="" width="360" height="257">

  <!-- углы: привязка к краям, слой не нужен -->
  <span class="gr-badge gr-absolute gr-top-0 gr-start-0 gr-m-2">top-start</span>
  <span class="gr-badge gr-absolute gr-top-0 gr-end-0 gr-m-2">top-end</span>
  <span class="gr-badge gr-absolute gr-bottom-0 gr-start-0 gr-m-2">bottom-start</span>
  <span class="gr-badge gr-absolute gr-bottom-0 gr-end-0 gr-m-2">bottom-end</span>

  <!-- середины сторон и центр: слой во весь родитель + выравнивание -->
  <div class="gr-absolute gr-inset-0 gr-flex gr-flex-center gr-flex-items-start gr-pointer-events-none">
    <span class="gr-badge gr-m-2">top-center</span>
  </div>
  <div class="gr-absolute gr-inset-0 gr-flex gr-flex-start gr-flex-items-center gr-pointer-events-none">
    <span class="gr-badge gr-m-2">center-start</span>
  </div>
  <div class="gr-absolute gr-inset-0 gr-grid-place-center gr-pointer-events-none">
    <span class="gr-badge">center</span>
  </div>
  <div class="gr-absolute gr-inset-0 gr-flex gr-flex-end gr-flex-items-center gr-pointer-events-none">
    <span class="gr-badge gr-m-2">center-end</span>
  </div>
  <div class="gr-absolute gr-inset-0 gr-flex gr-flex-center gr-flex-items-end gr-pointer-events-none">
    <span class="gr-badge gr-m-2">bottom-center</span>
  </div>
</div>
Девять точек: углы — краями, центры — слоями с выравниванием
top-start top-end bottom-start bottom-end
top-center
center-start
center
center-end
bottom-center

Растянутые размещения

Те же кирпичи в другой паре — «край + ось» вместо «край + край» — дают растянутые варианты. .gr-top-0 .gr-inset-x-0 — полоса во всю ширину по верхнему краю; .gr-bottom-0 .gr-inset-x-0 — классическая подпись к фотографии; .gr-start-0 или .gr-end-0 с .gr-inset-y-0 — колонка во всю высоту. Растяжение по всем четырём краям сразу — .gr-inset-0, уже знакомое по слоям выравнивания и затемнению. Специальных классов «полоса» или «колонка» в библиотеке нет, и они не нужны: край называет одна утилита, растяжение — другая.

<!-- полосы во всю ширину -->
<div class="gr-relative gr-w-fit">
  <img class="gr-radius-4" src="chair.jpg" alt="" width="280" height="200">
  <div class="gr-absolute gr-top-0 gr-inset-x-0 gr-bg-black gr-text-white gr-opacity-75 gr-p-2 gr-text-center">top + inset-x</div>
  <div class="gr-absolute gr-bottom-0 gr-inset-x-0 gr-bg-black gr-text-white gr-opacity-75 gr-p-2 gr-text-center">bottom + inset-x</div>
</div>

<!-- колонки во всю высоту -->
<div class="gr-relative gr-w-fit">
  <img class="gr-radius-4" src="chair.jpg" alt="" width="280" height="200">
  <div class="gr-absolute gr-start-0 gr-inset-y-0 gr-bg-black gr-text-white gr-opacity-75 gr-p-2 gr-flex gr-flex-items-center">start</div>
  <div class="gr-absolute gr-end-0 gr-inset-y-0 gr-bg-black gr-text-white gr-opacity-75 gr-p-2 gr-flex gr-flex-items-center">end</div>
</div>
Полосы — «край + .gr-inset-x-0», колонки — «край + .gr-inset-y-0»
top + inset-x
bottom + inset-x
start
end

Вынос за край

Элемент снаружи родителя или наполовину за его углом — счётчик непрочитанного на кнопке, ярлык сбоку от блока. В шкале краёв таких значений нет намеренно: случай редкий, и готовые классы под «−0.5rem» и «100%» раздували бы CSS. Их дают произвольные значения.gr-top-[-0.5rem] ставит верхний край выше родителя, а .gr-end-[100%] выносит элемент целиком за начало строки: в LTR его правый край встаёт на левый край родителя, в RTL картинка разворачивается сама. Вертикальный центр снаружи — тот же слой с выравниванием, что и в девяти точках, только вместо .gr-start-0 у него .gr-end-[100%].

<!-- счётчик наполовину за углом кнопки -->
<button class="gr-btn gr-relative" type="button">
  Входящие
  <span class="gr-badge gr-badge-count gr-badge-danger gr-absolute gr-top-[-0.5rem] gr-end-[-0.5rem]">3</span>
</button>

<!-- ярлыки целиком снаружи, по вертикальному центру краёв -->
<div class="gr-relative gr-w-fit">
  <img class="gr-radius-4" src="chair.jpg" alt="" width="280" height="200">
  <div class="gr-absolute gr-inset-y-0 gr-end-[100%] gr-flex gr-flex-items-center">
    <span class="gr-badge gr-m-2">снаружи</span>
  </div>
  <div class="gr-absolute gr-inset-y-0 gr-start-[100%] gr-flex gr-flex-items-center">
    <span class="gr-badge gr-m-2">снаружи</span>
  </div>
</div>
Счётчик за углом — отрицательные смещения; ярлыки снаружи — .gr-end-[100%] / .gr-start-[100%]
снаружи
снаружи

Липкая шапка

.gr-sticky липнет к ближайшему прокручиваемому предку. На странице это обычно само окно — шапке хватает .gr-sticky .gr-top-0 .gr-z-40 и непрозрачного фона; в демонстрации прокручивается рамка с .gr-overflow-y-auto, поэтому шапка останавливается у её верха. Без .gr-top-0 липкость не работает вовсе: браузеру не сказано, у какого края замереть.

<!-- прокручиваемая рамка — деталь демонстрации:
     на странице липкую шапку прокручивает само окно -->
<div class="gr-overflow-y-auto gr-border gr-radius-4" style="height: 180px">
  <header class="gr-sticky gr-top-0 gr-z-10 gr-bg-surface gr-border-b gr-p-2 gr-font-semibold">
    Журнал наблюдений
  </header>
  <p class="gr-p-2 gr-m-0">Запись первая…</p>
</div>
Прокрутите рамку — шапка с .gr-sticky .gr-top-0 остаётся у верха
Журнал наблюдений

Запись первая: прокрутка ещё не началась, шапка стоит там же, где стояла бы и без липкости.

Запись вторая: как только её верх дошёл бы до края рамки, шапка осталась на месте, а текст ушёл под неё.

Запись третья: непрозрачный фон шапке обязателен — иначе прокручиваемый текст просвечивал бы сквозь неё.

Запись четвёртая: .gr-z-10 держит шапку над содержимым, у которого случится свой контекст наложения.

Запись пятая: дальше прокручивать некуда, но шапка так и не сдвинулась.

Затемнение под модальным окном

Два слоя над содержимым, разведённые по ступеням: затемнению — .gr-z-40, окну — .gr-z-50. В демонстрации оба заперты в рамке классом .gr-absolute; на настоящей странице затемнение берёт .gr-fixed .gr-inset-0 — привязку к окну браузера. Собирать эту пару руками нужно редко: модальное окно на <dialog> красит подложку само через ::backdrop.

<!-- в демонстрации слои держит рамка с .gr-relative;
     на странице у затемнения вместо .gr-absolute — .gr-fixed -->
<div class="gr-relative">
  … содержимое страницы …
  <div class="gr-absolute gr-inset-0 gr-z-40 gr-bg-black gr-opacity-50"></div>
  <div class="gr-absolute gr-inset-0 gr-z-50 gr-grid-place-center">
    <div class="gr-bg-surface gr-radius-4 gr-shadow-lg gr-p-3">Окно</div>
  </div>
</div>
Затемнение .gr-z-40 над страницей, окно .gr-z-50 над затемнением

Страница под затемнением

Текст читается сквозь полупрозрачный слой, но заметно тускнеет.

Окно поверх затемнения