Справочник ядра

Переменные, брейкпоинты, grid-парсер, flex- и grid-утилиты, темы и стратегии оформления — полный список того, что даёт ядро.

griffincss-core полный список

CSS-переменные / CSS Custom Properties

ПеременнаяЗначениеОписание
--gr-gap0.75rem1rem с mdБазовый отступ для grid/flex
--gr-gap-sm0.375rem0.5rem с mdМалый отступ
--gr-gap-lg1.25rem2rem с mdБольшой отступ
--gr-max-width1440pxМакс. ширина контейнера
--gr-font-sanssystem-ui, -apple-system, sans-serifСемейство sans-serif
--gr-font-serifGeorgia, Cambria, serifСемейство serif
--gr-font-monoSFMono-Regular, Menlo, Consolas, monospaceСемейство monospace
--gr-font-size-base1remБазовый размер шрифта
--gr-hsl-gray-50--gr-hsl-gray-9000, 0%, 96%0, 0%, 5%HSL-токены нейтральной шкалы
--gr-hsl-primary / success / warning / danger / info220,80%,50% / …HSL-токены акцентных цветов
--gr-hsl-surface-0-2, --gr-hsl-surface-sunken, --gr-hsl-surface-overlay0, 0%, 100%Шкала поверхностей — её меняет тема
--gr-hsl-ink-0--gr-hsl-ink-30, 0%, 5%Шкала чернил — её меняет тема
--gr-hsl-accent-base / -base-hover220, 80%, 45% / 220, 80%, 36%; тёмная — 220, 85%, 68% / 78%Якорь бренда: повседневный акцент. --gr-hsl-accent, -accent-hover, -link и -link-hover выводятся из него через var(). Бренд задаётся этой парой, а не --gr-hsl-accent напрямую — иначе режим для слабовидящих перестанет переводить акцент; рецепт — «Темы»
--gr-hsl-accent-max / -max-hover220, 100%, 35% / 25%; тёмная — 220, 100%, 80% / 90%Якорь режима для слабовидящих: акцент и ссылки в режиме. Не ниже 7 : 1 на --gr-hsl-surface-max
--gr-hsl-ink-max / -strong / -mid, --gr-hsl-surface-max / -near, --gr-hsl-visited-max0, 0%, 0% … — зеркальные в тёмнойОстальные якоря режима: куда уходят чернила, поверхности и посещённая ссылка. Чернила на акценте в режиме — surface-max: ось двигает пару целиком
--gr-hsl-accent-soft / -soft-hover, --gr-hsl-on-accent-soft220, 65%, 62% / 54%, чернила 220, 60%, 12%Якорь воздушного стиля и чернила на пастели — приезжают с griffincss-styles.css; не ниже 4,5 : 1 между собой. У журнального стиля своя пара — --gr-hsl-accent-press / -press-hover
--gr-color-bghsl(var(--gr-hsl-surface-0))Цвет фона страницы
--gr-color-surface / -raised / -sunkenhsl(var(--gr-hsl-surface-*))Карточка, приподнятая, утопленная поверхности
--gr-color-surface-overlayhsl(var(--gr-hsl-surface-overlay))Поверхность всплывающего слоя: меню, окно, выдвижная панель. В светлой теме белый лист, в тёмной — серый светлее фона: «выше страницы» в двух темах означает разное, одной ступенью -raised они не покрываются
--gr-color-texthsl(var(--gr-hsl-ink-0))Основной цвет текста
--gr-color-text-secondary / -muted / -disabledhsl(var(--gr-hsl-ink-*))Вторичный, приглушённый, неактивный текст
--gr-color-border / -stronghsl(var(--gr-hsl-border*))Разделитель и граница элемента управления
--gr-color-link / -hover / -visitedhsl(var(--gr-hsl-link*))Цвета ссылок
--gr-color-accent / -hover / --gr-color-on-accenthsl(var(--gr-hsl-accent*))Акцент и текст на нём
--gr-color-focushsl(var(--gr-hsl-focus))Кольцо фокуса
--gr-color-success / warning / danger / infohsl(var(--gr-hsl-status-*))Статусы — значения, безопасные для текста
--gr-color-warning-surface / --gr-color-on-warninghsl(var(--gr-hsl-status-warning-surface)) / hsl(var(--gr-hsl-on-status-warning))Заливка предупреждения и чернила на ней: единственный статус, чей текстовый цвет нельзя использовать как фон
--gr-color-ratinghsl(var(--gr-hsl-status-warning-surface))Цвет звёзд и всего, что изображает оценку: ряд .gr-rating, ввод оценки, полосы распределения, значок у подписи. Отдельный токен, потому что текстовый --gr-color-warning затемнён ради контраста и рядом со звёздами читается коричневым
--gr-border-width1px2px в режиме для слабовидящихШирина границы по умолчанию
--gr-focus-width2px3px в режимеТолщина кольца фокуса
--gr-leading-base1.51.7 в режимеИнтерлиньяж body
--gr-tracking-basenormal0.02em в режимеТрекинг body
--gr-a11y-scale1.25Во сколько раз режим увеличивает кегль
--gr-radius0.5remСкругление элементов управления
--gr-radius-containerне объявлен; читается как var(--gr-radius-container, var(--gr-radius))Скругление контейнеров — карточки, окна, меню, сообщения. Без объявления равен --gr-radius, и запасное значение считается на самом элементе: островок стиля с другим --gr-radius получает свои углы. Объявите его в своём CSS, если контейнер должен скругляться иначе — рецепт «радиус + отступ» на странице темы
--gr-transition0.2s easeПереходы
--gr-shadow-color0, 0%, 0%HSL-компоненты цвета тени — общие для всех ступеней
--gr-shadow-strength12.6 в тёмной темеМножитель плотности тени; геометрия не меняется
--gr-shadow-xs--gr-shadow-2xl0 1px 2px …Шесть ступеней тени
--gr-shadow-innerinset 0 2px 4px …Внутренняя тень
--gr-control-height / -sm / -lg2.5rem / 2rem / 3remВысота кнопки и поля; в rem, поэтому растёт вместе с режимом для слабовидящих
--gr-control-padding-x0.875remГоризонтальные поля элементов управления
--gr-control-font-size1remКегль элементов управления
--gr-ui-gap0.5remВнутренний зазор компонента — между иконкой и текстом
--gr-overlayhsl(var(--gr-shadow-color), 55%)Подложка модалки и ящика
--gr-z-dropdown / -modal / -toast1000 / 1100 / 1200Порядок наложения попапов
--gr-bp-sm640pxБрейкпоинт sm (справочно; собирается из карты $gr-breakpoints)
--gr-bp-md768pxБрейкпоинт md
--gr-bp-lg1024pxБрейкпоинт lg
--gr-bp-xl1280pxБрейкпоинт xl

