Spacing

Margin и padding с шагом 0.25rem по разреженной шкале от 0 до 4rem. По сторонам, по осям, авто-маржины и адаптивные варианты у каждого свойства.

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

Принцип работы

Число в имени класса — это множитель базового шага 0.25rem. gr-m-4 = 4 × 0.25rem = 1rem, gr-p-16 = 16 × 0.25rem = 4rem.

Значения вне шкалы. Разовый отступ задаётся произвольным значением — gr-mt-[13px]. Правило собирает рантайм, поэтому без JS такой класс не работает: см. Произвольные значения.

Шкала

Ступеней двенадцать: 0 1 2 3 4 5 6 7 8 10 12 16. Мелкий шаг сохранён там, где важна точность, — до 2rem; дальше идут секционные отступы 2.5rem, 3rem и 4rem.

Классrempx при базе 16px
.gr-m-0 / .gr-p-000
.gr-m-1 / .gr-p-10.25rem4px
.gr-m-2 / .gr-p-20.5rem8px
.gr-m-3 / .gr-p-30.75rem12px
.gr-m-4 / .gr-p-41rem16px
.gr-m-5 / .gr-p-51.25rem20px
.gr-m-6 / .gr-p-61.5rem24px
.gr-m-7 / .gr-p-71.75rem28px
.gr-m-8 / .gr-p-82rem32px
.gr-m-10 / .gr-p-102.5rem40px
.gr-m-12 / .gr-p-123rem48px
.gr-m-16 / .gr-p-164rem64px

Изменение в 0.7.0. Раньше шкала шла подряд от 0 до 16 — 17 ступеней, 1193 класса, две трети всей библиотеки. Ступени 9, 11, 13, 14 и 15 убраны: за ними не стоит ни одного реального сценария, который не решался бы соседней ступенью. Нужное промежуточное значение задаётся обычным CSS.

Сокращения по сторонам

СуффиксCSS-свойствоПример
-ttop.gr-mt-4margin-top: 1rem
-bbottom.gr-mb-4margin-bottom: 1rem
-sначало строки.gr-ms-4margin-inline-start: 1rem
-eконец строки.gr-me-4margin-inline-end: 1rem
-xобе стороны строки.gr-mx-4margin-inline: 1rem
-ytop + bottom.gr-my-4margin-top + margin-bottom: 1rem

-s и -e — логические стороны строки: в LTR это левый и правый край, в RTL они меняются местами сами. Физических -l и -r в шкале больше нет — подробнее в разделе «Направление письма».

Полный список классов

Каждое свойство — по всем двенадцати ступеням шкалы:

.gr-m-{n}    /* margin */
.gr-p-{n}    /* padding */

.gr-mt-{n}   /* margin-top */
.gr-mb-{n}   /* margin-bottom */

.gr-pt-{n}   /* padding-top */
.gr-pb-{n}   /* padding-bottom */

.gr-ms-{n}   /* margin-inline-start */
.gr-me-{n}   /* margin-inline-end */

.gr-ps-{n}   /* padding-inline-start */
.gr-pe-{n}   /* padding-inline-end */

.gr-mx-{n}   /* margin-inline */
.gr-my-{n}   /* margin-top + margin-bottom */

.gr-px-{n}   /* padding-inline */
.gr-py-{n}   /* padding-top + padding-bottom */

/* где {n} — одно из: 0 1 2 3 4 5 6 7 8 10 12 16 */
Визуальная демонстрация

gr-p-4 (1rem)

padding со всех сторон

gr-p-6 (1.5rem)

padding 1.5rem

gr-p-8 (2rem)

padding 2rem

Авто-маржины

КлассCSSПрименение
.gr-mx-automargin-inline: autoЦентрирование блока
.gr-ms-automargin-inline-start: autoПрижать к концу строки (во flex)
.gr-me-automargin-inline-end: autoПрижать к началу строки (во flex)
ms-auto во flex-контейнере
Лого
ms-auto → прижат к концу строки

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

