Справочник ядра
Переменные, брейкпоинты, grid-парсер, flex- и grid-утилиты, темы и стратегии оформления — полный список того, что даёт ядро.
CSS-переменные / CSS Custom Properties
| Переменная | Значение | Описание |
|---|---|---|
--gr-gap | 0.75rem → 1rem с md | Базовый отступ для grid/flex |
--gr-gap-sm | 0.375rem → 0.5rem с md | Малый отступ |
--gr-gap-lg | 1.25rem → 2rem с md | Большой отступ |
--gr-max-width | 1440px | Макс. ширина контейнера |
--gr-font-sans | system-ui, -apple-system, sans-serif | Семейство sans-serif |
--gr-font-serif | Georgia, Cambria, serif | Семейство serif |
--gr-font-mono | SFMono-Regular, Menlo, Consolas, monospace | Семейство monospace |
--gr-font-size-base | 1rem | Базовый размер шрифта |
--gr-hsl-gray-50 … --gr-hsl-gray-900 | 0, 0%, 96% … 0, 0%, 5% | HSL-токены нейтральной шкалы |
--gr-hsl-primary / success / warning / danger / info | 220,80%,50% / … | HSL-токены акцентных цветов |
--gr-hsl-surface-0 … -2, --gr-hsl-surface-sunken, --gr-hsl-surface-overlay | 0, 0%, 100% … | Шкала поверхностей — её меняет тема |
--gr-hsl-ink-0 … --gr-hsl-ink-3 | 0, 0%, 5% … | Шкала чернил — её меняет тема |
--gr-hsl-accent-base / -base-hover | 220, 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-hover | 220, 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-max | 0, 0%, 0% … — зеркальные в тёмной | Остальные якоря режима: куда уходят чернила, поверхности и посещённая ссылка. Чернила на акценте в режиме — surface-max: ось двигает пару целиком |
--gr-hsl-accent-soft / -soft-hover, --gr-hsl-on-accent-soft | 220, 65%, 62% / 54%, чернила 220, 60%, 12% | Якорь воздушного стиля и чернила на пастели — приезжают с griffincss-styles.css; не ниже 4,5 : 1 между собой. У журнального стиля своя пара — --gr-hsl-accent-press / -press-hover |
--gr-color-bg | hsl(var(--gr-hsl-surface-0)) | Цвет фона страницы |
--gr-color-surface / -raised / -sunken | hsl(var(--gr-hsl-surface-*)) | Карточка, приподнятая, утопленная поверхности |
--gr-color-surface-overlay | hsl(var(--gr-hsl-surface-overlay)) | Поверхность всплывающего слоя: меню, окно, выдвижная панель. В светлой теме белый лист, в тёмной — серый светлее фона: «выше страницы» в двух темах означает разное, одной ступенью -raised они не покрываются |
--gr-color-text | hsl(var(--gr-hsl-ink-0)) | Основной цвет текста |
--gr-color-text-secondary / -muted / -disabled | hsl(var(--gr-hsl-ink-*)) | Вторичный, приглушённый, неактивный текст |
--gr-color-border / -strong | hsl(var(--gr-hsl-border*)) | Разделитель и граница элемента управления |
--gr-color-link / -hover / -visited | hsl(var(--gr-hsl-link*)) | Цвета ссылок |
--gr-color-accent / -hover / --gr-color-on-accent | hsl(var(--gr-hsl-accent*)) | Акцент и текст на нём |
--gr-color-focus | hsl(var(--gr-hsl-focus)) | Кольцо фокуса |
--gr-color-success / warning / danger / info | hsl(var(--gr-hsl-status-*)) | Статусы — значения, безопасные для текста |
--gr-color-warning-surface / --gr-color-on-warning | hsl(var(--gr-hsl-status-warning-surface)) / hsl(var(--gr-hsl-on-status-warning)) | Заливка предупреждения и чернила на ней: единственный статус, чей текстовый цвет нельзя использовать как фон |
--gr-color-rating | hsl(var(--gr-hsl-status-warning-surface)) | Цвет звёзд и всего, что изображает оценку: ряд .gr-rating, ввод оценки, полосы распределения, значок у подписи. Отдельный токен, потому что текстовый --gr-color-warning затемнён ради контраста и рядом со звёздами читается коричневым |
--gr-border-width | 1px → 2px в режиме для слабовидящих | Ширина границы по умолчанию |
--gr-focus-width | 2px → 3px в режиме | Толщина кольца фокуса |
--gr-leading-base | 1.5 → 1.7 в режиме | Интерлиньяж body |
--gr-tracking-base | normal → 0.02em в режиме | Трекинг body |
--gr-a11y-scale | 1.25 | Во сколько раз режим увеличивает кегль |
--gr-radius | 0.5rem | Скругление элементов управления |
--gr-radius-container | не объявлен; читается как var(--gr-radius-container, var(--gr-radius)) | Скругление контейнеров — карточки, окна, меню, сообщения. Без объявления равен --gr-radius, и запасное значение считается на самом элементе: островок стиля с другим --gr-radius получает свои углы. Объявите его в своём CSS, если контейнер должен скругляться иначе — рецепт «радиус + отступ» на странице темы |
--gr-transition | 0.2s ease | Переходы |
--gr-shadow-color | 0, 0%, 0% | HSL-компоненты цвета тени — общие для всех ступеней |
--gr-shadow-strength | 1 → 2.6 в тёмной теме | Множитель плотности тени; геометрия не меняется |
--gr-shadow-xs … --gr-shadow-2xl | 0 1px 2px … | Шесть ступеней тени |
--gr-shadow-inner | inset 0 2px 4px … | Внутренняя тень |
--gr-control-height / -sm / -lg | 2.5rem / 2rem / 3rem | Высота кнопки и поля; в rem, поэтому растёт вместе с режимом для слабовидящих |
--gr-control-padding-x | 0.875rem | Горизонтальные поля элементов управления |
--gr-control-font-size | 1rem | Кегль элементов управления |
--gr-ui-gap | 0.5rem | Внутренний зазор компонента — между иконкой и текстом |
--gr-overlay | hsl(var(--gr-shadow-color), 55%) | Подложка модалки и ящика |
--gr-z-dropdown / -modal / -toast | 1000 / 1100 / 1200 | Порядок наложения попапов |
--gr-bp-sm | 640px | Брейкпоинт sm (справочно; собирается из карты $gr-breakpoints) |
--gr-bp-md | 768px | Брейкпоинт md |
--gr-bp-lg | 1024px | Брейкпоинт lg |
--gr-bp-xl | 1280px | Брейкпоинт xl |
Grid-парсер / Grid Parser
Уникальная фича Griffincss — описание сложных сеток через компактный строковый формат в data-gr-layout. JS-рантайм (griffincss.js) автоматически обходит DOM, парсит все data-gr-layout атрибуты и генерирует CSS Grid правила в <style> на лету. Никакой прекомпиляции раскладок не нужно.
Формат: "a3b4-c2d5" где:
-— разделитель рядов- Буквенная часть — имя grid-области (одна или несколько букв)
- Цифра — количество повторений
_— пустая ячейка (в CSS:.)
Пример / 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-cols | repeat(N, minmax(0, 1fr)) | Ширины колонок раскладки. Число треков обязано совпадать со строкой: недостающие браузер дорисует неявными по auto, лишние останутся пустыми — это видно в раскладке и в DevTools |
--gr-l-rows | repeat(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-переменные):
sm: 640pxmd: 768pxlg: 1024pxxl: 1280px
Защита от 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-grid | display: grid + gap |
.gr-container | max-width + auto margin |
.gr-container-full | 100% width |
.gr-expand-root | container-type: inline-size — предок, по которому равняется расширение |
.gr-container-expand | Блок во всю ширину корня из любой вложенности |
.gr-grid-sm … .gr-grid-xl | display: grid и базовый отступ — от брейкпоинта |
.gr-grid-auto-fit | repeat(auto-fit, minmax(--gr-grid-min, 1fr)), по умолчанию 250px |
.gr-grid-auto-fill | repeat(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-center | place-items: center |
.gr-grid-col-span-1 … .gr-grid-col-span-12 | Column span |
.gr-grid-row-span-1 … .gr-grid-row-span-6 | Row span |
.gr-subgrid | grid-template-columns: subgrid — колонки родителя проходят внутрь |
.gr-subgrid-rows | grid-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-cq | container-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-sm | var(--gr-gap-sm) — 0.375rem, с md 0.5rem |
.gr-gap | var(--gr-gap) — 0.75rem, с md 1rem |
.gr-gap-lg | var(--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-flex | display: flex |
.gr-inline-flex | display: inline-flex |
.gr-flex-row / .gr-flex-col | flex-direction |
.gr-flex-wrap / .gr-flex-nowrap | flex-wrap |
.gr-flex-start / center / end / between / around / evenly | justify-content |
.gr-flex-items-start / center / end / stretch / baseline | align-items |
.gr-flex-self-start / center / end / stretch | align-self |
.gr-flex-1 | flex: 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> на бумаге — потерянный текст |
h1–h6 { 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 и классы переходов.