Правила оформления компонентов — для плагина-проверки

Источник: ds-docs/component-contracts/rules/README.md

Здесь лежит эталонный список правил, по которым мы описываем компоненты, в двух видах:

component-rules.jsonмашинный каталог: 41 правило, у каждого — признак, строгость, сообщение дизайнеру и способ достать признак из Plugin API. Это источник истины
component-rules.mdтот же список для чтения; собирается из JSON командой node rules/render-md.mjs
lint-dump.mjsобразцовая реализация: применяет правила к выгрузке компонента и печатает нарушения
test-lint-dump.mjsnpm run lint:dump-test — пять случаев STR-004 по выгрузке: срабатывает на значке, покрашенном литералом, и молчит у логотипа, своей заглушки и значка, покрашенного переменной обоймы

Зачем это

Порядок работы: сначала плагин проверяет компонент дизайнера, потом мы снимаем контракт. Плагин ловит то, что чинится за минуту (имена, свойства, токены, версии), — и в разбор компонент приходит уже чистым. Иначе те же огрехи всплывают в дырах контракта, но чинить их поздно: макет уже разошёлся по продуктам.

Как устроено правило

{
  "id": "PROP-001",
  "title": "Boolean-свойство названо «Show …»",
  "severity": "warning",
  "category": "Свойства",
  "why": "…зачем это правило, словами из источника",
  "detect": "свойство типа BOOLEAN, имя которого не начинается со «Show »",
  "dump": "defs: {имя: {type: BOOLEAN}}",
  "figma": "componentPropertyDefinitions[key].type === 'BOOLEAN'",
  "message": "Логическое свойство «{name}» названо не по правилу «Show …»",
  "fix": "Переименовать в «Show {название слоя}»",
  "auto": true
}
  • severity: error — нельзя описать контрактом без потерь; warning — копит долг; info — повод посмотреть глазами;
  • detect — признак словами, чтобы правило можно было перечитать и оспорить;
  • dump / figma — одно и то же место в двух системах координат: наша выгрузка и Plugin API;
  • auto: false стоит там, где выгрузки не хватает: порядок свойств (нужны координаты узлов), стопы градиента.

Скелет плагина

import rules from "./component-rules.json";

type Finding = { rule: string; node: string; message: string; severity: string };

export function lintComponentSet(set: ComponentSetNode): Finding[] {
  const out: Finding[] = [];
  const say = (id: string, node: BaseNode, message: string) => {
    const r = rules.rules.find((x) => x.id === id)!;
    out.push({ rule: id, node: node.name, message, severity: r.severity });
  };

  // PROP-001 — логическое свойство «Show …»
  for (const [key, def] of Object.entries(set.componentPropertyDefinitions)) {
    const name = key.split("#")[0];                    // Figma добавляет к ключу «#id»
    if (def.type === "BOOLEAN" && !name.startsWith("Show "))
      say("PROP-001", set, `Логическое свойство «${name}» названо не по правилу «Show …»`);
  }

  // TOK-002 — отступ вне шкалы space
  const SPACE = new Set([0, 2, 4, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 56, 64, 80]);
  set.findAll((n) => "paddingTop" in n).forEach((n: any) => {
    for (const k of ["paddingTop", "paddingRight", "paddingBottom", "paddingLeft", "itemSpacing"])
      if (n[k] && !SPACE.has(n[k])) say("TOK-002", n, `Отступ ${n[k]} вне шкалы space`);
  });

  return out;
}

Дальше — цикл по правилам: у каждого в figma написано, что именно спрашивать у узла. Порядок вывода: сначала error, потом warning, потом info; внутри — по узлам сверху вниз, как в панели слоёв.

Проверить правила на живых данных

node rules/lint-dump.mjs dumps/badge.json          # один компонент
node rules/lint-dump.mjs --dir dumps --summary     # свод: сколько раз какое правило

Замер по базе на 17.09.2026 — 264 выгрузки, ровно по одной на контракт. Замер воспроизводится: снять выгрузки (node tools/refresh-dumps.mjs) и выполнить команду со сводом. Полный список — вывод команды; здесь первая дюжина:

TOK-005 скругление числом, а не токеном820наблюдение
LAY-006 высота не кратна 4332наблюдение
LAY-005 сетка или абсолютное положение231наблюдение
LAY-004 скрытые слои в дереве185наблюдение
NAME-003 лишние пробелы в имени146долг
TOK-001 отступ числом, а не токеном134долг
PROP-004 текстовое свойство вне словаря92наблюдение
TOK-002 отступ вне шкалы space91долг
AXIS-002 состояние вне словаря89долг
TOK-003 промежуток оставлен умолчанием Figma87наблюдение
PROP-002 подмена названа не «Swap … Container»58долг
TOK-006 цвет литералом57ошибка
VER-004 живой компонент собран на отменённом23ошибка
PLT-001 размер экрана прибит числом17ошибка

TXT-002 в этот замер не входит. Правило было переписано 09.09.2026: текстовый стиль Astra DS сам собран из переменных типографики, поэтому привязки на узле — норма, а не дубль. Дефект — своя переменная поверх стиля, и увидеть его может только плагин: он читает сам стиль и сравнивает. По выгрузке правило не проверяется.

LAY-001 и LAY-002 переписаны 09.09.2026. Прежние формулировки считали слой состояния соседом содержимого. На деле у него два вида: обойма, ВНУТРИ которой лежит содержимое (1478 узлов базы), и служебный инстанс ⚒︎ State Layer, который кладут абсолютно поверх, чтобы не плодить варианты (330 узлов). Ни к тем, ни к другим порядок в стопке и размер относительно родителя не относятся: правила давали 88 и 300 ложных срабатываний. Теперь LAY-001 ловит только пустую обойму-подкраску поверх содержимого, а LAY-002 проверяет другое и более полезное — чем слой красится в своём состоянии: наведение hover-bg-dark, нажатие press-bg-dark, фокус focus-bg. По базе это находит ровно те отклонения, что нашёл аудит: Numkey красит фокус наведением, Tab Item — нажатие фокусом.

Числа ниже прежних не потому, что систему починили. Прежний замер шёл по другому набору выгрузок (297 файлов, среди них отменённые компоненты и части семейств по отдельности), а этот — ровно по тем 264, что описаны контрактами. Сравнивать два замера между собой нельзя; сравнивать имеет смысл только повторы этого.

Это не приговор компонентам, а мера долга: половина найденного — следы приёмов макета (слои состояния, костыли обрезки, служебные обоймы). Поэтому у правил и есть три степени: плагину стоит показывать error крупно, warning списком, info — по запросу.

Чего в списке нет

Правила описывают оформление компонента, а не его смысл: плагин не скажет, что компонент лишний, что его надо было собрать из существующего или что состояние названо неудачно. Это остаётся дизайн-ревью. И отдельно: правила не проверяют содержимое gaps — дыры находит разбор, а не плагин.

Источники

  • ../reference/guidelines/component-authoring-rules.md — «Правила оформления компонентов», версия 2.0 (buildin.ai): атомарность, нейминг, свойства, слои, слой состояния;
  • ../reference/guidelines/naming-tags.md — теги-префиксы и суффиксы;
  • фундаменты — токены, слой состояния, системные элементы, платформа, версии, обёртки текста;
  • contract.mjs — валидатор контрактов: те же правила, но по другую сторону разбора.