Transitions

Плавные переходы, их длительность и индикатор загрузки.

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

Полноценной библиотеки анимаций в Griffincss нет и не планируется: fade, slide и pulse — задача уровня приложения, а не фреймворка раскладки. Модуль даёт переходы, их длительность и один keyframe — gr-spin, потому что индикатор загрузки нужен почти везде.

Переходы

КлассЧто анимируется
.gr-transitionВсе свойства (all)
.gr-transition-colorscolor, background-color, border-color, fill, stroke
.gr-transition-opacityopacity
.gr-transition-transformtransform
.gr-transition-shadowbox-shadow
.gr-transition-noneНичего — переход снят

Длительность и функция сглаживания берутся из токена --gr-transition (по умолчанию 0.2s ease). .gr-transition-colors предпочтительнее .gr-transition: браузеру не приходится следить за всеми свойствами разом.

Длительность

КлассCSS
.gr-duration-75.gr-duration-1000transition-duration: 75, 100, 150, 200, 300, 500, 700, 1000 мс

Класс длительности ставится рядом с классом перехода и перебивает только время:

<a class="gr-transition-colors gr-duration-150" href="#">Ссылка</a>
Наведите курсор
gr-duration-150
gr-duration-500

Индикатор загрузки

КлассCSS
.gr-animate-spinanimation: gr-spin 1s linear infinite
Утилита крутит то, что ей дали
<span class="gr-animate-spin gr-inline-block">↻</span>

Кольцо загрузки собирать руками не нужно. .gr-animate-spin — это только движение: она вращает любой элемент, который ей дали, — знак, иконку, картинку. Готовое кольцо с рамкой, тремя размерами и подписью для скринридера — это .gr-spinner из griffincss-ui, см. «Сообщения». Дубля тут нет: одна вещь — анимация без формы, другая — фигура вместе с ней.

Обе крутит один и тот же keyframe gr-spin, объявленный в каждом пакете одинаково: пакеты независимы, а определения обязаны совпадать посимвольно — иначе картинка зависела бы от того, какой файл подключён. Совпадение сторожит тест.

Имя keyframe префиксовано — gr-spin, столкнуться с анимацией проекта оно не может. Само правило @keyframes вынесено в отдельный файл: в scoped-сборке оно подключается вне области .griffin — имя анимации ищется глобально, и префикс области к нему неприменим.

Уважение к настройкам системы

При prefers-reduced-motion: reduce все переходы модуля сжимаются до 0.01ms, а вращение замедляется до 3 секунд. Движение не убирается полностью намеренно: обработчики, завязанные на событие окончания перехода, продолжают срабатывать.

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

Модуль отвечает на вопрос «как двигаться», а «когда» задаёт кто-то другой: атрибут, класс из JS, своё :hover-правило. Рецепты ниже — три ходовые задачи движения, собранные с кнопками ui, прозрачностью и событиями указателя.

Кнопка в состоянии отправки

Форма ушла на сервер — кнопка показывает процесс: внутрь встаёт вращающийся знак .gr-animate-spin .gr-inline-block (или готовое кольцо .gr-spinner .gr-spinner-current из ui — оно наследует цвет текста кнопки), а сама кнопка получает disabled. Флекс кнопки уже выравнивает знак с текстом; зазор — .gr-gap-sm не нужен, хватает пробела.

<button class="gr-btn gr-btn-primary" disabled>
  <span class="gr-animate-spin gr-inline-block" aria-hidden="true">↻</span>
  Сохраняем…
</button>
Знак крутит .gr-animate-spin, недоступность — атрибут disabled

Плавная реакция на наведение

Ховер-правило пишется в CSS приложения — а вот плавность его срабатывания даёт разметка: .gr-transition-colors .gr-duration-150 на элементе, и смена фона из вашего :hover перестаёт «мигать». Тот же принцип с тенью: .gr-transition-shadow под своё правило подъёма — токен --gr-transition держит сглаживание единым по всему интерфейсу.

/* CSS приложения */
.menu-item:hover { background-color: var(--gr-color-surface); }

<!-- разметка: переход из утилит -->
<a class="menu-item gr-transition-colors gr-duration-150 gr-p-2 gr-radius-2" href="#">Пункт меню</a>
Наведите: фон меняет своё CSS-правило, плавность — .gr-transition-colors

Появление и скрытие без keyframes

Тост, подсказка, панель фильтров: атрибуты видимости переключает JS, а движение целиком собирается из утилит. Скрытое состояние — пара .gr-opacity-0 .gr-pointer-events-none (невидимое не должно ловить клики), видимое — снятые JS-ом эти же классы; .gr-transition-opacity .gr-duration-300 превращает переключение в плавное растворение. В отличие от .gr-hidden элемент остаётся в потоке — вёрстка не дёргается. Готовый тост со временем жизни и регионами — .gr-toast.

<div id="hint" class="gr-alert gr-alert-info gr-transition-opacity gr-duration-300
                     gr-opacity-0 gr-pointer-events-none">Скопировано!</div>

<script>
  hint.classList.remove('gr-opacity-0', 'gr-pointer-events-none'); // показать
</script>
Показ — снятие .gr-opacity-0, движение — .gr-transition-opacity
Скопировано!