Каскадные слои
Библиотека укладывается в свой @layer, поэтому ваши правила побеждают без !important.
Четырнадцать крупных сегментов вёрстки, которые забирают в проект целиком: шапка, первый экран, сетка карточек, карточка товара, тарифы, форма входа, лента статей, панель админки, дашборд, таблица с фильтрами, вопросы и ответы, призыв к действию, подвал и каталог с фасетами. У каждого — вкладка «Код» и кнопка копирования.
Каждый блок ниже собран только на классах библиотеки:
ни одного литерального цвета, ни одного пикселя в разметке, ни строчки
собственного CSS у этой страницы. Единственный атрибут style
на всей странице — оценка товара в --gr-rating: это данные,
а не оформление. Поэтому блок, перенесённый в ваш проект,
сразу знает про светлую и тёмную тему, про режим для слабовидящих
и про выбранную стратегию оформления — переключатели в боковой панели
действуют и на эти сегменты, попробуйте.
Порядок работы: вкладка «Код» → «Копировать» →
вставить в свою разметку → заменить тексты и ссылки. Классы
gr-area-* в панели админки и data-gr-layout
на её контейнере — единственное место, где нужен JS-рантайм библиотеки;
остальные тринадцать блоков работают без него. Своего кода просит один
счётчик количества в карточке товара — два вызова
stepUp() / stepDown() прямо в разметке блока.
Что нужно подключить. Все блоки требуют трёх файлов:
griffincss-core.css, griffincss-ui.css
и griffincss-utils.css. Панели админки нужен ещё
griffincss.js. Ни одна картинка, иконка или шрифт
в комплект не входят: в блоках стоят заглушки на фоновых утилитах
и юникодные символы — замените их на своё.
Бренд, меню и действие. До 768px меню сворачивается в кнопку
☰ — это <details>, без единой строки JS.
Заголовок, одна фраза о продукте, два действия и строка доверия.
Ширина текста ограничена gr-max-w-page, а не пикселями,
поэтому на широком мониторе строка не растягивается до нечитаемой.
Опишите раскладку строкой a3b4-c2d5 вместо десяти правил
grid-template-areas. Адаптивность — атрибутом, без единого медиа-запроса вручную.
Без карты · 14 дней · Отмена в один клик
gr-grid-auto-fit сама решает, сколько колонок поместится:
ни одного брейкпоинта в разметке. Карточки выравниваются по нижнему краю,
потому что подвал прижат gr-card-footer, а не отступом.
Библиотека укладывается в свой @layer, поэтому ваши правила побеждают без !important.
Светлая и тёмная — одна ось, режим для слабовидящих — вторая, независимая от первой.
Те же брейкпоинты, но от ширины блока: карточка в узкой колонке ведёт себя как на телефоне.
Витрина магазина целиком: стикеры поверх обложки, рейтинг-звёзды, цена
со старой ценой и размером скидки, выбор цвета и размера, счётчик
количества и кнопка. Нового CSS здесь два модуля —
gr-rating: дробное заполнение знака из готовых утилит
не собирается, — и образцы
gr-swatch / gr-swatch-tag: радио в виде
кружка цвета и плашки размера, где состояние «выбран» на самом образце
ни одна утилита дать не могла. Всё остальное уже было —
gr-badge в углу gr-relative-обёртки,
gr-line-through на старой цене, gr-input-group
вокруг пары кнопок со счётчиком.
Под курсором карточка приподнимается — это gr-hover-lift
из модуля трансформаций, один класс со своим переходом и своим
@media (hover: hover) внутри. На сенсорном экране эффект
не залипает после касания, а при prefers-reduced-motion: reduce
сжимается до неразличимого. Ни своего @media, ни своего
transition в блоке для этого не появилось.
Стикер стоит на логическом gr-start-0 — физической
привязки в библиотеке больше нет: под dir="rtl" он уезжает
к правому краю обложки сам. Оценка
дублируется числом рядом и aria-label на самом рейтинге —
пять знаков это цвет и форма, а они не переживают ни печать, ни программу
чтения с экрана. Кнопка «В корзину» прижата к низу тем же приёмом, что
подвал карточки в третьем блоке: gr-flex-1 на строке цены
забирает остаток высоты, и кнопки в ряду встают на одну линию.
style на всей странице есть только здесь.
В нём данные товара: значение оценки --gr-rating: 4.5
и цвет образца --gr-swatch: #1f1f1f. Это данные,
а не оформление — кегль, размеры и раскладка по-прежнему целиком
на классах, и в библиотеке от цвета товара не остаётся ни одного
литерала. Второе исключение рядом: кнопки счётчика зовут
stepDown() и stepUp() у соседнего поля прямо
из разметки — счётчик в демонстрации рабочий, и скопированный блок
работает так же. Методы родные, они уважают min,
max и step, а системные стрелки у поля
снимает gr-input-no-spin — рядом с кнопками они второй
орган управления тем же числом. Всё остальное — погасить «минус»
на границе, послать change в пересчёт корзины — остаётся
за вашим кодом, см. формы.
12 990 ₽
Старая цена 17 320 ₽
2 450 ₽
4 100 ₽
Сообщим, когда появится.
Три плана, средний выделен рамкой и плашкой, а не размером: увеличенная карточка ломает выравнивание цен, и глаз перестаёт их сравнивать.
Для личных проектов
0 ₽
навсегда
Для команды до десяти человек
1 490 ₽
в месяц
Для крупных внедрений
Договорная
по итогам разговора
Каждое поле — gr-field с подписью и подсказкой, связанной
через aria-describedby. Ошибка показана словом, а не только
цветом рамки. У пароля — кнопка «Показать» в .gr-input-group:
три строки на странице
переключают type, aria-pressed и подпись,
виджета для этого нет.
Ещё нет учётной записи? Зарегистрируйтесь.
Крупная статья и две поменьше. Раскладка описана строкой
gr-grid-12 со span-утилитами, поэтому на узком
экране колонки схлопываются сами.
Спор о том, какое правило победит, решается не подсчётом единиц специфичности, а порядком слоёв — и это меняет способ писать библиотеки целиком.
Наблюдатель парс-тайма отдаёт области детям по мере их появления в документе.
Разбор селекторов собранного файла вместо регулярного выражения по тексту.
Единственный блок на этой странице, которому нужен
griffincss.js: раскладка описана строкой
data-gr-layout, а на широком экране меняется
на data-gr-layout-md — без единого медиа-запроса в разметке.
Четыре показателя в ряд, под ними — полосы выполнения. Числа набраны
gr-font-mono: в колонке показаний разряды обязаны стоять друг
под другом, а пропорциональный шрифт этого не обещает. Полоса —
родной <progress>, а не div со вложенным span:
доступность у него уже есть.
Выручка
1 284 900 ₽
↑ 12,4% к прошлой неделе
Заказы
438
↑ 3,1%
Средний чек
2 934 ₽
↓ 1,8%
Возвраты
7
без изменений
Панель отбора, таблица в прокручиваемой обёртке и страницы под ней.
gr-table-wrap с tabindex="0" — чтобы до
прокрутки можно было добраться с клавиатуры; ниже 640 px прокрутка
не понадобится — .gr-table-stack-sm складывает каждую строку
в карточку с подписями из data-label
(«Карточки вместо
строк»). Период — пара нативных
полей даты, сумма — текстовое поле: без скрипта оба работают как есть,
с подключённым griffinjs-fields.js поля периода связываются
атрибутом to (второе не раньше первого), а сумма получает
разряды по локали — «С подключённым
griffinjs-fields.js».
| Номер | Клиент | Статус | Сумма |
|---|---|---|---|
| A-10482 | Кузнецова А. | Оплачен | 12 400 ₽ |
| A-10481 | Орлов Д. | В сборке | 3 950 ₽ |
| A-10480 | Гаврилова Е. | Отменён | 7 100 ₽ |
| A-10479 | Северов И. | Оплачен | 21 800 ₽ |
Показаны 1–4 из 96
Аккордеон на <details name="…">: общее имя заставляет
браузер закрывать соседей сам, без JS и без управления состоянием.
Нет. Без JS работают все утилитарные классы, все компоненты и статические SCSS-миксины раскладок. Рантайм нужен только для data-gr-layout, тем и произвольных значений в квадратных скобках.
Да, пакета три, и каждый самодостаточен. Минимальный набор — griffincss-core.css в 24 КБ; утилиты и компоненты добавляются по желанию.
Ничего. Весь вывод библиотеки лежит в каскадных слоях griffincss.*, а безслойные правила побеждают любой слой. Ни одного !important в сборке нет — это проверяется на каждой сборке.
Она уже есть. Цвета компонентов заданы семантическими токенами, поэтому обе темы и режим для слабовидящих приходят к вашей вёрстке даром — если вы пользуетесь классами, а не литеральными цветами.
Полоса на цвете акцента. Текст берёт gr-text-on-accent,
а не белый: в режиме для слабовидящих пара «фон — текст» меняется вместе,
и контраст сохраняется.
Три файла, ноль зависимостей, четырнадцать дней без карты.
Четыре колонки на широком экране, две на планшете, одна на телефоне —
gr-grid-1 gr-grid-2-sm gr-grid-4-lg, три класса вместо двух
медиа-запросов. Здесь колонки заданы числом, а не
gr-grid-auto-fit: у подвала их ровно четыре по смыслу,
и отдавать это решение ширине экрана незачем. Нижняя строка отделена
границей, а не отступом: так видно, где кончается навигация
и начинается правовая часть.
Боковая колонка отбора (четыре доли из двенадцати) и сетка карточек —
gr-grid-12-lg со span-утилитами; ниже 1024 px
колонки схлопываются в одну.
Порог здесь lg, а не md: на планшете три колонки
из двенадцати оставляют фасетам полосу, в которую не помещается ни цена
«от — до», ни подпись флажка.
Весь сегмент лежит внутри одной <form method="get">:
отбор уезжает в адресную строку, страница возвращается с теми же
значениями в полях, и переносится это в любой движок сайта как есть.
Ни одной строки скрипта, ни одного нового класса.
Цена — два числовых поля «от» и «до», и это база: без скрипта форма
работает ими. С подключённым griffinjs.js над полями ложится
пара ползунков
с параметром fields на обёртке — потянул ручку, число
обновилось; вписал число, ручка уехала. Отправляют форму по-прежнему
поля: name у них, у ползунков его нет, и в адрес уезжают
два параметра, а не четыре.
Фасеты — вложенные <details>: группа сворачивается,
и это платформа, а не виджет. Внешний <details open>
«Фильтры» нужен телефону: там колонка идёт над товаром, и её сворачивают
одним касанием. Выдвижной панели здесь намеренно нет — она потребовала бы
второго набора тех же полей в форме (или своего CSS у этой страницы),
а дубль полей в GET-форме отправляет фасет дважды.
Чего здесь нет и не будет: счётчиков у фасетов, логики фильтрации и состояния в адресе, собираемого скриптом. Это витрина, а не приложение: разметка и форма — всё. Крестик у выбранного фасета — ссылка на тот же адрес без этого параметра, а не кнопка, которой нужен обработчик.
Ни выбора даты, ни фильтрации по щелчку. Это не забывчивость: таким блокам нужен не «тонкий опциональный JS», а полноценный виджет с управлением состоянием — другая ответственность и другой размер. Всё, что на этой странице, работает от разметки и стилей, и ровно один сегмент из четырнадцати просит рантайм.
Фасетный фильтр из этого списка ушёл — но ушла только его разметка:
сегмент 14 отбирает <form method="get">, а счётчики
у фасетов, подгрузка выдачи и состояние в адресе остаются за приложением.
Граница та же, что и была: витрина показывает, приложение считает.
Автодополнение и сортировка таблицы стояли здесь же — и оба уже неправда: комбобокс и сортируемая таблица приехали тем же опциональным слоем. У сортировки основание пересмотра названо отдельно: перестановка строк по признаку из разметки обходится без модели состояния, а выбор даты — нет, и он в списке остался.
Про карусель здесь раньше стояло то же самое, и это уже неправда: слайдер, галерея, лайтбокс, параллакс
и мегаменю приехали опциональным слоем
griffinjs.js внутри пакета griffincss-ui —
«другая библиотека» получила имя и место, не меняя состава npm.
Подключается она отдельным файлом и по-прежнему никому не навязывается:
все четырнадцать блоков выше собраны без неё. Единственное место, где
она подключена, — ползунок цены в каталоге с фасетами, и он лежит
поверх двух числовых полей, которые работают и без него.
Товарной специфики здесь тоже нет как отдельного API: в карточке товара
ни gr-price, ни gr-sticker, ни gr-qty —
цена собирается кеглем и gr-line-through, стикер это
gr-badge в углу, счётчик — gr-input-group.
Своим классом стал только рейтинг: дробное заполнение знака из готовых
утилит не собирается, а остальное собирается — и тогда новое имя
не добавляет ни одного свойства.