Grid-парсер / Grid Parser

Уникальная фича Griffincss — описание сложных сеток через компактный строковый формат в data-gr-layout. JS-рантайм (griffincss.js) автоматически обходит DOM, парсит все data-gr-layout атрибуты и генерирует CSS Grid правила в <style> на лету. Никакой прекомпиляции раскладок не нужно.

Формат: "a3b4-c2d5" где:

Пример / Example:

<!-- Подключаем CSS + JS -->
<link rel="stylesheet" href="griffincss-core/dist/griffincss-core.css">
<script src="griffincss-core/dist/griffincss.js"></script>

<!-- JS-рантайм автоматически распарсит и сгенерирует grid -->
<div data-gr-layout="a3b4-c2d5">
  <div class="gr-area-a">A — 3 колонки</div>
  <div class="gr-area-b">B — 4 колонки</div>
  <div class="gr-area-c">C — 2 колонки</div>
  <div class="gr-area-d">D — 5 колонок</div>
</div>

Рантайм вешает на контейнер класс .gr-l-<хеш> и пишет правила под него — атрибуты остаются только источником данных:

.gr-l-k7gi9w {
  display: grid;
  grid-template-areas: "a a a b b b b" "c c d d d d d";
  grid-template-columns: var(--gr-l-cols, repeat(7, minmax(0, 1fr)));
  grid-template-rows: var(--gr-l-rows, repeat(2, auto));
}

