Произвольные значения
Разовое значение прямо в имени класса: gr-mt-[13px]. Правило собирает рантайм — статический файл выразить это не может, потому что список возможных значений неизвестен до того, как страница написана.
Без JS не работает — и не может работать.
Класса .gr-mt-[13px] в собранном CSS нет: правило создаётся
в момент, когда рантайм видит элемент. Если скрипт не подключён или не
выполнился, отступа не будет. Поэтому произвольным значением задаётся
оформление, а не то, от чего зависит читаемость страницы: шкала
.gr-mt-4 работает всегда, и она остаётся основным способом.
Как это выглядит
<script src="griffincss.js"></script>
<script src="griffincss-utils.js"></script>
<div class="gr-mt-[13px]">отступ сверху 13px</div>
<div class="gr-w-[42%]">ширина 42%</div>
<div class="gr-p-[calc(1rem+3px)]">паддинг из calc()</div>
Рантайм разбирает имя класса, находит свойство по части до скобки
и добавляет правило в тот же слой griffincss.utils, где живут
обычные утилиты. Значит, произвольное значение спорит с классом из шкалы
по порядку в слое, а не по весу селектора, — как и любые два класса
библиотеки между собой.
/* что оказывается в <style id="griffincss-dynamic"> */
@layer griffincss.utils {
.gr-mt-\[13px\]{margin-top:13px}
.gr-w-\[42\%\]{width:42%}
}
Обратные слэши в селекторе рантайм проставляет сам: без экранирования
браузер прочитал бы .gr-mt-[13px] как класс
gr-mt- и следом селектор по атрибуту — правило не совпало бы
ни с чем.
Какие свойства доступны
Таблица свойств повторяет объявления статического класса один в один:
.gr-p-4 пишет и --gr-p, и padding, —
и .gr-p-[13px] пишет столько же. Иначе каскад скруглений видел
бы зазор у одного класса и не видел у другого.
| Класс | Свойство |
|---|---|
.gr-m-[…], .gr-mt-[…], .gr-mb-[…] | margin, верх и низ |
.gr-ms-[…], .gr-me-[…], .gr-mx-[…], .gr-my-[…] | логические и осевые маржины |
.gr-p-[…], .gr-pt-[…], .gr-pb-[…] | padding, верх и низ |
.gr-ps-[…], .gr-pe-[…], .gr-px-[…], .gr-py-[…] | логические и осевые паддинги |
.gr-radius-[…] | --gr-r + border-radius |
.gr-gap-[…] | gap |
.gr-w-[…], .gr-h-[…] | width, height |
.gr-min-w-[…], .gr-max-w-[…], .gr-min-h-[…], .gr-max-h-[…] | минимумы и максимумы |
.gr-top-[…], .gr-bottom-[…], .gr-start-[…], .gr-end-[…], .gr-inset-[…] | смещения; start/end — логические, вдоль строки |
.gr-z-[…] | z-index |
Почему нет .gr-text-[…].
У префикса gr-text- два смысла: .gr-text-2xl —
это кегль, а .gr-text-white — цвет. Что означает
.gr-text-[13px], пришлось бы угадывать, а рантайм не
угадывает. Цвета и кегль задаются своим CSS или переменной
--gr-* на элементе.
Что считается невалидным
Содержимое скобок приходит из разметки, поэтому проверяется так же, как
строка раскладки в ядре. Невалидное даёт console.warn
и пропуск класса — не сломанное правило, за которым в таблице стилей идут
все остальные.
| Отклоняется | Пример |
|---|---|
| пустые скобки | gr-mt-[] |
| значение длиннее 48 символов | gr-mt-[calc(1px+1px+…)] |
{, } или ; в значении | gr-mt-[1px;color:red] |
комментарий /* в значении | gr-mt-[1px/*x*/] |
| свойство неизвестно таблице | gr-zzz-[13px] |
Пробела в значении не бывает по устройству разметки: атрибут
class разделяется пробелами, и gr-mt-[1px + 2px]
распался бы на три класса. Составное значение пишется без пробелов:
gr-mt-[calc(1px+2px)].
Вместе с каскадом скруглений
Произвольные .gr-radius-[…] и .gr-p-[…] участвуют
в каскаде скруглений наравне со ступенями шкалы: рантайм читает их как
объявленные значения и выводит радиус вложенного
.gr-radius по той же рекурренте.
<div class="gr-radius gr-radius-[2rem] gr-p-[8px]">
<div class="gr-radius">радиус max(0px, calc(2rem - 8px))</div>
</div>
Отсечение неиспользуемого
npm run purge произвольных значений не касается: в статическом
файле их правил нет, отсекать нечего, и в safelist их вносить не нужно.
Safelist нужен для обратного случая — классов из шкалы, которые собираются
в коде склейкой ('gr-mt-' + n).
Когда лучше не надо
- Значение повторяется. Три одинаковых
gr-p-[13px]на странице — это ступень шкалы, которой не хватает, или своя переменная, а не три произвольных значения. - От значения зависит читаемость. Без JS правила не будет; критичное к содержимому оформление задаётся статикой.
- Значение приходит из данных. Ширина полосы прогресса
в процентах — это инлайновое
styleили CSS-переменная, а не новый класс на каждое число: каждое уникальное значение добавляет в таблицу стилей своё правило.
Примеры использования
Произвольные значения раскрывают возможности, которых у шкал не бывает
в принципе: отрицательные смещения, calc(), единицы
ch и точные пиксели макета. Рецепты ниже — три задачи,
которые без скобок не решаются вовсе.
Счётчик за углом кнопки
Шкала привязки к краям знает только 0 и auto —
наружу за край она не выходит. Отрицательные смещения
.gr-top-[-0.5rem] .gr-end-[-0.5rem] выносят бейдж-счётчик
наполовину за угол кнопки — как у иконки уведомлений. Родителю —
.gr-relative .gr-w-fit. Остальные выносы за край — колонка
целиком снаружи, ярлык на ребре — разобраны в «Выносе за край»
на странице Position.
<span class="gr-relative gr-w-fit gr-inline-block">
<button class="gr-btn gr-btn-secondary">Уведомления</button>
<span class="gr-badge gr-badge-danger gr-absolute gr-top-[-0.5rem] gr-end-[-0.5rem]">3</span>
</span>
.gr-top-[…]/.gr-end-[…]Рабочая область «экран минус шапка»
Чат, редактор, карта: под фиксированной шапкой область должна занять
ровно остаток экрана и прокручиваться внутри себя.
.gr-h-[calc(100svh-56px)] считает это прямо в классе —
calc() пишется без пробелов, иначе атрибут class
разрежет его на части. В связке — .gr-overflow-y-auto
для внутренней прокрутки.
<header style="height:56px">…</header>
<main class="gr-h-[calc(100svh-56px)] gr-overflow-y-auto">
…лента чата со своим скроллом…
</main>
calc(12rem-40px) при обёртке 12remОбласть получила ровно остаток высоты и прокручивается сама.
Сообщение 2
Сообщение 3
Сообщение 4
Сообщение 5
Сообщение 6
Панель точной ширины из макета
Дробные ширины хороши, пока панель может дышать; сайдбар из макета —
это ровно 280px на любом экране. .gr-w-[280px] фиксирует
панель, сосед забирает остальное знакомой парой .gr-flex-1
.gr-min-w-0. Если 280px повторяется в проекте много раз —
это уже не произвольное значение, а своя переменная (см. «Когда лучше
не надо»).
<div class="gr-flex gr-gap">
<aside class="gr-w-[280px]">Панель из макета</aside>
<main class="gr-flex-1 gr-min-w-0">Контент</main>
</div>
.gr-flex-1Контент
Занимает всё остальное.