Суффикс брейкпоинта в конце имени класса: .gr-p-8-md. Правило применяется начиная с указанной ширины экрана. Адаптивные варианты есть у всех свойств шкалы — физических, логических и осевых — и у авто-маржинов .gr-mx-auto, .gr-ms-auto, .gr-me-auto.

Логические -s и -e адаптивны по всей шкале. .gr-ms-4-md и контейнерный .gr-ms-4-cmd существуют наравне с .gr-mt-4-md: односторонний отступ от брейкпоинта разворачивается в RTL так же, как и базовый.

Базовый классАдаптивный вариантПрименение
.gr-m-4.gr-m-4-mdmargin: 1rem от 768px
.gr-p-8.gr-p-8-lgpadding: 2rem от 1024px
.gr-mt-2.gr-mt-2-smmargin-top: 0.5rem от 640px
.gr-mb-6.gr-mb-6-xlmargin-bottom: 1.5rem от 1280px
.gr-mx-auto.gr-mx-auto-mdЦентрирование от 768px
.gr-py-4.gr-py-4-lgpadding по вертикали от 1024px
<div class="gr-p-4 gr-p-8-md gr-p-12-lg">
  Адаптивный padding: 1rem → 2rem → 3rem
</div>

Доступные суффиксы: -sm, -md, -lg, -xl.

Изменение в 0.7.0. Префиксная форма .gr-md:p-8 удалена. Раньше спейсинг был единственным модулем с двоеточием, а всё остальное — flex, grid, типографика, видимость — использовало суффикс. Теперь синтаксис в библиотеке один: .gr-md:p-8.gr-p-8-md.

Направление письма

Классы, работающие вдоль строки, — логические: они называют сторону строки, start и end, и разворачиваются сами, когда на элементе или предке стоит dir="rtl". Правил на [dir] в библиотеке нет: разворотом занимается браузер. Физические односторонние классы удалены — замена по таблице соответствия ниже.

Было (удалено)СталоСвойство
.gr-ml-{n} / .gr-mr-{n}.gr-ms-{n} / .gr-me-{n}margin-inline-start / -end
.gr-pl-{n} / .gr-pr-{n}.gr-ps-{n} / .gr-pe-{n}padding-inline-start / -end
.gr-ml-auto / .gr-mr-auto.gr-ms-auto / .gr-me-automargin-inline-start / -end: auto

Суффиксы брейкпоинтов переносятся как есть: .gr-ml-4-md.gr-ms-4-md, .gr-pl-2-clg.gr-ps-2-clg. Эта же таблица — в журнале изменений.

Осевые .gr-mx-* и .gr-px-* записаны логическими сокращениями margin-inline и padding-inline: обе стороны получают одно значение, поэтому направление письма результата не меняет, а объявлений выводится вдвое меньше.

Один и тот же класс в двух направлениях
.gr-ms-8 в LTR — отступ слева
‏.gr-ms-8 مع dir="rtl" — الهامش على اليمين

Физические односторонние классы удалены. Это единственное запланированное ломающее изменение библиотеки: класс вида .gr-ml-4 нельзя было молча переопределить в логический — разметка, выбравшая левую сторону буквально, сменила бы смысл. Классы удалены целиком, замена — по таблице выше. Верх и низ (.gr-mt-*, .gr-pb-*) остаются физическими: блочная ось в RTL не разворачивается. Стороне, нужной буквально из-за геометрии экрана, остаётся свой CSS.

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

Ходовые задачи вёрстки, в которых отступы работают вместе с соседями по библиотеке: флексом и гридом ядра, токенами .gr-gap-*, типографикой, цветами и компонентами ui. Классов «секция», «стек» или «медиа-объект» в библиотеке нет намеренно: каждый такой блок — это два-три отступа в правильных местах, и рецепты ниже показывают, в каких именно.

Ритм лендинга: секции и шапка секции