Ширины дорожек задаются двумя свойствами — строка раскладки остаётся про области:

СвойствоПо умолчаниюЧто делает
--gr-l-colsrepeat(N, minmax(0, 1fr))Ширины колонок раскладки. Число треков обязано совпадать со строкой: недостающие браузер дорисует неявными по auto, лишние останутся пустыми — это видно в раскладке и в DevTools
--gr-l-rowsrepeat(M, auto)Высоты рядов раскладки. То же условие по числу треков

Свойства читает само правило раскладки, поэтому работают обе дорожки — и рантайм, и миксин gr-grid-layout(). Подробнее — «Ширины треков».

Хеш считается по набору раскладок элемента (базовой и всех адаптивных), поэтому два контейнера с одинаковым data-gr-layout, но разной адаптивностью не мешают друг другу.

Раскладка во время загрузки страницы / Streaming layout

Рантайм не ждёт DOMContentLoaded. Если скрипт подключён синхронно в <head>, он поднимает наблюдателя парсинга и раскладывает каждый контейнер в тот момент, когда парсер его создал: data-gr-layout приходит вместе с открывающим тегом, то есть раньше, чем контейнер наполнится детьми. На медленной сети страница проявляется сверху вниз, а не остаётся невидимой до последнего байта.

Дети подхватываются следующими порциями: ребёнок без gr-area-* получает первое свободное имя сразу, а явный класс в разметке всегда сильнее выданного рантаймом — контейнер пересчитывается, и итог совпадает с тем, что дал бы разбор целиком.

<head>
  <link rel="stylesheet" href="griffincss-core/dist/griffincss-core.css">
  <!-- синхронно и после CSS: так рантайм видит и брейкпоинты, и весь <body> -->
  <script src="griffincss-core/dist/griffincss.js"></script>
</head>

С defer или скриптом в конце <body> парсинг уже завершён — работает прежний путь через DOMContentLoaded. Отключить режим принудительно:

<script src="griffincss.js" data-stream="false"></script>

Посмотреть разницу вживую: npm run demo:stream поднимает сервер, отдающий docs/stream.html порциями с паузой; рядом — та же страница с выключенным режимом.

Динамически добавленные контейнеры рантайм раскладывает сам: после init() он наблюдает за body (с 0.26.0; выключатель — data-observe="false" на теге скрипта), перерисовка идёт с дебаунсом и только по корню наблюдения. Griffincss.observe(root?) — наблюдение по своему корню, когда автоматическое выключено; Griffincss.refresh(root?) — проход вручную. Наблюдатель следит за добавлением узлов, но не за атрибутами: если data-gr-layout поменялся на уже существующем элементе, вызовите refresh() сами. Повторный проход безопасен — прежний класс .gr-l-* снимается, а выданные раньше gr-area-* переназначаются по новой раскладке (проставленные в разметке вручную остаются). Ребёнок с gr-area-*, которого нет ни в одной раскладке контейнера, получает предупреждение в консоли: авто-скрытие спрятало бы его на любой ширине молча.

Дедупликация: одинаковый набор раскладок → одинаковый хеш → один класс и одно правило. Каждое имя области даёт один .gr-area-*. Сгенерированный CSS организован в три секции: /* FOUC guard */, /* Grid Layouts */ и /* Grid Areas */.

