@verta/ui-gates (3.0.0)

Published 2026-07-30 14:46:39 +00:00 by forgeadmin

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.gritAnimatePresence 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 не видит; raw px ≥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.json required-имён: ломает CI потребителей.
  • minor — backwards-compatible опция/улучшение диагностики/ослабление правила.
  • patch — фикс правила или репорта без изменения поверхности.

Релиз-флоу

  1. В ветке: pnpm changeset (описать изменение, выбрать bump по политике выше).
  2. Там же: pnpm changeset version (бампит версию, пишет CHANGELOG).
  3. Мерж в 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
Details
npm
2026-07-30 14:46:39 +00:00
2
latest
12 KiB
Assets (1)
Versions (5) View all
3.0.0 2026-07-30
2.0.0 2026-07-30
1.1.0 2026-07-28
1.0.0 2026-07-07
0.1.0 2026-07-06