Страница-лендинг — стопка секций с одинаковым вертикальным ритмом: .gr-py-8 .gr-py-12-md даёт 2rem на телефоне и 3rem на десктопе — адаптивные варианты избавляют от медиазапросов в CSS. Секции разделяются не бордерами, а чередованием фонов: чётным — .gr-bg-surface, нечётные остаются на фоне страницы. Шапка секции — три ступени отступов по убыванию связности: надзаголовку .gr-mb-2, заголовку .gr-mb-3, а подводке — крупный .gr-mb-8, отделяющий шапку от содержимого.

<section class="gr-py-8 gr-py-12-md gr-px-4 gr-bg-surface">
  <p class="gr-text-xs gr-uppercase gr-font-semibold gr-text-primary gr-text-center gr-mb-2">Возможности</p>
  <h2 class="gr-text-center gr-mb-3">Всё нужное уже внутри</h2>
  <p class="gr-text-center gr-text-ink-secondary gr-mb-8">Подводка секции — один-два абзаца серым.</p>
  <div class="gr-grid-1 gr-grid-3-md gr-gap">
    <div class="gr-p-4">Карточка</div>
    <div class="gr-p-4">Карточка</div>
    <div class="gr-p-4">Карточка</div>
  </div>
</section>
Секция: ритм .gr-py-* + шапка на трёх ступенях .gr-mb-*

Возможности

Всё нужное уже внутри

Подводка секции — один-два абзаца серым.

Карточка

Карточка

Карточка

Центрированная колонка текста

Статья или подводка не должны растягиваться на всю ширину экрана — строка длиннее 60–75 символов тяжело читается. Пара «предел ширины + авто-поля» решает это двумя классами: .gr-max-w-[60ch] задаёт предел в символах текущего шрифта (произвольное значение — собирает рантайм), а .gr-mx-auto делит остаток ширины поровну между полями. Боковые .gr-px-4 не дают колонке прилипнуть к краям экрана, пока он ýже предела.

<article class="gr-max-w-[60ch] gr-mx-auto gr-px-4">
  <h1>Заголовок статьи</h1>
  <p>Текст читаемой ширины на любом экране…</p>
</article>
Колонка — .gr-max-w-[60ch] + .gr-mx-auto

Заголовок статьи

Колонка не растягивается шире шестидесяти символов: на широком экране остаток ширины уходит в равные поля слева и справа, на узком — колонку держат боковые отступы.

Стопка формы: gap контейнера вместо margin у каждого

Когда элементы однородны — поля формы, пункты списка, абзацы карточки, — интервал лучше отдать контейнеру: .gr-flex .gr-flex-col .gr-gap ставит одинаковый промежуток между всеми потомками, и у стопки нет ни «двойных» отступов на стыках, ни лишнего поля после последнего элемента. Мелкий ритм внутри группы «метка → поле → подсказка» уже держит .gr-field из ui — отступы руками ему не нужны. Парные поля встают в одну строку сеткой .gr-grid-2-sm с мелким зазором .gr-gap-sm, а кнопки дожимает вправо .gr-flex-end. Маржины при этом остаются для неоднородного ритма — как три ступени в шапке секции из первого рецепта.

<form class="gr-flex gr-flex-col gr-gap">
  <div class="gr-grid-1 gr-grid-2-sm gr-gap-sm">
    <div class="gr-field">
      <label class="gr-label" for="name">Имя</label>
      <input class="gr-input" id="name" type="text">
    </div>
    <div class="gr-field">
      <label class="gr-label" for="surname">Фамилия</label>
      <input class="gr-input" id="surname" type="text">
    </div>
  </div>
  <div class="gr-field">
    <label class="gr-label" for="mail">Почта</label>
    <input class="gr-input" id="mail" type="email">
    <p class="gr-hint">Пришлём ссылку для подтверждения.</p>
  </div>
  <div class="gr-flex gr-flex-end gr-gap-sm">
    <button class="gr-btn gr-btn-ghost" type="button">Отмена</button>
    <button class="gr-btn gr-btn-primary" type="submit">Сохранить</button>
  </div>
</form>
Ритм формы — .gr-flex-col .gr-gap, внутри групп — .gr-field

Пришлём ссылку для подтверждения.

Медиа-объект: фигура фиксированная, текст — резиновый

