@verta/ui-gates (3.0.0)
Installation
@verta:registry=npm install @verta/ui-gates@3.0.0"@verta/ui-gates": "3.0.0"About this package
@verta/ui-gates
Дизайн-гейты Verta: Biome-пресет (+GritQL-правила) и ui-gates-check
(токен-контракт + добивка динамики). Ловит машина, не ревью.
Подключение (consumer)
pnpm add -D @verta/ui-gates
biome.json проекта:
{ "extends": ["@verta/ui-gates/biome"] }
Ось гейта в package.json / CI:
{ "scripts": { "ui-gate": "ui-gates-check" } }
Пакет должен быть прямым devDependency — пути GritQL-плагинов резолвятся
как ./node_modules/@verta/ui-gates/rules/*.grit от корня проекта
(pnpm-симлинк подходит).
Флаги ui-gates-check:
--root <dir>— что сканировать (по умолчанию.);--tokens <file>— откуда брать объявленные токены. Без флага берётся первыйglobals.cssпод--root. Файл.jsonчитается как shadcn-реестр: токены собираются изcssVarsвсех items и из деклараций внутри ихcss— так проверяются блоки, у которых своего CSS-файла нет.
Переменная, объявленная в самом файле — inline-стилем
(style={{ "--ray-left": … }}) или arbitrary-property в классе
([--sidebar-width:16rem]), — считается объявленной: per-элементную динамику
токен-файл выразить не может.
Что ловится
nursery/noInlineStyles,nursery/useSortedClasses(функции:cn,cva,clsx,tw) — Biome-правила;no-native-interactive.grit— нативные<button>/<input>/<select>/<textarea>/<a>внеsrc/shared/ui/**;no-direct-radix.grit— прямойimportиз@radix-ui/*внеsrc/shared/ui/**: Radix владеет copy-in кит, страницы ходят через shared UI;no-raw-color-classname.grit— arbitrary-цвета в классах:bg-[#…],text-[rgb(…)],ring-[oklch(…)]; цветофункция от токена —bg-[rgb(var(--accent))]— легальна;no-palette-color-classname.grit— палитра Tailwind в классах:bg-red-500,dark:text-slate-700,border-t-zinc-200/60,from-blue-500,!bg-amber-400; префикс утилиты открыт (border-t-,ring-offset-), имя цвета — одно из 22, шейд — числовой шаг, поэтому нецветовые числовые утилиты (z-50,duration-500) не ловятся, как и потребление токена с палитро-подобным именем (bg-(--color-red-500));no-arbitrary-size.grit— arbitrary px/rem-размеры в классах:w-[37px],text-[11px];calc(…),var(--…)и безразмерные (grid-cols-[…]) легальны;no-raw-color-css.grit— сырые цвета в CSS вне токен-файла (globals.css);no-wait-page-transition.grit—AnimatePresence mode="wait"на переходе роута: exit старого экрана доигрывает до конца, новый монтируется после — пустой кадр на каждой навигации. Роут опознаётся по ключу ребёнка (key={pathname},key={location.pathname}); свап на месте (key={isCopied ? …}, счётчики, бейджи) легален — там пустой кадр незаметен, а оверлей двух состояний хуже. Канон-замена — слоёный кроссфейд: обёрткаgrid,[grid-area:1/1]на анимируемом ребёнке,initial={false}, безmode;ui-gates-check— каждый токен изcontract/tokens.jsonобъявлен в:rootи.dark(не-цветовые — только:root); token-name гейт:var(--x), потреблённый в CSS или class-значениях без объявления в токен-файле (фантом-токен) — fail, defined-but-unused — report-only, внешние неймспейсы (--radix-,--tw-,--fd-) исключены; сырые цвета и arbitrary-размеры вcn()/cva()/template-строках/className, которые GritQL не видит; rawpx≥3px в CSS вне токен-файла (hairlines 0–2px,:root,@keyframesи prelude@mediaлегальны); палитра Tailwind в тех же динамических значениях.
Known limitations: text-white / bg-black/50 не ловятся — их несёт сам
shadcn-канон (destructive-вариант кнопки и бейджа), запрет развёл бы блоки
с upstream; палитра ловится только с числовым шейдом (-red-500);
интерполяция `bg-${tone}` / `w-[${n}px]` не
анализируется; строка, где смешаны токен-цвет rgb(var(--…)) и сырой цвет,
grit-правилом не ловится (динамический скан ui-gates-check ловит);
useSortedClasses не сортирует объект-пропсы clsx и часть вариантов — см.
доку Biome; no-wait-page-transition опознаёт роут по имени в ключе
(pathname/location) — ключ вида key={routeId} пройдёт молча, а
key={locationLabel} даст ложный красный.
Semver-политика
Якорь для всех UI-реп — версия в Forgejo npm.
- major — новое правило гейта или изменение состава
contract/tokens.jsonrequired-имён: ломает CI потребителей. - minor — backwards-compatible опция/улучшение диагностики/ослабление правила.
- patch — фикс правила или репорта без изменения поверхности.
Релиз-флоу
- В ветке:
pnpm changeset(описать изменение, выбрать bump по политике выше). - Там же:
pnpm changeset version(бампит версию, пишет CHANGELOG). - Мерж в
main→ CI (.forgejo/workflows/publish.yml) публикует недостающие версии в Forgejo npm (changeset publish, идемпотентно).
Peer-диапазон Biome запинован (>=2.5.2 <2.6.0): nursery-правила и GritQL
экспериментальны, бамп Biome = перепроверка фикстур.
Dependencies
Dependencies
| ID | Version |
|---|---|
| oxc-parser | ^0.139.0 |
Development dependencies
| ID | Version |
|---|---|
| @biomejs/biome | 2.5.2 |
| vitest | ^3.2.4 |
Peer dependencies
| ID | Version |
|---|---|
| @biomejs/biome | >=2.5.2 <2.6.0 |