Справочник компонентов
Двадцать шесть модулей интерфейса на токенах ядра: кнопки, поля, карточки, таблицы, навигация, всплывающие слои, состояния.
Готовые элементы интерфейса на токенах ядра. Обе темы и режим для слабовидящих приходят к ним даром — тем же механизмом, что и ко всему остальному: компоненты не знают ни одного литерального цвета и ни одной литеральной высоты.
Peer-зависимость — только griffincss-core. От griffincss-utils пакет не зависит: компонент обязан выглядеть правильно сам по себе, утилиты нужны, чтобы его подвинуть.
Именование плоское, в один дефис: .gr-btn-primary, а не .gr-btn--primary. Тот же строй, что в ядре и утилитах — двух синтаксисов в библиотеке нет.
Двадцать шесть модулей тремя волнами. Первая — статические: ни один не открывается, ни одному не нужен JS. Вторая — навигация и раскрытие: всё, что открывается, открывается средствами платформы — <details>, <dialog>, Popover API, — а не скриптом библиотеки. Третья — обратная связь и состояния: прогресс, кольцо занятости, заглушки на время загрузки, тосты, шаги процесса и пустое состояние.
Кнопки (scss/_button.scss)
| Класс | Описание |
|---|---|
.gr-btn | Базовая кнопка: поверхность с границей. Ставится на <button>, <a> или <input type="submit"> |
.gr-btn-primary / -secondary | Заливка акцентом / контур акцентом |
.gr-btn-success / -warning / -danger / -info | Заливка цветом статуса |
.gr-btn-ghost / -link | Без фона и границы / в виде ссылки |
.gr-btn-sm / -lg | Размеры из --gr-control-height-sm / -lg |
.gr-btn-block | Во всю ширину родителя |
.gr-btn-icon | Квадратная, под одну иконку; требует aria-label |
.gr-btn-group | Слипшийся ряд: скругления только по краям, границы не удваиваются |
Состояния: :hover (внутри @media (hover: hover)), :active, :focus-visible, :disabled, [aria-disabled="true"], [aria-busy="true"].
Вариант задаёт цвета четырьмя переменными — --gr-btn-bg, --gr-btn-fg, --gr-btn-border и триплетом --gr-btn-fg-hsl. Поэтому :hover и :active написаны один раз на все девять: они кладут поверх фона варианта вуаль из его же цвета текста (--gr-btn-veil-hover, --gr-btn-veil-active), а насколько это темнее или светлее — решает тема.
Ссылки (scss/_link.scss)
| Класс | Описание |
|---|---|
.gr-link | Цвет ссылки, подчёркивание from-font, состояния :visited / :hover / :active / :focus-visible |
.gr-link-muted | Цвет вторичного текста — подписи, футер |
.gr-link-inherit | Цвет наследуется от окружения — ссылка-заголовок, ссылка на плашке |
.gr-link-quiet | Подчёркивание только при наведении и фокусе |
.gr-link-external | Стрелка-указатель после текста |
.gr-link-dotted / -dashed / -wavy / -double | Стиль линии; сочетается с любым вариантом |
Модуль нужен потому, что ресет снимает с <a> и цвет, и подчёркивание (color: inherit; text-decoration: none) — это правильно для ссылки-обёртки вокруг карточки, но обычную ссылку в тексте оставляет неотличимой от текста.
.gr-link и .gr-btn-link — разные вещи: первая ведёт по адресу, вторая выполняет действие на текущей странице. Выбирает разметка, оформление лишь подтверждает выбор.
Линия объявлена только длинными свойствами — text-decoration-line, -thickness, -style. Сокращённое text-decoration сбрасывает остальные длинные в исходные значения, поэтому одно text-decoration: underline в :hover обнуляло бы толщину from-font из базового класса.
.gr-link-quiet применим там, где ссылка опознаётся по положению — навигация, ряд карточек, футер, — но не в тексте абзаца. Режим для слабовидящих перекрывает его: правило [data-gr-a11y="low-vision"] a[href] из темы возвращает и цвет, и подчёркивание.
Формы (scss/_form.scss)
| Класс | Описание |
|---|---|
.gr-field | Обёртка «метка + поле + подсказка» одной колонкой |
.gr-label / .gr-hint / .gr-error | Метка, пояснение, сообщение об ошибке |
.gr-success | Подпись о принятом значении: пара к .gr-error, тот же кегль и то же место |
.gr-required | Звёздочка обязательности вручную — для групп, где обязательность лежит не на одном контроле. У поля с required она появляется сама |
.gr-input / .gr-textarea / .gr-select | Три поля одной семьи: общая высота, кегль и состояния. У списка своя стрелка на currentcolor, отбитая от края на --gr-control-padding-x: положение системной задаёт браузер, и она прижимается вплотную к краю |
.gr-input-sm / -lg | Размер — на любом из трёх полей |
.gr-input-group | Поле с приклеенными аддонами |
.gr-input-no-spin | Числовое поле без системных стрелок шага: в счётчике количества шагают кнопки рядом, и стрелки становятся вторым органом управления тем же числом. Клавиатурный шаг ↑/↓ остаётся |
.gr-input-addon | Неинтерактивная подпись у края поля |
.gr-input на type="date" | Нативный выбор даты в оформлении семьи; два вендорных правила снимают отступы внутреннего редактора, чтобы поле не было выше соседнего. Плата — системный календарь и формат по локали браузера: «Формы» |
Состояния: :focus-visible, :user-invalid, [aria-invalid="true"], [aria-invalid="false"], :disabled, :read-only. :user-invalid, а не :invalid: пустое обязательное поле не должно краснеть до того, как его тронули.
Успех — только по явному [aria-invalid="false"]. Пары :user-valid у :user-invalid нет намеренно: браузер считает валидным любое непустое поле без ограничений, и форма зеленела бы сама собой по мере заполнения. Обе стороны ставит одна и та же серверная проверка.
Обязательность рисует .gr-field:has([required]) .gr-label::after — атрибут required уже стоит в разметке, и требовать рядом ещё и класс значило бы держать два источника правды.
Выбор (scss/_choice.scss)
| Класс | Описание |
|---|---|
.gr-checkbox / .gr-radio | Системный элемент, цвет отметки — через accent-color |
.gr-switch | Тумблер на <input type="checkbox">; дорожка и бегунок рисуются фоном |
.gr-choice | <label>-обёртка: подпись в строку с элементом и кликабельна целиком |
.gr-segmented | Сегментный переключатель из радиогруппы в <fieldset> |
.gr-swatch / .gr-swatch-tag | Радио в виде кружка цвета (--gr-swatch на элементе) или плашки размера, следом за .gr-radio внутри .gr-choice; выбранный — кольцо или заливка акцентом |
Элемент не подменяется: класс ставится на сам <input>, а не на спрятанный input рядом с оформленным <span>. Родная разметка сохраняет участие в форме, клавиатуру и объявление состояния скринридером.
Ползунок (scss/_range.scss)
| Класс | Описание |
|---|---|
.gr-range | <input type="range">: фильтр цены, громкость, доля. Дорожка — оттенок currentcolor, бегунок — акцент |
.gr-range-pair | Пара «от — до»: два ползунка в одной ячейке сетки, отрезок между ручками — наложение двух дорожек. Порядок значений держит виджет range слоя |
--gr-range-track / --gr-range-thumb | Толщина дорожки и размер бегунка. Обе в rem: режим для слабовидящих поднимает кегль корня, и ползунок растёт вместе с полем рядом |
--gr-progress | Доля пройденного пути одиночного ползунка, 0…1. Её пишет виджет range слоя GriffinJS; CSS переводит её в проценты (--gr-range-fill) и красит след |
--gr-range-from / --gr-range-to | Границы отрезка в паре, 0…1, на самой .gr-range-pair: отрезок рисует она одна, и обе границы нужны ей сразу |
Шесть вендорных псевдоэлементов, каждый — отдельным правилом: неизвестный селектор в группе делает невалидной всю группу, и бегунок остался бы системным в обоих движках. accent-color при этом не снимается — он единственное, что делает ползунок частью библиотеки там, где appearance: none ещё не применён.
Заполненную часть дорожки Firefox рисует сам (::-moz-range-progress) и без скрипта. У WebKit псевдоэлемента для неё нет: там след — градиент по --gr-progress, и без слоя он просто не появляется. Чисто-CSS-приём (тень бегунка, обрезанная дорожкой) отвергнут: та же обрезка съедает круглую ручку.
Направление следа задано ручкой --gr-range-dir, а не стороной в каждом градиенте: логической формы у градиента нет, а в RTL движок разворачивает ползунок — и одно правило :dir(rtl) разворачивает след во всех дорожках сразу, включая пару.
Карточки (scss/_card.scss)
| Класс | Описание |
|---|---|
.gr-card | Поверхность с границей и скруглением; колонка на flex |
.gr-card-header / -body / -footer | Шапка, тело, подвал |
.gr-card-media | Картинка во всю ширину; углы срезает сама карточка |
.gr-card-raised / -flat | Тень вместо границы / только фон |
.gr-card-interactive | Кликабельна целиком, но в табе — один элемент со ссылкой |
Токены компонента читаются с запасным значением, на компоненте не объявляются, поэтому переопределение на любой обёртке доходит до карточки:
| Токен | По умолчанию | Читает |
|---|---|---|
--gr-card-bg | --gr-color-surface | .gr-card, фон |
--gr-card-border | --gr-color-border | .gr-card, цвет обводки |
--gr-card-radius | --gr-radius-container → --gr-radius | .gr-card, скругление |
--gr-card-pad | --gr-gap | .gr-card-body целиком; -header и -footer — по горизонтали тот же, по вертикали ¾ |
--gr-card-overflow | hidden | .gr-card, обрезка содержимого: hidden срезает медиа по скруглению; visible — для панели, из которой выходит абсолютный список подсказки |
Таблицы (scss/_table.scss)
| Класс | Описание |
|---|---|
.gr-table | Схлопнутые границы, отступы в ячейках, приглушённая шапка |
.gr-table-striped / -bordered / -compact / -relaxed / -hover | Полосы, полная сетка, плотный и разреженный варианты, подсветка строки |
.gr-table-wrap | Обёртка с горизонтальной прокруткой — вбок уезжает таблица, а не страница |
.gr-table-sticky | Липкая шапка внутри прокручиваемой области; линию под ней рисует внутренняя тень — схлопнутая граница с уехавшей ячейкой не едет |
.gr-table-stack, -{sm,md,lg,xl}, -c{sm,md,lg,xl} | Карточки вместо строк: всегда, ниже ширины окна или ниже ширины ближайшего .gr-cq. Суффикс называет порог: -md — карточки, пока уже 768 px. Шапка ужата в пиксель (не display: none — читалке нужна), подпись ячейки — из data-label, без атрибута — без подписи |
th[aria-sort] | Указатель направления сортировки у заголовка — рисуется из aria-sort, а не из класса, и потому работает и на таблице, отсортированной сервером |
[data-gr-sortable] | Сортировка строк виджетом слоя GriffinJS: атрибут на <table>, aria-sort="none" на сортируемых заголовках, необязательный data-gr-sort-value на ячейке — «Таблицы» |
Сообщения (scss/_alert.scss)
| Класс | Описание |
|---|---|
.gr-alert | Сообщение о статусе |
.gr-alert-info / -success / -warning / -danger | Статус |
.gr-alert-title | Слово статуса — обязательная часть |
.gr-alert-icon / -body | Слот под иконку и текст |
.gr-alert-dismissible | Отступ под кнопку закрытия |
Слово берёт --gr-alert-color (безопасный для текста), полоса и подложка — --gr-alert-fill. У предупреждения это разные цвета: подробности ниже, в разделе о токенах.
Статус читается словом, а не только цветом: цветная полоса не переживает чёрно-белую печать, дальтонизм и монохромный режим высокого контраста, а слово переживает. Роль (role="alert" или role="status") объявляется разметкой — модуль её не подставляет.
Плашки (scss/_badge.scss)
| Класс | Описание |
|---|---|
.gr-badge | Плашка статуса; цвета -accent / -success / -warning / -danger / -info |
.gr-badge-pill / -outline / -count | Пилюля, контур, счётчик |
.gr-tag / .gr-tag-remove | Тег и кнопка-крестик; крестику нужен data-gr-dismiss и рантайм |
.gr-dot | Точка-индикатор; цвета те же |
Статус задаёт три цвета, а не один: --gr-badge-color для текста и линии, --gr-badge-fill для заливки и --gr-badge-on-fill для чернил на ней. У четырёх статусов из пяти первые два совпадают; расходятся они у предупреждения — см. --gr-color-warning-surface в таблице токенов.
Аватары (scss/_avatar.scss)
| Класс | Описание |
|---|---|
.gr-avatar | Круг с инициалами; <img> внутри перекрывает их собой |
.gr-avatar-xs / -sm / -lg / -xl | 1.5rem / 2rem / 3rem / 4rem |
.gr-avatar-square | Скруглённый квадрат вместо круга |
.gr-avatar-group | Ряд с перекрытием |
Фолбэк не требует скрипта: нет картинки — нет и перекрытия, инициалы остаются видны. Битая ссылка даёт тот же результат.
Навигация (scss/_nav.scss)
| Класс | Описание |
|---|---|
.gr-nav | Ряд ссылок; работает и на <nav>, и на <ul> — список обнуляется здесь же |
.gr-nav-link | Пункт: ссылка или кнопка |
.gr-nav-vertical | Колонка вместо ряда — боковое меню раздела |
.gr-nav-underline | Активный пункт подчёркнут, а не залит |
.gr-navbar / .gr-navbar-brand | Шапка сайта и название в ней |
.gr-nav-toggle / .gr-nav-collapse | Переключатель на <details> и сворачиваемая навигация |
| Токен | По умолчанию | Читает |
|---|---|---|
--gr-nav-item-pad | 0 | .gr-nav > li — отступ пункта; правило темы на самом <li> проиграло бы слою компонентов |
--gr-nav-link-radius | --gr-radius | .gr-nav-link, скругление; в .gr-nav-underline радиус по-прежнему 0 |
Активный пункт помечается aria-current="page" — тем же признаком, по которому его находит программа чтения с экрана. Отдельного класса на это нет: класс подсветил бы пункт только зрячему.
Сворачивание не прячет навигацию внутрь <details>: содержимое <details> скрывает сам браузер, и вернуть его на широком экране без скрипта нечем. Навигация стоит следующим соседом переключателя, поэтому раскрытием управляет .gr-nav-toggle[open] + .gr-nav-collapse, а с 768px медиазапрос отменяет сворачивание целиком:
<div class="gr-navbar">
<a class="gr-navbar-brand" href="/">Сайт</a>
<details class="gr-nav-toggle"><summary>Меню</summary></details>
<nav class="gr-nav gr-nav-collapse">…</nav>
</div>
Хлебные крошки (scss/_breadcrumb.scss)
| Класс | Описание |
|---|---|
.gr-breadcrumb | Путь до страницы; разметка — <nav> с <ol> |
.gr-breadcrumb-item | Пункт, если списка <li> нет |
.gr-breadcrumb-scroll | Длинный путь уезжает в прокрутку, а не переносится |
Разделитель рисует ::before у каждого пункта, кроме первого; символ — переменная --gr-breadcrumb-sep (по умолчанию /). Последний пункт не ссылка, а текст с aria-current="page".
Меню (scss/_menu.scss)
| Класс | Описание |
|---|---|
.gr-menu | Список действий: поверхность, рамка, тень |
.gr-menu-item | Пункт — <button> или <a> |
.gr-menu-header / .gr-menu-sep | Заголовок группы и разделитель на <hr> |
.gr-menu-danger | Опасное действие |
.gr-menu-icon / .gr-menu-shortcut | Слот под иконку и под сочетание клавиш |
Собственного раскрытия у модуля нет — это оформление списка. Открывает его .gr-dropdown.
Выпадающая панель (scss/_dropdown.scss)
| Класс | Описание |
|---|---|
.gr-dropdown | Обёртка: <div> с попапом или сам <details> |
.gr-dropdown-panel | Панель; внутрь кладут .gr-menu или что угодно ещё |
.gr-dropdown-end / -up | Раскрытие к концу строки и вверх |
.gr-dropdown-hover | Раскрытие по наведению; с клавиатуры и на сенсорном экране — по фокусу |
| Токен | По умолчанию | Читает |
|---|---|---|
--gr-dropdown-min-width | max-content | .gr-dropdown-panel — минимальная ширина панели, в обоих путях (абсолютная и popover) |
--gr-dropdown-pad | 0 | .gr-dropdown-panel — поле вокруг содержимого; сама панель — коробка позиционирования, рамку и фон даёт .gr-menu внутри |
Два пути, оба без скрипта. <details class="gr-dropdown"> работает везде, но не закрывается по Esc и по клику мимо — <details> о них не знает. <button popovertarget> с панелью popover получает и то, и другое от браузера; привязать её к кнопке умеет только CSS anchor positioning, поэтому там, где его нет, панель раскрывается листом у нижнего края экрана. Положение не то — меню доступно и закрывается штатно.
Третий способ — .gr-dropdown-hover: панель держится, пока указатель на обёртке, и уходит с четвертьсекундной задержкой; зазор до панели перекрыт невидимым мостиком, иначе наведение прерывалось бы на полпути. Скрипт не нужен и здесь: показ — :hover внутри @media (hover: hover), а на сенсорном экране и с клавиатуры — :focus-within, поэтому кнопка обязана быть <button>. Цена способа: aria-expanded без скрипта не обновить (кнопке ставят aria-haspopup="true" и молчат об открытости), а закрытия по Esc платформа тут не даёт — наведение для браузера не «открытое состояние». Годится для навигации; меню с «Удалить» и «Выйти» делают на <details> или на попапе, где состояние настоящее.
Аккордеон (scss/_accordion.scss)
| Класс | Описание |
|---|---|
.gr-accordion | Обёртка с рамкой и линиями между разделами |
.gr-accordion-item | Раздел — <details> |
.gr-accordion-trigger | Заголовок — <summary>; стрелка своя, родная снята |
.gr-accordion-panel | Содержимое раздела |
.gr-accordion-flush | Без рамки и фона — внутрь карточки или колонки текста |
| Токен | По умолчанию | Читает |
|---|---|---|
--gr-accordion-summary-pad | --gr-gap | .gr-accordion-trigger — по горизонтали целиком, по вертикали половина |
Эксклюзивность (открыт только один раздел) даёт платформа: общий атрибут name у соседних <details>. CSS на это не нужен вовсе.
Плавное раскрытие включается только там, где браузер умеет анимировать высоту до auto (interpolate-size: allow-keywords) и выбирать содержимое <details> селектором (::details-content). Без обоих условий раздел раскрывается сразу — и это рабочее состояние, а не поломка.
Вкладки (scss/_tabs.scss)
| Класс | Описание |
|---|---|
.gr-tabs | Обёртка |
.gr-tablist | Ряд заголовков; прокручивается вбок, а не переносится |
.gr-tab | Заголовок — ссылка-якорь или кнопка |
.gr-tab-panel | Панель |
.gr-tabs-pills / -stretch | Заливка вместо линии; ряд во всю ширину |
| Токен | По умолчанию | Читает |
|---|---|---|
--gr-tabs-tab-pad | --gr-control-padding-x | .gr-tab, горизонтальный отступ вкладки |
Без скрипта это ссылки-якоря и подряд идущие секции: страница читается и печатается целиком. Со скриптом (griffincss-ui.js) появляются role="tablist", стрелки, Home/End и roving tabindex, а неактивные панели прячет атрибут hidden — не правило библиотеки: display: none убрал бы панель с экрана, но оставил бы её в порядке обхода.
Модальное окно (scss/_modal.scss)
| Класс | Описание |
|---|---|
.gr-modal | Окно на <dialog> |
.gr-modal-header / -title / -body / -footer | Пояса; прокручивается только тело |
.gr-modal-sm / -lg / -full | Размеры |
| Токен | По умолчанию | Читает |
|---|---|---|
--gr-modal-body-overflow | auto | .gr-modal-body, overflow-y: окну с собственной прокруткой внутри (карта, список) — hidden |
--gr-modal-transition | --gr-transition | Длительность и кривая перехода окна — все четыре свойства (opacity, translate, overlay, display) |
--gr-radius-container | --gr-radius | Скругление окна — токен ядра, общий с карточкой, меню и сообщением (reference-core.html) |
Верхний слой без z-index, удержание фокуса внутри окна, закрытие по Esc, возврат фокуса на кнопку, инертность страницы под окном — всё это даёт <dialog>. Открывает окно showModal(), закрывает <form method="dialog"> внутри него — без единой строки собственного JS.
Единственное, чего <dialog> не умеет, — закрытие щелчком по подложке. Его добавляет опциональный griffincss-ui.js, и включается оно атрибутом на самом окне, а не глобальной настройкой:
<dialog class="gr-modal" data-gr-overlay-close>…</dialog>
<script src="griffincss-ui/dist/griffincss-ui.js"></script>
Работает и на .gr-drawer — это тот же <dialog>. После такого закрытия dialog.returnValue равен "overlay", поэтому обработчик отличит щелчок мимо от кнопки «Сохранить». Выделение текста, начатое внутри окна и законченное за его краем, окно не закрывает: рантайм сверяет обе половины щелчка. У окна с наполовину заполненной формой атрибут просто не ставят.
Выдвижная панель (scss/_drawer.scss)
| Класс | Описание |
|---|---|
.gr-drawer | Тот же <dialog>, прижатый к краю экрана |
.gr-drawer-start / -end / -top / -bottom | Сторона |
.gr-drawer-sm / -lg | Ширина |
Пояса берутся у модалки — .gr-modal-header, -body, -footer работают и здесь: панель отличается положением, а не устройством. Стороны заданы логическими свойствами, поэтому в разметке справа налево панель «конца строки» выезжает слева.
Подсказка (scss/_tooltip.scss)
| Класс | Описание |
|---|---|
.gr-tooltip-anchor | Обёртка вокруг элемента и подсказки |
.gr-tooltip | Сама подсказка |
.gr-tooltip-bottom / -start / -end | Сторона (по умолчанию сверху) |
Показывается и указателем, и с клавиатуры, и то и другое — чистым CSS: :hover внутри @media (hover: hover) и :focus-within. aria-describedby в разметке обязателен, иначе подсказку получит только тот, кто её видит. Прячут её visibility и прозрачность, а не display: none: скрытая через display, она исчезла бы из дерева доступности вместе со связью.
Пагинация (scss/_pagination.scss)
| Класс | Описание |
|---|---|
.gr-pagination | Ряд страниц |
.gr-page | Страница — ссылка или кнопка |
.gr-page-gap | Многоточие: пропуск страниц |
.gr-pagination-compact / -joined | Плотный ряд; слипшаяся лента |
Текущая страница помечается aria-current="page" и выделяется заливкой, а не только цветом: в ряду одинаковых цифр разница в оттенке замечается хуже, чем разница в весе пятна. Недоступная кнопка «дальше» получает aria-disabled="true" и остаётся в порядке обхода — исчезнувшая кнопка сбивает счёт нажатий Tab, приглушённая не сбивает.
Полоса выполнения и шкала (scss/_progress.scss)
| Класс | Описание |
|---|---|
.gr-progress | Полоса на <progress>; значение показывает сам элемент |
.gr-progress-sm / -lg | Толщина |
.gr-progress-success / -warning / -danger | Цвет заполнения |
.gr-progress-indeterminate | Работа идёт, доля неизвестна: бегущий отрезок |
.gr-meter | Шкала на <meter>; цвет вычисляет браузер |
Дорожку и заполнение рисуют вендорные псевдоэлементы, и каждый объявлен своим правилом: неизвестный селектор делает невалидной всю группу, поэтому ::-webkit-progress-value рядом с ::-moz-progress-bar не покрасил бы ни того, ни другого.
У шкалы классов-статусов нет намеренно. <meter> сам вычисляет по low, high и optimum, попало ли значение в хороший диапазон; класс позволил бы разметке сказать «плохо» там, где элемент вычислил «хорошо».
<progress> — про ход задачи, у которой есть конец. <meter> — про измеренную величину: заполненность диска, оценку, запас.
Рейтинг (scss/_rating.scss)
| Класс или переменная | Описание |
|---|---|
.gr-rating | Пять знаков, залитых по оценке: отзыв о товаре, средний балл в списке. Только отображение — ввод оценки остаётся за потребителем |
--gr-rating | Оценка от 0 до 5 на самом элементе. Дробная рисуется частью знака: 4,5 — ровно 90 % ширины ряда |
--gr-rating-symbol | Знак, токен ядра. По умолчанию звезда, записанная экранированием "\2605": литеральный символ сделал бы собранный CSS не-ASCII, и Sass приписал бы к файлу BOM |
--gr-color-rating | Цвет залитого знака — семантический токен темы (reference-core.html): его же читает всё, что изображает оценку рядом с рядом. По умолчанию --gr-color-warning-surface; пустой знак — оттенок currentcolor |
--gr-rating-color | Переопределение цвета на одном ряду (или в своём :root): style="--gr-rating-color: var(--gr-color-danger)". В :root библиотеки не объявлена — объявленная там, она резолвилась бы один раз и не следовала бы теме вложенной секции |
Два одинаковых ряда знаков друг над другом: нижний (::before) задаёт ширину и рисует пустые знаки, верхний (::after) залит и обрезан по доле оценки. Заливка одного ряда через background-clip: text потребовала бы color: transparent, а прозрачный текст исчезает в режиме принудительных цветов вместе со всей оценкой.
Разрядки между знаками нет намеренно: интервал сдвигал бы границу заливки на целое число промежутков, и доля перестала бы совпадать с оценкой. Обрезка идёт от логического края (inset-inline-start), поэтому под dir="rtl" ряд разворачивается сам — правил на [dir] в модуле нет.
Иконок библиотека не везёт: вид задаётся глифом в --gr-rating-symbol, и на этом граница. Оценка обязана быть и словом — role="img" с aria-label вида «4,5 из 5».
Кольцо занятости (scss/_spinner.scss)
| Класс | Описание |
|---|---|
.gr-spinner | Кольцо; диаметр 1.5em от кегля родителя |
.gr-spinner-sm / -lg | Меньше и крупнее |
.gr-spinner-current | Кольцо цветом текста — для цветной кнопки |
Размеры в em, а не в rem: кольцо внутри кнопки должно быть по её кеглю. Пустому <span> нужны role="status" и подпись, иначе о загрузке узнает только тот, кто видит кольцо; рядом с видимым словом «Сохраняем…» хватает aria-hidden="true".
При prefers-reduced-motion вращение замедляется, но не снимается: остановленный индикатор занятости сообщает «зависло».
Заглушка на время загрузки (scss/_skeleton.scss)
| Класс | Описание |
|---|---|
.gr-skeleton | Прямоугольник на месте будущего содержимого |
.gr-skeleton-text | Строка высотой в кегль; последняя в блоке короче |
.gr-skeleton-circle | Круг под аватар |
.gr-skeleton-group | Коробка заглушки: свои отступы и зазоры между фигурами |
.gr-skeleton-stack | Колонка строк внутри коробки |
Залито оттенком currentcolor, а не серым: непрозрачный цвет совпал бы с одной из подложек и стал бы невидимым. .gr-skeleton-group задаёт отступы и зазоры сама, поэтому в карточке встаёт вместо .gr-card-body — у самой .gr-card внутренних отступов нет. Обёртке нужен aria-hidden="true", а рядом — слово в role="status": пустые прямоугольники в озвучке не значат ничего.
Мерцание по prefers-reduced-motion снимается совсем — в отличие от спиннера: заглушка сообщает о загрузке самим своим присутствием.
Всплывающие уведомления (scss/_toast.scss)
| Класс | Описание |
|---|---|
.gr-toast-region | Живая область; aria-live и роль задаёт разметка |
.gr-toast-region-top-start / -top-end / -bottom-start / -bottom-end | Угол; у нижних колонка перевёрнута |
.gr-toast | Сообщение |
.gr-toast-info / -success / -warning / -danger | Статус |
.gr-toast-title | Слово статуса |
.gr-toast-icon / -body | Слот под знак и текст |
.gr-toast-leaving | Уход; класс вешает рантайм |
Единственный компонент библиотеки без статического применения: показывает и убирает тосты griffincss-ui.js.
<div class="gr-toast-region gr-toast-region-bottom-end"
role="status" aria-live="polite"></div>
Griffincss.ui.toast('Черновик сохранён', { status: 'success', title: 'Готово' });
Регион ставят на страницу один раз и пустым: программа чтения с экрана следит за изменениями внутри aria-live, а не за появлением самой области. Появление тоста объявлено через @starting-style, поэтому рантайму не нужны ни второй кадр, ни пара «добавил класс — снял класс».
Шаги процесса (scss/_steps.scss)
| Класс | Описание |
|---|---|
.gr-steps | Ряд шагов; разметка — <ol> |
.gr-steps-vertical | Тот же ряд колонкой |
.gr-step | Шаг; по умолчанию — ожидающий |
.gr-step-done | Пройденный: линия от него к следующему залита |
.gr-step-disabled | Пропущенный или отменённый |
.gr-step-marker / -label / -note / -body | Кружок со знаком, название, пояснение, колонка подписи |
Текущий шаг помечается aria-current="step" — тем же признаком, по которому его находит программа чтения с экрана; класса для него нет. Знак в маркере (номер, галочка) кладут в разметку: цвет не переживает чёрно-белую печать и дальтонизм, знак переживает.
Линия — хвост шага, ведущий к следующему: ::after у левого, в колонке — у верхнего. Залита она у пройденного, у текущего остаётся серой. Так решено потому, что только у левого шага длину линии считает сам CSS: сколько текста висит под маркером предыдущего шага, правило, написанное на следующего, знать не может, и в колонке хвост, растущий вверх, не доставал бы до соседнего маркера.
Пустое состояние (scss/_empty.scss)
| Класс | Описание |
|---|---|
.gr-empty | Колонка по центру: иконка, заголовок, пояснение, действие |
.gr-empty-boxed | То же внутри пунктирной рамки |
.gr-empty-icon / -title / -text / -actions | Части |
Ширина пояснения ограничена в ch, а не в пикселях, поэтому ограничение в 46 знаков остаётся таким же и в режиме для слабовидящих. Слот под кнопку есть потому, что пустое состояние без действия — тупик.
Рантайм компонентов (dist/griffincss-ui.js)
ES5-совместимый файл без зависимостей, пишется руками и не компилируется. Автостарт по DOMContentLoaded; отключается атрибутом data-auto="false" на теге <script>. Делает четыре вещи, которых платформа не даёт:
| Что | Как |
|---|---|
| Закрытие окна щелчком по подложке | data-gr-overlay-close на <dialog>; returnValue — "overlay" |
| Крестик | [data-gr-dismiss] внутри .gr-alert, .gr-toast или .gr-tag; значение атрибута — селектор цели, если она снаружи |
| Тосты | Griffincss.ui.toast(text, options) |
| Клавиатура вкладок | data-gr-tabs на обёртке: роли, aria-selected, стрелки, Home/End, roving tabindex, hidden у неактивных панелей |
Параметр toast | По умолчанию | Что делает |
|---|---|---|
status | нет | info / success / warning / danger; danger переводит область в assertive |
title | нет | Слово статуса; статус без слова — предупреждение в консоль |
timeout | 5000 | Миллисекунды до ухода; 0 — ждать крестика |
position | bottom-end | Один из четырёх углов |
icon | нет | Знак в слот .gr-toast-icon |
dismissible | true | false убирает крестик |
assertive | по статусу | Прямое указание, какой областью показать |
Таймер тоста останавливается под указателем и под фокусом и дочитывает остаток, а не начинается заново. Все обработчики — делегированные, по одному на документ: Griffincss.ui.start() и .destroy() поднимают и снимают их целиком.
Клавиатура вкладок включается только при data-gr-tabs. Без атрибута и без скрипта та же разметка остаётся списком ссылок-якорей и секциями подряд — страница читается сверху вниз и печатается целиком.
События рантайма
Рантайм сообщает о действиях пользователя событиями CustomEvent — по образцу griffincss:themechange у рантайма темы. События всплывают, слушать их можно на документе.
| Событие | Когда | detail |
|---|---|---|
griffincss:tabchange | Пользователь переключил вкладку щелчком или с клавиатуры; расстановка ролей при инициализации события не шлёт | index, tab, panel |
griffincss:toast | Тост показан вызовом Griffincss.ui.toast() | node, status (null без статуса), position |