astra-component-contract — Контракт компонента (YAML, нейтральный словарь)
Источник:
ds-docs/component-contracts/skills/astra-component-contract/SKILL.mdСправочники скилла — вложенные страницы. В вики не выложены, лежат в папке скилла:scripts/contract.mjs(валидатор, 100 КБ),scripts/figma-read.js. Командыnpm run parse,tools/*.mjs— из корняds-docs/component-contracts/.
Шапка скилла:
name: astra-component-contract
description: Читает компонент Astra DS из Figma и пишет его контракт — файл components/<slug>.yaml с осями, мастерами, анатомией, дельтами вариантов, привязками токенов, формами семейства и проброшенными наружу инстансами. Ключи английские, словарь раскладки нейтральный к платформе (не CSS). Использовать, когда просят «опиши компонент контрактом», «собери YAML по компоненту», «спарси компонент из Figma», «добавь компонент в базу контрактов».
Один компонент — один файл components/<slug>.yaml. Прозы в нём нет: контракт описывает то,
что нарисовано, в машинном виде, чтобы это мог проверить скрипт и прочитать генератор кода
под любую платформу.
Источник контракта — оригинал дизайн-системы, файл eGpqY0OqVR8l8zhmlHdfb9, а не рабочая
копия. Копия отстаёт: сверка семейства Navigation Bar показала два значка, переставленных
в оригинале и не переставленных в копии, — 140 вхождений. Копия нужна только там, где надо
писать в макет, то есть для кругового прогона.
Если у компонента есть карточка в reference/ того же репозитория, она второй голос: где проза
расходится с макетом, это отчёт человеку, а не повод править контракт под прозу.
Полнота контракта измеряется обратным ходом. Контракт полон, если по нему собрать компонент
заново и он совпадёт с оригиналом. Этим занят скилл
astra-contract-to-figma; протокол прогонов —
в round-trip.md.
Порядок
Сначала скрипт, потом слова. Механическая часть — чтение, черновик, дыры из фактов, проверки — делается одной командой и агента не требует:
FIGMA_TOKEN=… npm run parse -- --id <id набора> --slug <slug>
Она пишет components/<slug>.yaml, который уже сходится с макетом в ноль. Шаги ниже
описывают, что к этому добавляет разбор: смысл, сжатие повторов и дыры, которые видно
только глазом. Если контракт пишется руками без скрипта — шаги те же.
-
Что парсить и в каком порядке. Снять карту ярусов:
FIGMA_TOKEN=… node tools/tier-map.mjs <id узла> --out maps/<имя>.jsonОна даёт замыкание — что во что вложено — и очередь: описывать снизу вверх, от атомов к организмам, иначе ссылку на ребёнка не с чем сверить.
-
Откуда читать. По умолчанию — REST по оригиналу:
FIGMA_TOKEN=… node tools/read-component.mjs <id набора> --out dump.jsonДамп сразу годится для
--check-against. REST не тратит квоту MCP, читает оригинал и отдаёт всё, что нужно контракту: проброс наружу, привязки переменных, свойства инстансов, имя текстового стиля. Выгрузка плагина — второй путь, MCP — только для записи в макет. -
Если читать всё же через MCP — загрузить скилл
figma-use, без негоuse_figmaне вызывать. -
Прочитать формат контракта и словарь раскладки.
-
Разведка по чек-листу: что за узел по ссылке, оси и свойства, имена всех мастеров, деревья, проброшенные инстансы, окружение файла. Фрагменты запросов — в
scripts/figma-read.js. Не больше 6–8 мастеров за вызов, у семейства — чтение диффом. Выгрузку сохранить: фрагмент 7 отдаёт её в виде, который ест--check-against, вместе с картой переменных по id. Без неё сухая сверка на шаге 12 сравнивать будет нечего. -
Из имён мастеров вывести
countsиrules. Проверить арифметикой: правила должны давать ровноcount. Не сходится — искать дальше, а не писать «примерно». -
Решить, сколько в семействе форм: одно дерево или несколько. Признак — нужен ли
replaceцелого поддерева. Несколько —anatomyиvariantsуходят внутрьforms. Тем же признаком решается осьType Scale(Base/Scale): вид под крупную типографику остаётся дельтой, пока дерево одно, и становится формой, когда переверстан — см. масштаб типографики. -
Базовое дерево перевести в
anatomyпо словарю. Имя переменной — последний сегмент после/. Свойство без привязки — числом.Механическую часть — оси, свойства, решётку, базовое дерево и дельты — снимает черновик:
node tools/draft-contract.mjs dump.json --slug <slug> [--page "<страница>"] [--form-axis Type]Он сходится с выгрузкой в ноль расхождений сразу. Остаётся человеческое: вводный комментарий, осмысленные имена узлов, таблицы
tokensс привязками вместо повторяющихся дельт и разделgaps. Черновик — не контракт: без разбора он описывает, но не объясняет. -
Отличия остальных мастеров записать в
variantsдельтами. Значение, зависящее от оси, — таблицей вtokensи привязкой$имя. -
Узлы-инстансы:
of— slug контракта ребёнка;props— свойства инстанса;overrides— правки поверх;exposed— проброшен ли наружу. Инстанс обёртки текста («Text / …») контрактом не описывается: читалка сворачивает его в узелkind: textсfoundation: typography, а настройки — стиль, цвет, число строк, обрезку — кладёт вtext. Семейство описано в фундаменте обёрток. Прочитать не удалось —nullи запись вgaps. Если ребёнок уже описан, его свойства и значения сверяются с его контрактом: расхождение — ошибка. Поэтому описывать имеет смысл снизу вверх, от атомов к организмам. Ребёнок из подключённой библиотеки читается из её файла, а не из потребителя: в файле-потребителе у него пустое поддерево. Ключ библиотеки узнаётся черезGET /v1/components/<key>— полеfile_key; значки Astra DS лежат вIPwMyKXhxPhaJIMClmXM19(«Astra DS: Icons»). Вsourceпишется файл библиотеки и её id, а не тот, под которым набор виден потребителю. -
Слой состояния — обойма
State layer …либо инстанс⚒︎ State Layer— помечаетсяfoundation: state-layer. Абсолютный инстанс задаёт область нажатия родителя; его исчезновение в каком-то мастере значит, что там не нажимается. Правила записи — формат, устройство — фундамент. -
SYSTEM-слои контрактов не получают. Приставка
SYSTEMзначит «здесь встанет элемент платформы» — строка статуса, клавиатура, полоса жеста, рамка устройства. В чужом дереве такой узел пишетсяof: null,local: true,foundation: system-chrome; отдельный контракт на него не заводится. Почему — фундамент. -
Раздел Deprication Version не разбирается. Страницы
❌ List,❌ Sub-list,❌ Format Container / Text State— склад отменённого: у каждого набора там в имени написана живая замена. Контрактов им не пишут. Если отменённый набор всё ещё лежит внутри живого компонента, узел записываетсяof: null,local: true, а вgaps— чем его заменить. То же и с системными страницами (Activity View,Device,Keyboard,Scroll Bar): их рисует платформа, см. шаг 12. -
Версия внутри компонента: описываем только живую. У части наборов есть ось
Version(в файле встречается иVesrion) со значениями🟢 Stableи❌ Deprecated(а такжеDepricated,Description,Stabled— пишут по-разному). Обе версии держат в одном наборе нарочно: продукт переходит на новую сменой значения свойства, а не заменой компонента, — при свапе содержимое слетает. В контракт идёт только живая ветка: у оси остаётся одно значение, счётчики считают живых мастеров, правила про отменённых не пишутся, а вgaps— строка, сколько мастеров осталось за кадром. Черновик и сухая сверка отменённых мастеров уже не берут. Ось или свойство с крестом в имени (❌ Progress) остаются: без них живого мастера не собрать — валидатор о них предупреждает. Подробно — фундамент версий. -
Размер экрана — от платформы, а не от компонента. Локальная коллекция режимов (
Apple,Android,Bottom Nav Bar,Web,Internet Bank View,WebView) подставляет в макет размер устройства и отступы под системные полосы — это вариации показа, а не устройство компонента. Такой узел помечаетсяfoundation: platform, а размер пишется словом:size: {width: platform, height: platform}. ПределыminHeight/maxHeight, равные высоте экрана, не пишутся вовсе. Черновик делает это сам, валидатор предупреждает, если число экрана осталось. Подробно — фундамент платформы. -
Костыли обрезки хвоста —
⚒︎ Text Overflow Blur/Layer-01…03и⚒︎ Elilpses Loading/1…4со страницы Text. Внутри обёртки они не видны: обёртка сворачивается в текстовый узел сtext.truncate. Положенные в дерево руками — пишутсяof: null,local: true,foundation: typography, без своего контракта. Устройство — фундамент обёрток. -
Всё странное или отсутствующее — в
gapsклассомSUSPECTилиGAP. -
Записать файл
components/<slug>.yaml. Имя файла равно полюslug. -
Прогнать проверку:
node skills/astra-component-contract/scripts/contract.mjs components/<slug>.yaml --grid -
Сухая сверка — сохранить прочитанные деревья выгрузкой и прогнать
--check-against. Она сравнивает развёрнутый контракт с тем, что прочитано из Figma, и не требует ни одной записи в макет. Расхождения — ошибки. -
Проверить новизну —
--novelty. Если все конструкции контракта уже обкатаны круговым прогоном, прогон не нужен. Если появилась новая — нужен. -
У семейства проверить срезы: каждая форма обязана резаться в самостоятельный контракт.
--outкладёт по срезу на форму рядом с целым контрактом. -
Показать, что получилось, и остановиться. Если у компонента есть карточка в
reference/— перечислить расхождения отдельным списком: это находки для аудита, а не правки. В GitLab и на сервер ничего не выгружать.
Что скрипт умеет
contract.mjs <контракт.yaml> | разбирает YAML, проверяет |
contract.mjs <карточка.md> | достаёт машинный блок за маркером из карточки reference/ |
--docs <корень> | откуда брать выгрузку токенов и контракты детей |
--out <папка> | выгрузить в JSON — для генераторов кода и для рендера; у семейства ещё и срезы |
--grid | вывести решётку мастеров и наборов, сверить с count, sets и counts |
--form <id> | напечатать срез одной формы семейства — самостоятельный контракт |
--resolve "<мастер>" | развернуть один мастер в полное дерево |
--check-against <выгрузка.json> | сухая сверка: развернуть контракт и сравнить с деревом, прочитанным из Figma |
--novelty | показать конструкции, которые ещё не обкатаны круговым прогоном |
Что формат умеет
дельты set · add · remove · swap · replace · move | варианты как отличия от базы, а не копии |
привязки $имя по оси | одно значение на ось вместо повторов в каждом правиле |
slots и swapFrom | подмена вложенного компонента через свойство набора |
exposed | проброс свойств вложенного компонента наружу — публичный API |
text.style, overrides.textStyle | типографика только именем стиля файла; токены стиля — дело фундамента |
axes[].in: name | семейство: сегменты имени набора как измерения |
forms | семейство из нескольких структурно разных деревьев под общей шапкой |
hidden | узлы, скрытые по умолчанию |
gaps | всё, чего в макете нет или что в нём странно |
Проверка круговым прогоном
Что делать с непоследовательностью макета — выбор автора контракта. Записать её в gaps и жить
с расхождением, либо выразить узким правилом when и получить точное совпадение. Второе длиннее:
у Tag на четыре узла базового дерева пришлось 19 правил, из них семь — исключения на один-два
мастера, зато прогон сошёлся до нуля. Это про августовскую редакцию контракта: после
перестройки по оригиналу прогон 23.09.2026 дал 467. Ноль привязан к версии контракта,
а не к компоненту, и после перестройки его надо получать заново.
Второй вариант словаря — на будущее
Если решим, что CSS понятнее разработчикам: меняется только
layout-vocabulary.md и таблица имён в скрипте. Структура
контракта, операции, привязки, чтение Figma и проверки остаются.
Что уже описано
Контракты лежат в components/ в корне репозитория.
button | 5 осей, 273 мастера, подмена на индикатор, новая ветка слоёв, слой виброотклика в interaction, 21 находка |
tag | два слота подмены, замена подписи на маску и на скелетон, текстовые стили файла; прогон 23.09.2026 — 467, разбор в round-trip.md |
navigation-bar | семейство: 19 наборов, 76 мастеров, восемь форм под общей шапкой, 24 находки |