Правила оформления компонентов — для плагина-проверки
Источник:
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.mjs | npm 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 высота не кратна 4 | 332 | наблюдение |
LAY-005 сетка или абсолютное положение | 231 | наблюдение |
LAY-004 скрытые слои в дереве | 185 | наблюдение |
NAME-003 лишние пробелы в имени | 146 | долг |
TOK-001 отступ числом, а не токеном | 134 | долг |
PROP-004 текстовое свойство вне словаря | 92 | наблюдение |
TOK-002 отступ вне шкалы space | 91 | долг |
AXIS-002 состояние вне словаря | 89 | долг |
TOK-003 промежуток оставлен умолчанием Figma | 87 | наблюдение |
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— валидатор контрактов: те же правила, но по другую сторону разбора.