Валидация: раскладка отвергается, если в ней есть счётчик без имени области (3a), нулевой счётчик (a0b2), счётчик больше 64 (a99999b), имя длиннее 32 символов, недопустимые символы в имени или пустой ряд (a1-). Такой контейнер пропускается целиком — вместо сломанного CSS в консоль уходит Griffincss: … с описанием ошибки, а сам блок остаётся видимым.

Адаптивные раскладки / Responsive layouts

Атрибуты data-gr-layout-{sm,md,lg,xl} задают раскладку для соответствующего брейкпоинта. JS-рантайм читает значения из CSS-переменных --gr-bp-* и генерирует непересекающиеся диапазоны в range-синтаксисе:

РаскладкаДиапазон
базовая data-gr-layout@media (width < 768px) — до первой адаптивной
data-gr-layout-md@media (768px <= width < 1024px)
data-gr-layout-lg (последняя)@media (width >= 1024px)

В любой момент активна ровно одна раскладка. Арифметики -1px нет, поэтому щелей на дробных ширинах не возникает, а единицы измерения брейкпоинта могут быть любыми (px, rem, em).

<div data-gr-layout="a1b1"
     data-gr-layout-md="a3b4-c2d5"
     data-gr-layout-lg="hdr4-main4-side2_2-ftr4">
  <!-- дочерние элементы с gr-area-* -->
</div>

Брейкпоинты по умолчанию (можно переопределить через CSS-переменные):

Защита от FOUC и работа без JS

До обработки рантаймом grid-контейнер выглядит как обычный поток блоков. Чтобы этот кадр не мелькал, рантайм первым же действием внедряет правило:

[data-gr-layout]:not(.gr-ready), … { opacity: 0; }
[data-gr-layout].gr-ready, …      { opacity: 1; transition: opacity 0.3s ease; }

Ключевое: этого правила нет в статическом CSS. Если скрипт заблокирован, не загрузился или упал — правила не существует, и контент виден. Ценой становится краткий кадр без сетки, а не пустая страница.

В потоковом режиме перехода не видно, и это ожидаемо: контейнер раскладывается в той же задаче, в которой был создан, поэтому браузер не успевает отрисовать его прозрачным. opacity: 0 остаётся страховкой на случай, когда кадр всё же проскочил. Плавное появление сохраняется там, где раскладка приходит заметно позже защиты, — при defer, при скрипте в конце <body>, при data-stream="false".

Если нужно предупредить пользователя, у которого JS отключён:

<noscript>
  <p>Для сложных раскладок нужен JavaScript. Без него блоки идут потоком —
     содержимое остаётся доступным.</p>
</noscript>

Не нужен рантайм вовсе? Сгенерируйте раскладки на этапе сборки миксином gr-grid-layout() — CSS получится тот же самый, JS не понадобится.

Grid-утилиты / Grid Utilities

КлассОписание
.gr-griddisplay: grid + gap
.gr-containermax-width + auto margin
.gr-container-full100% width
.gr-expand-rootcontainer-type: inline-size — предок, по которому равняется расширение
.gr-container-expandБлок во всю ширину корня из любой вложенности
.gr-grid-sm.gr-grid-xldisplay: grid и базовый отступ — от брейкпоинта
.gr-grid-auto-fitrepeat(auto-fit, minmax(--gr-grid-min, 1fr)), по умолчанию 250px
.gr-grid-auto-fillrepeat(auto-fill, minmax(--gr-grid-min, 1fr)), по умолчанию 250px
.gr-grid-1.gr-grid-12Явные сетки на 1–12 колонок (сами включают grid и отступ)
.gr-grid-1-sm.gr-grid-12-xlАдаптивные сетки — от ширины окна
.gr-grid-1-csm.gr-grid-12-cxlКонтейнерные сетки — число колонок от ширины ближайшего предка с .gr-cq
.gr-grid-rowsВсе ряды равной высоты — grid-auto-rows: 1fr
.gr-grid-place-centerplace-items: center
.gr-grid-col-span-1.gr-grid-col-span-12Column span
.gr-grid-row-span-1.gr-grid-row-span-6Row span
.gr-subgridgrid-template-columns: subgrid — колонки родителя проходят внутрь
.gr-subgrid-rowsgrid-template-rows: subgrid — то же по рядам