Аватар с текстом, иконка с описанием фичи, пункт уведомления — везде одна схема: .gr-flex .gr-gap-sm .gr-flex-items-start, слева фигура фиксированного размера (здесь — .gr-avatar из ui), справа текстовый блок. Текст получает .gr-min-w-0: без него флекс-потомок не сжимается ýже своего содержимого, и длинное слово распирает строку. Внутренний ритм текста — сброс .gr-m-0 плюс точечный .gr-mt-1, а стопку комментариев разделяет всё тот же .gr-flex-col .gr-gap из предыдущего рецепта.

<div class="gr-flex gr-flex-col gr-gap">
  <div class="gr-flex gr-gap-sm gr-flex-items-start">
    <span class="gr-avatar">АК</span>
    <div class="gr-min-w-0">
      <p class="gr-m-0 gr-font-semibold">Анна Ковалёва</p>
      <p class="gr-m-0 gr-mt-1 gr-text-sm gr-text-ink-secondary">Текст комментария…</p>
    </div>
  </div>
  <!-- следующий комментарий… -->
</div>
Медиа-объект — .gr-flex .gr-gap-sm, тексту — .gr-min-w-0
АК

Анна Ковалёва

Отступы контейнером — то, чего мне не хватало: стопка комментариев собралась без единого margin.

ДС

Даниил Соколов

А фигуре ничего и не нужно: размер держит сам аватар.

Навбар: авто-маржин дожимает группу до края

Шапка сайта — один флекс-ряд: лого, ссылки, действия. Между соседями работает .gr-gap, а всё, что после .gr-ms-auto, уезжает к концу строки (в LTR — к правому краю): авто-маржин съедает свободное место ряда с одной стороны. Это тот же приём, что .gr-mx-auto у колонки, только место отдаётся не поровну, а целиком одной стороне; класс логический, и в RTL шапка развернётся сама. .gr-flex-wrap — страховка для совсем узких экранов: когда ряду не хватает места, действия переносятся на новую строку, а не распирают шапку.

<header class="gr-flex gr-flex-wrap gr-flex-items-center gr-gap gr-px-4 gr-py-2">
  <span class="gr-font-bold">Griffin</span>
  <nav class="gr-flex gr-gap-sm">
    <a href="#">Документация</a>
    <a href="#">Примеры</a>
  </nav>
  <button class="gr-btn gr-btn-primary gr-btn-sm gr-ms-auto">Войти</button>
</header>
Шапка — .gr-gap между соседями, .gr-ms-auto у действий
Griffin

Равновысокие карточки с прижатым низом

В ряду карточек разного объёма кнопки должны стоять на одной горизонтали. Грид уже растягивает карточки до одной высоты — остаётся прижать низ. Класса .gr-mt-auto в библиотеке нет, и он не нужен: карточка становится колонкой .gr-flex .gr-flex-col, а описание получает .gr-flex-1 и забирает всю разницу высот себе — футер оказывается у нижнего края. Внутренние поля карточки — .gr-p-4, зазоры ряда — токен .gr-gap, который меняется темой вместе с плотностью.

<div class="gr-grid-1 gr-grid-3-md gr-gap">
  <div class="gr-flex gr-flex-col gr-p-4">
    <h3 class="gr-mb-2">Старт</h3>
    <p class="gr-flex-1 gr-mb-4">Короткое описание.</p>
    <button class="gr-btn gr-btn-primary">Выбрать</button>
  </div>
  <div class="gr-flex gr-flex-col gr-p-4">
    <h3 class="gr-mb-2">Команда</h3>
    <p class="gr-flex-1 gr-mb-4">Описание на три строки — а кнопка всё равно на одной линии с соседями.</p>
    <button class="gr-btn gr-btn-primary">Выбрать</button>
  </div>
</div>
Низ прижимает .gr-flex-1 у описания — не margin-top: auto

Старт

Короткое описание.

Команда

Описание подлиннее, на пару строк, — а кнопка всё равно стоит на одной линии с соседями.

Бизнес

Среднее описание в полторы строки.