Блок во всю ширину / Full-bleed

.gr-container-expand растягивает блок на всю ширину предка с классом .gr-expand-root, сколько бы ограниченных по ширине контейнеров ни лежало между ними:

<body class="gr-expand-root">
  <div class="gr-container">
    <p>Текст, ограниченный шириной контейнера.</p>
    <div class="gr-container-expand">Во всю ширину окна</div>
  </div>
</body>

Корнем может быть любой предок, а не только body — например колонка раскладки, и тогда блок не наедет на соседнюю. Ширина берётся из 100cqw, поэтому, в отличие от 100vw, полоса прокрутки не учитывается и горизонтального переполнения нет.

Чтобы блок перекрыл ещё и паддинги корня, сообщите их величину переменной --gr-expand-inset — сам паддинг она не задаёт:

.content {
  padding-inline: 3rem;
  container-type: inline-size;
  --gr-expand-inset: 3rem;
}

Два ограничения: ширина корня не должна зависеть от его содержимого (container-type включает инлайн-containment, и inline-block схлопнется в ноль), а предки между корнем и блоком должны быть центрированы — формула складывает 50% родителя с 50cqw корня, и .gr-container с margin-inline: auto этому удовлетворяет. Без корня cqw откатывается на svw: блок займёт ширину окна вместе с полосой прокрутки.

Контейнерные запросы / Container queries

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

Класс / атрибутОписание
.gr-cqcontainer-type: inline-size — элемент становится контейнером запроса
--gr-nameИмя контейнера, если нужно обратиться к нему через голову ближайшего
.gr-cq-page / -main / -aside / -cardКонтейнер с именем классом: container: aside / inline-size — четыре слота раскладки как соглашение библиотеки
-c<брейкпоинт> в ядре.gr-grid-3-cmd, .gr-flex-row-csm, .gr-flex-col-cmd, .gr-flex-wrap-clg
-c<брейкпоинт> у утилит.gr-p-4-cmd, .gr-text-lg-clg, .gr-hidden-csm, .gr-w-full-cxl
data-gr-layout-c<брейкпоинт>Контейнерная раскладка: data-gr-layout-cmd="a1b2"
<div class="gr-cq">
  <article data-gr-layout="a1-b1" data-gr-layout-cmd="a1b2"
           class="gr-p-3 gr-p-5-cmd">
    <div class="gr-area-a">…</div>
    <div class="gr-area-b"><h3 class="gr-text-base gr-text-lg-cmd">…</h3></div>
  </article>
</div>

Контейнерные варианты есть у четырёх модулей griffincss-utils — отступы, размеры, типографика и видимость. Остальным (цвет, тень, граница, позиционирование, прозрачность, переходы) контейнерная адаптивность смысла не добавляет, а байты стоит.

Контейнер — это предок, а не сам элемент. @container ищет ближайшего предка с container-type и никогда не спрашивает сам элемент: элемент не может измерять себя. Поэтому .gr-cq ставится на обёртку, а -cmd-классы и data-gr-layout-cmd — на то, что внутри неё. Рантайм не проставляет .gr-cq сам: инлайн-containment, навешенный на чужую разметку, ломает страницу молча и в неожиданном месте.

container-type: inline-size включает инлайн-containment. Ширина элемента перестаёт зависеть от содержимого: у inline-block, у float, у width: fit-content она схлопывается в ноль. Ставьте .gr-cq на блок, ширину которого задаёт раскладка. По той же причине библиотека намеренно не объявляет контейнером body за вас: иначе каждый безымянный @container на странице разрешался бы к окну вместо того, чтобы просто не сработать.

Шкала общая — и потому крупная. 768px ширины контейнера достигает разве что основная колонка макета; реально пересекаемый порог для блока внутри страницы — -csm (640px). Нужен свой порог — он пишется одной строкой своего CSS: @container (width >= 26rem) { … }.

Имя контейнера приезжает свойством, а не классом: имя — это CSS-идентификатор, придуманный на стороне страницы, и статический файл не может знать его список. Свойство общее, а не --gr-cq-name: container-name, anchor-name и view-transition-name берут один и тот же идентификатор, и элемент вправе называться один раз на все роли. Оно ненаследуемое (@property … inherits: false), иначе вложенный .gr-cq перенял бы имя предка.

<section class="gr-cq" style="--gr-name: page">
  <div class="gr-cq">…</div>
</section>
@use 'griffincss-core/scss/container-queries' as cq;

@include cq.gr-container-media(md)        { .my-card { padding: 2rem; } }
@include cq.gr-container-media(lg, page)  { .my-card { padding: 3rem; } }

Компилтайм-раскладки принимают контейнерные варианты параметрами $csm$cxl, и вывод побайтово совпадает с тем, что генерирует рантайм.

Отступы / Gap

Один набор классов на все контейнеры — grid, flex и раскладки data-gr-layout:

КлассОписание
.gr-gap-smvar(--gr-gap-sm) — 0.375rem, с md 0.5rem
.gr-gapvar(--gr-gap) — 0.75rem, с md 1rem
.gr-gap-lgvar(--gr-gap-lg) — 1.25rem, с md 2rem

Адаптивных вариантов с суффиксом брейкпоинта нет и не нужно: значение меняют сами токены, поэтому один и тот же класс даёт меньший отступ на мобильном.

<div class="gr-flex gr-gap">…</div>
<div class="gr-grid gr-gap-lg">…</div>
<div data-gr-layout="a3b4-c2d5" class="gr-gap-sm">…</div>

Flex-утилиты / Flex Utilities

КлассОписание
.gr-flexdisplay: flex
.gr-inline-flexdisplay: inline-flex
.gr-flex-row / .gr-flex-colflex-direction
.gr-flex-wrap / .gr-flex-nowrapflex-wrap
.gr-flex-start / center / end / between / around / evenlyjustify-content
.gr-flex-items-start / center / end / stretch / baselinealign-items
.gr-flex-self-start / center / end / stretchalign-self
.gr-flex-1flex: 1
.gr-gap-sm / .gr-gap / .gr-gap-lgОтступ — общие классы, см. «Отступы» выше

Все flex-утилиты имеют адаптивные варианты: .gr-flex-md, .gr-flex-center-lg, и т.д. Контейнерные варианты — только у направления и переноса: .gr-flex-row-c*, .gr-flex-col-c*, .gr-flex-wrap-c*.

Печать / Print

Ресет несёт блок @media print и приезжает только вместе с самим ресетом: griffincss-reset.css — отдельная точка входа.

ПравилоЗачем
body { background: #fff; color: #000 }Фоны браузер по умолчанию не печатает, и светлый текст тёмной темы исчез бы на белом листе
details::details-content { content-visibility: visible }Свёрнутый <details> на бумаге — потерянный текст
h1h6 { break-after: avoid }Заголовок не остаётся один в конце страницы
blockquote, figure, img, pre, table { break-inside: avoid }Не разрываются посередине
p { orphans: 3; widows: 3 }Одна строка абзаца, оторванная от остальных, читается как обрывок

Тени гасятся не в ресете, а в теме — и потому во всех сборках. Слой griffincss.reset младший, и box-shadow: none оттуда проиграл бы и компоненту, и утилите .gr-shadow-*, а !important в библиотеке запрещён. На печати обнуляется множитель --gr-shadow-strength: семь готовых теней собраны из него, и ноль означает нулевую непрозрачность у каждой — в ядре, в компонентах и в утилитах разом, независимо от слоя и от того, подключён ли ресет. Там же останавливаются .gr-animate-spin и классы переходов.