Формат документа компонента (references/document-format.md)

Источник: ds-docs/src/skills/astra-figma-to-md/references/document-format.md — справочник скилла astra-figma-to-md

Документ — чертёж. Читатель никогда не открывал Figma и открыть не может: по файлу он должен собрать компонент заново. Любое место, где документ отсылает «смотри в макете», — дыра.

Словари, от которых нельзя отступать

Формат рассчитан на разных исполнителей: один и тот же компонент, разобранный дважды, должен дать одинаковый файл. Там, где выбор конечен, он закрыт списком — свободной формулировки быть не должно.

Разделы документа и их порядок:

История изменений · Спецификация · Структура · Свойства · Анатомия · Варианты. Других разделов не бывает. Порядок фиксирован.

Строки узла и их порядок:

По умолчанию · Роль · Слот · Выключается · Свойства здесь · Переопределено · Задаётся снаружи · Зафиксировано · При изменении размера · Примечание · Отличие — затем раскладка: position · constraints · inset · top · right · bottom · left · align-self · z-index · flex-direction · align-items · justify-content · padding · gap — затем размеры: width · height · flex · flex-shrink · min-width · max-width · min-height · max-height · aspect-ratio — затем оформление: background · border-color · border-width · border-style · border-align · border-radius · corner-smoothing · box-shadow · backdrop-filter · filter · mix-blend-mode · transform · opacity · overflow — и типографика у текстовых слоёв: text-style · color · font-size · line-height · font-weight · text-align · text-overflow · white-space.

Строка с именем вне этого списка — ошибка. Порядок присутствующих строк — ошибка, если нарушен.

Производных чисел в прозе нет. Число пишется только там, где оно зафиксировано в макете — в строке свойства, токеном или жёстким значением. Размер, который складывается из содержимого, числом не приводится: «высота 72» верна ровно для одного наполнения, а разработчик прочитает её как константу и зашьёт. Вместо числа пишется, из чего размер складывается.

То же с шириной мастера: 375 в макете — размер холста, а не свойство компонента. В строке width пишется поведение (100%), а факт фиксации в мастере — в Задаётся снаружи.

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

Какие строки обязательны, а какие пишутся только при наличии:

ВсегдаТолько когда задано
position, flex-direction, align-items, justify-content, padding, gap, width, heightвсё остальное

Раскладка пишется целиком, включая нули: padding: space-none — это решение дизайнера, а не пустота, и разработчику надо знать, что отступа тут не должно быть. Остальные свойства при отсутствии просто не пишутся.

Строк со значением «нет» в документе нет. Правило выше делает пропуск однозначным: если border-radius обязан быть только при наличии, то его отсутствие в узле и означает, что скругления нет. Писать это отдельной строкой — тратить место на то, что уже сказано форматом.

По той же причине не пишутся значения, совпадающие с умолчанием: opacity: 1, overflow: visible.

Если у слоя нет auto-layout, строки раскладки опускаются целиком, а сам факт сказан в Роли. Обязательными остаются position, width и height.

Значения там, где выбор конечен:

positionstatic, absolute
overflowvisible, hidden
flex-directionrow, column
border-aligninside, outside, center

Вердикт в заголовке узла в разделе «Варианты»:

без изменений · свойства изменены · новый слой · слоя нет · вместо <Имя>. Ничего описательного в заголовке: «стало компактнее», «строка приподнята» — не вердикты. Всё объясняющее идёт строкой Отличие внутри узла.

Шапка: ровно три поля, в порядке Версия · Дата изменения · Источник. Дата — в формате ДД.ММ.ГГГГ, ЧЧ:ММ МСК.

Всё перечисленное проверяется scripts/check-card.js. Документ, который её не проходит, не готов.

Общее знание не повторяется

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

Что из этого следует:

  • вводной оговорки под шапкой нет: после Источник сразу ---;
  • общие правила фундаментов не пересказываются. Не «слой состояния — это 4 % чёрного под курсором и 8 % при нажатии», а просто имена токенов в дереве плюс ссылка на state-layer, если она к месту;
  • то же с токенами, сеткой, типографикой, хаптиками: ссылка — да, пересказ — нет.

Граница простая: что принадлежит этому компоненту — в карточке; что верно для всей системы — в фундаменте, и на него ссылаются. Если абзац можно без правки перенести в другую карточку, он лишний.

Имя токена

В документе стоит имя переменной из Local Variables — то, как она названа в Figma: bg-layer-01, hover-bg-dark, space-m, radius-ar. Без группы: путь colors/background/bg-layer-01 — это раскладка по папкам, а не имя.

Не code syntax. MCP отдаёт токены в виде var(--имя) — это CSS-представление, и оно не всегда совпадает с именем переменной. Отбросить var(--…) мало: там, где Figma подставила шаблон вместо имени, надо найти настоящую переменную.

Известный случай — слой состояния. MCP показывает var(--ИмяТокенаЗаднегоФона-hover) и -press; это не переменные, а шаблон из state-layer: «имя токена заднего фона» плюс суффикс. Настоящие имена берутся по значению:

ИмяТокенаЗаднегоФона-hoverчёрный 4 %hover-bg-dark
ИмяТокенаЗаднегоФона-pressчёрный 8 %press-bg-dark
—белый 8 %hover-bg-light · press-bg-light
—чёрный 8 % под фокусfocus-bg

У компонента со своей заливкой подсветка обычно своя: button-primary-hover, button-primary-focus, button-primary-press. Берётся то, что стоит в мастере.

Документ описывает дизайн, а не платформу. Как имя превратится в CSS-переменную, в токен Flutter или во что-то ещё — забота того, кто пишет платформенный компонент по этому документу. Ни var(--…), ни префиксов платформ в документе нет.

Переменной нет в выгрузке токенов — значит, выгрузка отстала, а не документ ошибся. Имя пишется как в Local Variables, добавляется строкой в ds-docs/foundations/tokens/not-exported.txt и отражается находкой MISMATCH в аудите.

Чего не видно в выводе MCP

get_design_context отдаёт узел рендером в React с Tailwind. Всё, чего Tailwind выразить не умеет, в этом выводе отсутствует — не помечено пропуском, а просто исчезает. Проверено: ни одного упоминания в ответах.

Эти свойства добирает scripts/fetch-details.js — узкий запрос к Figma REST только за ними. Если токена нет, остаётся аннотация дизайнера или вопрос к нему.

Выравнивание рамкиstrokeAlignМеняет способ реализации: внутрь — border, наружу — кольцо box-shadow
Сглаживание угловcornerSmoothingСквиркл вместо дуги; в CSS не выражается, нужен clip-path или SVG
Рисунок пунктираstrokeDashesПриходит только border-dashed, длины штриха и промежутка теряются
Толщина рамки по сторонамindividualStrokeWeightsРамка с одной стороны читается как сплошная
Привязки к краямconstraintsНужны у слоёв вне потока
Режим наложенияblendModeСлой может смешиваться с подложкой
Прозрачность заливкиfillOpacityОтличается от прозрачности слоя: гасится только фон
Угол и стопы градиентаgradientTransform, gradientStopsПриходят только крайние цвета
Обрезка текста по числу строкtextTruncation, maxLinesОпределяет, где появится многоточие
Масштабирование картинки в заливкеscaleModefill, fit, crop, tile — разное поведение при растяжении
Переопределения инстансаoverridesКакие поля правили поверх компонента: цвет, размер, подменённое содержимое

Как с этим быть. Свойство пишется в узел, когда оно задано и влияет на реализацию:

  • border-align — при любой рамке толще одного пикселя, а также всегда, когда обводка внешняя;
  • corner-smoothing — когда не ноль;
  • border-style — когда рамка не сплошная, вместе с длинами штриха и промежутка;
  • constraints — у слоёв вне потока.

Если значение не удалось получить, а оно влияет на вид, — это находка в ds-internal/audit/, одна на компонент, а не на каждый слой. Выдумывать значение по умолчанию нельзя: «inside» выглядит правдоподобно, но при внешней обводке даст другую геометрию.

Единица документа

Файл на компонент, все файлы лежат плоско. Подпапок в components/ нет.

components/
├── avatar.md                 одиночный компонент
├── basic-list.md             ядро семейства — здесь же обзор состава
└── basic-list-item.md        вложенный компонент со своей документацией

Папки по разделам Figma не заводятся, и вот почему. Имя раздела часто не равно имени компонента: раздел «Navigation Bar» содержит Search Bar и Select City, раздел «Search Input» — Small Search Input. Папка по разделу разложила бы компоненты по тому, где их нарисовали, а не по тому, что это. Плюс большинство разделов дают ровно одну карточку — вышла бы сотня папок с одним файлом.

Родство показывается не папкой, а связью: обзор состава в ядровой карточке и относительные ссылки между документами. Как назвать файл, когда в имени набора есть слеш, — в figma-conventions.md, раздел «Слеш в имени набора».

Находки разбора рядом с карточкой не лежат: их место — отдельная папка.

ds-internal/audit/
├── README.md                 указатель: раздел, файл, число находок, карточки
├── audit-avatar.md           один файл на раздел Figma
└── audit-basic-list.md       раздел дал несколько карточек — находки всё равно вместе

Обзор семейства живёт в файле ядрового компонента, отдельного README не создаётся. Ядро — тот компонент, внутрь которого складывают остальные.

Ссылки между файлами — обычные относительные markdown-ссылки, это навигация внутри базы. Ссылка на Figma в документе одна и стоит в шапке, вместе с остальными метаданными.

Шапка

Метаданные — списком, по одному на строку. Не сводить их в одну строку через разделители: строка длинная, читается плохо и плохо правится.

# Имя Компонента

- **Версия:** 1.7.0
- **Дата изменения:** 18.08.2026, 11:47 МСК
- **Источник:** [Figma MCP, Section «Имя Section»](<ссылка>)

Три строки — и сразу ---. Никакой вводной оговорки под шапкой нет.

Версия — версия компонента из журнала изменений. Дата изменения — когда обновлялся этот документ, а не когда правился макет. Дата и время по Москве: документ правят из разных часовых поясов, и без пометки непонятно, чья версия свежее. Формат — ДД.ММ.ГГГГ, ЧЧ:ММ МСК. Источник — markdown-ссылка, где текстом стоит «Figma MCP, Section «…»». Голого URL в документе нет: ссылка одна и она здесь. Ниже по документу ссылок на Figma нет ни одной.

Платформы в шапке не указываются: компоненты делаются сразу под веб и мобильные, и строка об этом ничего не сообщала бы. Если платформенное различие есть, оно описывается там, где проявляется, — свойством или отличием варианта.

Оговорка обязательна: она задаёт границу документа и снимает половину вопросов «а почему тут не написано про токены».

Разделы

Заголовки нейтральные, без терминологии документатора.

## История изменений
## Спецификация
   <описание — без заголовка>
   ### Структура                    только в ядровом компоненте
   ### Свойства                     индекс: состав, значения, умолчания
   ### Анатомия                     дерево слоёв, свойства и компоненты по умолчанию
   ### Варианты                     по свойствам, только отличия
   ### Интерактивность              виброотклик: событие, идентификатор, обязательность

Порядок повторяет Section в макете: История изменений → Спецификация → ❖ Компонент. «Спецификация» — обёртка над всем, что описывает сам компонент.

Читаются четыре блока Section: «История изменений», «Спецификация», «❖ Компонент» и «Интерактивность». Остальное — «Рекомендации», «Доступность», «Примеры» — в документ не идёт и находками не считается: этих данных мы пока не документируем.

Блок «Анимация» — исключение на будущее: его планируется описывать, но сейчас он тоже не читается.

История идёт первой: читатель сразу видит, живой компонент или его давно не трогали, и не ищет эту таблицу в конце длинного файла.

Обязательный минимум: описание, «Анатомия», «Свойства», строка источника. Остальное — по наличию данных: раздел без содержания не пишется, фразы «данных нет» не пишутся тоже.


Описание — без заголовка

Первый абзац внутри «Спецификации», собственного заголовка не имеет: первый же абзац документа и есть ответ на вопрос «что это». Отдельный заголовок здесь только добавляет ступеньку перед текстом.

Задача пользователя, границы применения. Отдельным абзацем — чем варианты отличаются по смыслу, а не по цвету.

Перечня сценариев не пишем. Список вида «история операций, курсы валют, настройки» выглядит информативным, но ничего не добавляет: он либо повторяет описание, либо перечисляет случайные экраны продукта. Если сценарий действительно задаёт границу применения — он идёт фразой в описание.

Демо-наполнение из макета (Label, Default Value, рыба) описанием не является и в документ не попадает.

Структура

Дерево состава, таблица «компонент → что делает → когда брать», общие правила семейства и устаревшая часть с указанием замены.

В дереве только вложенность. Варианты и состояния вложенного компонента в дерево не идут: Type, State и прочие оси принадлежат ему самому и описаны в его документе. Дерево отвечает на вопрос «что внутри чего лежит», а не «каким оно бывает».

Если возможности компонента зависят от того, чем его наполнили, — это пишется отдельным абзацем и таблицей «какое наполнение → что получает компонент», а не ветками дерева. Формулировка должна разделять две вещи: чего умеет сам контейнер и что приходит вместе с наполнением.

Если варианты различаются не оформлением, а раскладкой, — сказать об этом здесь одной фразой и отослать к «Вариантам» за значениями.

Устаревшее — одним абзацем и не глубже. Что помечено ❌, сколько таких мастеров и чем заменено. Ни слоёв, ни свойств, ни размеров устаревших вариантов в документе быть не должно: документ описывает живой компонент. Признак ошибки — ❌ где-либо, кроме этого абзаца.

Блоки спецификации

Frame «Спецификация» в макете почти всегда разбит на именованные блоки: заголовок ds-paragraph и абзац под ним. Обычно блок назван по свойству — Action Container, Show Header Container, is Web — или по правилу, которое в слоях не видно: формула отступа, ограничение по высоте, пометка «это только для дизайнеров».

Такие блоки читаются и попадают в «Структуру» подблоком. Дерево слоёв показывает, что нарисовано; блок спецификации говорит, что с этим можно делать и чего нельзя, — этого в слоях нет, и без него документ неполон.

Подблок называется #### Что сказано в спецификации и устроен так:

  1. Таблица «Блок → что в нём сказано» — по строке на каждый блок, своими словами, одним предложением. Порядок строк — как блоки идут в макете.
  2. Под таблицей — абзац на каждый блок, который несёт настоящее ограничение: формулу, предел, запрет, договорённость о наполнении. Здесь можно подробно: что именно ограничено, каким числом, что будет, если правило нарушить.

Что в подблок не идёт:

  • блок Z-index и любые числа глобальной шкалы наложения (200, 325, 400, 600, 900) — ни одно из них не проставлено в макете, между спецификациями они противоречат друг другу, и проверить их нечем. Блок пропускаем целиком: ни строки в таблице, ни абзаца, ни упоминания числа в прозе. Порядок слоёв внутри компонента это правило не затрагивает — он читается из макета и остаётся в анатомии;
  • блок, который пересказывает дерево слоёв («компонент состоит из шапки и содержимого») — такое уже сказано анатомией;
  • инструкция для работы в Figma («используйте DevMode»), кроме случая, когда она объясняет назначение оси, — тогда это довод в пользу того, что ось служебная;
  • перечень сценариев продукта — правило описания действует и здесь.

Дословная цитата — редкость. Пересказываем своими словами. Кавычки ставим, только когда важна сама формулировка: признание «это свойство нужно только для дизайнеров», обещание, которого нет в мастерах, или фраза, на которую опирается находка аудита.

Расхождения не сглаживаем. Если блок обещает то, чего в мастерах нет, — в карточке остаётся то, что нарисовано, а расхождение уходит находкой в аудит. Формулировка в карточке при этом честная: «спецификация обещает X, в мастерах этого нет».

Свойства

Порядок — как расположены в компоненте слева направо (правило макета).

Таблица: свойство, тип (Variant / Boolean / Instance swap / Text), значения, по умолчанию, что делает. Имена — по конвенции: Show Имя слоя, Swap Имя Container.

Для слотов и свапов в колонке «Значения» пишется «любой компонент» или «любое содержимое», а в колонке «По умолчанию» — тот компонент, что стоит в мастере. Slot и Swap описываются одинаково: разница между ними существует только в редакторе, контракт у них один.

Чего нет в «Свойствах»

Ось Format со значениями Layout и Prototype в индекс свойств не попадает. Это разметка для дизайнера, а не свойство интерфейса:

LayoutВсё содержимое страницы целикомDesign375 × NNN
PrototypeСодержимое, урезанное по высоте под экранPrototype375 × 812

Документируется только ветка Prototype: она отражает то, что пользователь видит на экране. Подраздела по этой оси в «Вариантах» тоже нет. Какую ветку брать — подтверждается у пользователя до разбора.

Правило узкое: оно про ось с именем Format и значениями Layout и Prototype, и больше ни про какую. Оси с другими именами — включая те, что тоже называются Prototype, — обычные свойства и документируются наравне с остальными.

Анатомия

Дерево слоёв. Имена — из макета, очищенные по правилу figma-conventions.md: без квадратных скобок, без слова Slot, без значков атомов и служебных слоёв. Скобки в имени слоя означают слот и заменяются знаком ▣ перед именем — дважды одно и то же не пишется.

Заглушка слота в дерево не переносится как компонент. Инстанс Swapper Utility или Swapper означает «здесь пусто, сюда подставляют чужое». Пишется это строкой у самого слота, а не отдельным узлом с именем заглушки. Справа коротко, за что слой отвечает и можно ли его выключить или заменить.

Basic List Item                        оболочка: фон, обрезка содержимого
└── State layer                        раскладка строки · вся площадь — зона нажатия
    ├── Left Container                 заменяемый · выключаемый
    │   └── Icon Container             по умолчанию: иконка 24×24

Компоненты по умолчанию стоят в дереве, а не в отдельном разделе. То, что засвапнуто в слот в мастере, — такой же узел дерева, только помеченный «по умолчанию». Он лежит там же, где лежит в макете, и несёт четыре строки: что это, роль в этом компоненте, значения его собственных свойств здесь, отсылка к его документации. CSS-свойств у такого узла нет — они принадлежат ему, а не документируемому компоненту.

Границы описания — обязательный абзац. Документ описывает только собственные слои компонента. Вложенный компонент библиотеки (Icon Container, Badge, Checkbox, любой другой со своей документацией) в дереве присутствует, но его внутренние отступы и размеры не пересказываются: они принадлежат ему, а не документируемому компоненту, и разъедутся при первом же его изменении. Исключение — если документируемый компонент что-то у вложенного переопределяет: тогда пишется только переопределение.

Здесь же — короткие заметки, которые иначе некуда положить: слой без собственного выключателя, скрытая зависимость между слоями.

Слои складываются друг в друга, а не идут списком. Блок слоя — раскрывающийся узел <details>, вложенный в узел родителя. Свёрнутое дерево показывает устройство компонента целиком, раскрытый узел — свойства слоя. Отдельная схема анатомии при этом не нужна: дерево и есть схема.

<details open>
<summary><code>Basic List Item</code> — оболочка строки</summary>

- **Роль:** …

<details>
<summary><code>State layer</code> — раскладка строки и зона нажатия</summary>

- **Роль:** …

<details>
<summary><code>Left Container</code> — заменяемая область под иконку</summary>
…
</details>
</details>
</details>

Корневой узел ставится open, остальные закрыты. Вложенные <details> не отбиваются пробелами: четыре и больше markdown примет за блок кода и сломает разметку. Вложенность держится тегами, а не отступом.

Цена решения: слои не попадают в оглавление документа, у них нет заголовков. Взамен получается кликабельная карта компонента, повторяющая макет один в один.

Блок на каждый слой, в порядке вложенности из «Анатомии». Не сводить слои в общую таблицу: слой — самостоятельная сущность со своим набором свойств, и в матрице это теряется. Родитель указывается явно.

Свойства названы как в CSS. Не «Отступы» и «Промежуток», а padding и gap. Читатель документа — тот, кто будет это верстать: он думает в терминах свойств, а не в терминах редактора. Перевод в человеческие слова добавляет ступеньку и порождает разночтения.

<summary><code>State layer</code> — раскладка строки и зона нажатия</summary>

- **Роль:** выстраивает содержимое; поверх лежит покрытие состояния на всю площадь
- **flex-direction:** row
- **align-items:** center
- **justify-content:** flex-start
- **padding:** space-m
- **gap:** space-m
- **width:** 100%
- **height:** auto
- **min-width:** нет
- **max-width:** нет
- **min-height:** нет
- **max-height:** нет
- **background:** нет
- **border-color:** нет
- **border-width:** нет
- **border-radius:** нет
- **opacity:** 1
- **overflow:** visible

Порядок: Роль · flex-direction · align-items · justify-content · padding · gap · width · height · flex · flex-shrink · min-width · max-width · min-height · max-height · background · border-color · border-width · border-radius · opacity · overflow. По месту добавляются box-shadow, mask, типографика текстового слоя.

Позиционирование и привязки — такие же свойства, как отступы. Figma умеет больше, чем auto-layout: слой можно вынуть из потока, прижать к краям, повернуть, задать пропорции. Всё это принадлежит слою и пишется в его узле.

  • position — пишется всегда: static, если слой лежит в потоке auto-layout, absolute, если вынут из него.
  • inset, либо top / right / bottom / left — только у слоёв вне потока.
  • constraints — привязка к краям родителя. CSS-эквивалента нет, поэтому имя оставлено фигмовским. Пишется только у слоёв вне потока: у детей auto-layout привязки не работают, и строка «не применяются» была бы шумом.

По наличию добавляются: align-self, z-index, transform (поворот), box-shadow, filter (размытие), mix-blend-mode, aspect-ratio, а у текстовых слоёв — font-size, line-height, font-weight, text-align, text-overflow, white-space.

Порядок в узле: сначала прозаические строки, затем раскладка (position → constraints → flex-direction → выравнивания → padding → gap), затем размеры, затем оформление.

Компонент по умолчанию ссылается на свой документ прямо именем. Имя в заголовке узла оборачивается в якорь, отдельной строки со ссылкой нет:

<summary>◆ <a href="./badge.md"><code>Badge</code></a> — по умолчанию</summary>

Якорем, а не markdown-ссылкой: разметка внутри <summary> разбирается не всеми рендерерами, а <a href> работает везде. Знак типа остаётся перед ссылкой, имя внутри <code> — поиском по имени слой по-прежнему находится.

Ссылка ведёт на файл по слагу компонента. Если такого документа ещё нет, ссылка всё равно ставится, а сам факт идёт находкой в audit.md: битая ссылка честно показывает дыру в базе, молчание — нет.

Аннотация дизайнера ложится в строку по смыслу, а не отдельным блоком. «Можно менять отступы» — это Задаётся снаружи. «Этот контейнер нельзя выключать» — Зафиксировано. «При длинном тексте растёт» — При изменении размера. Пояснение назначения — Роль.

Если содержание аннотации не ложится ни в одну строку, пишется Примечание — дословная выдержка. Это последнее средство: в большинстве случаев аннотация ложится в существующую строку, и тогда она туда и идёт.

Отдельного раздела с аннотациями нет: они относятся к конкретным слоям, а значит живут в узлах этих слоёв.

Значение живёт там, где действует. Всё, что относится к конкретному слою, пишется в его узле, а не выносится списком ниже. Отдельных разделов «Определяется извне», «Зафиксировано» и «Поведение при изменении размеров» нет: они пересказывали те же токены во второй раз, и при первой же правке макета одна из копий устаревала.

Для этого у узла есть три необязательные строки — сразу после «Роли», «Слота» и «Выключается»:

  • Задаётся снаружи — что приходит родителем или потребителем, а не задано в компоненте: ширина, заливка, содержимое области.
  • Зафиксировано — что менять нельзя и почему ломается. Пишется у того слоя, которому свойство принадлежит, рядом с самим значением.
  • При изменении размера — как слой ведёт себя при сжатии и росте: кто забирает подвижность, что переносится, что обрезается.

То же в разделе «Варианты»: если значение свойства меняет поведение слоя, это пишется в узле отличия, а не отдельным абзацем.

«Свойства» остаются индексом: состав, тип, значения, умолчания. Что каждое свойство делает — уже сказано в дереве и в отличиях, здесь не повторяется.

Слой, принимающий чужое содержимое, обязан это объявить. Если у слоя есть Instance swap или он объявлен слотом, в блоке идут две дополнительные строки — сразу после «Роли»:

- **Роль:** место для опознавательной иконки слева
- **Слот:** принимает любой компонент, не только иконку. Свойство `Swap Left Container`,
  по умолчанию `Icon Container`
- **Выключается:** свойством `Show Left Container`

Формулировка «принимает любой компонент» обязательна. Без неё роль вида «принимает иконку» читается как ограничение состава, хотя туда можно положить что угодно — а это главное свойство конструктора. Компонент, стоящий в мастере, называется только как значение по умолчанию.

Если слой выключается, но своего выключателя не имеет, так и пишется: «своего выключателя нет, скрывается вместе с Middle Container». Это факт, который иначе всплывает только при вёрстке.

«Роль» отвечает только на вопрос «зачем слой». Слоты, выключатели и значения по умолчанию живут в своих строках и в «Роли» не дублируются.

Значение пишется так, как оно задано в источнике.

  • Свойство привязано к токену — пишется имя токена как в макете: space-m, bg-list-primary, radius-l. Без префикса --: в макете переменная называется без него, а приставка — деталь конкретной реализации, которую документ навязывать не должен.
  • Привязки нет — пишется число в пикселях: 24px. Отличить одно от другого можно на глаз, поэтому пометок «без токена» не нужно.
  • Свойство не задано — «нет». Не прочерк и не пропуск строки.

В значении только значение. Никаких пояснений вроде «нет — фон дают строки» или «зафиксировано минимумом и максимумом»: строка свойства несёт технический факт. Всё, что нужно объяснить, идёт в Роль или в текст под деревом.

Описывается базовый вариант. Чем отличаются остальные — в «Вариантах».

Под блоками — короткий разбор, из чего складывается высота компонента.

Переопределения инстанса

Инстанс другого компонента почти никогда не вставляют как есть. Ему меняют вариант, подменяют вложенное содержимое, перекрашивают слой, растягивают на всю ширину, прячут часть. Всё это — часть описания использующего компонента, а не того, что используют.

Поэтому у узла-инстанса две разные строки:

  • Свойства здесь — значения свойств компонента: вариант, булевы, свапы, текстовые. В реализации это то, что передаётся пропсами.
  • Переопределено — прямые правки поверх компонента. В реализации это то, что придётся дописывать стилями, потому что через API компонента так не выражается.

Различать обязательно: первое — штатное использование, второе — вмешательство в чужой компонент.

Что считается переопределением. Перечень закрытый — проверять по нему каждый инстанс:

Растянут по ширинеflex-[1_0_0], w-full у инстанса«растянут на всю ширину родителя»
Задан размерширина или высота, отличная от собственной«ширина 343 вместо 390»
Прозрачностьopacity-* на инстансе«непрозрачность 60 %»
Цвет слоя внутризаливка отличается от умолчания компонента«значок перекрашен в fg-disabled»
Подменён тексттекст отличается от умолчания«подпись заменена на «Отмена»»
Скрыт слой внутриhidden у потомка инстанса«вторая строка скрыта»
Подменён вложенный компонентдругой инстанс внутри«значок подменён на Star»

Запись ярусами: что было у компонента и что стало здесь

Обе строки записывают два яруса: значение у самого компонента и значение в этом месте. Без первого яруса запись бесполезна — читатель не отличит решение от умолчания.

Обозначение одно на обе строки:

Значение совпало с умолчанием компонентаИмя = Значение
Значение изменено здесьИмя: умолчание → здесь

Разделитель между элементами — ·. Стрелка в элементе означает ровно одно: здесь принято решение, отличное от компонента. Её отсутствие — что взято как есть.

- **Свойства здесь:** `Type = Darkening` · `Size`: `Medium` → `Small`
- **Переопределено:** `width`: 190 FIXED → 100% FILL · `align-self`: INHERIT → STRETCH

Читается так: Type не трогали, это умолчание Overlay; Size закреплён родителем; ширина растянута, хотя компонент рисуется фиксированной.

Пропсы проверяются все. Не только те, что переключили: в строку идёт каждая ось и каждый булев переключатель инстанса. Ось, оставленную в умолчании, тоже надо назвать — иначе непонятно, проверяли её или пропустили.

Молчать нельзя. У каждого узла-инстанса стоят обе строки, Свойства здесь и Переопределено, и у каждой есть служебное значение на случай пустоты:

Свойства здесьу компонента нет ни одного свойствасвойств нет
Свойства здесьвсе свойства в умолчаниивсё по умолчанию
Переопределенопроверено, расхождений нетсовпадает с умолчанием
обепроверить не удалосьне проверено

Отсутствие строки и не проверено означают разное: первое читается как «забыли», второе — как честно отмеченный пробел. Пустой узел — утверждение, которое надо было проверить, а не пропустить.

Размер, названный в строке По умолчанию («стоит контейнер значка 24 × 24»), — это не переопределение, если он совпадает с собственным размером компонента. Если не совпадает — переопределение, и оно идёт в Переопределено со стрелкой.

Откуда берётся первый ярус. Умолчание компонента читается из его карточки — отсюда порядок разбора снизу вверх. Сам факт расхождения берётся из instance.overrides через use_figma: get_design_context отдаёт инстанс уже применённым, и отличить «здесь так решили» от «так у компонента» по его выводу нельзя. Подробный сбор — в instance-overrides.md.

Много переопределений — находка. Если у инстанса переписана половина свойств, компонент используют не по назначению: либо нужен новый вариант, либо не тот компонент выбран. Такое идёт в audit.md классом SUSPECT.

Отличить правку от умолчания можно только по документу вложенного компонента — отсюда порядок разбора снизу вверх. Документа нет — переопределения не проверены, и это пишется находкой, а не замалчивается.

Размеры: заданное против получившегося

Пишется то, что задано в макете, а не то, что вышло на экране. Разница принципиальная: заданное — правило компонента, оно переживёт смену содержимого; получившееся — снимок одного состояния, и при другом тексте оно другое.

get_metadata отдаёт каждому слою прямоугольник width × height. Это результат раскладки, а не свойство слоя. Брать высоту оттуда нельзя ни при каких условиях: числа выглядят правдоподобно и потому особенно опасны — читатель примет их за правило.

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

Пример из Chips:

# Chips — оболочка
- **padding:** 0 space-xs        ← задано
- **height:** auto               ← обтягивает содержимое
- **min-height:** 32             ← задано, ниже не станет

  # Content — содержимое
  - **height:** auto
  - **min-height:** 24           ← задано
  - **max-height:** 24

Высота 32 в документе не написана ни разу — и не должна быть. Она читается: обойма зажата минимумом 32, внутри содержимое 24. Напиши мы height: 32, документ утверждал бы, что высота задана числом, — и разработчик зафиксировал бы её жёстко, сломав компонент на длинном тексте.

Три сценария, и пишется тот, который задан. Компонент по каждой оси живёт в одном из трёх режимов, и все три встречаются в библиотеке постоянно:

Стоит фиксированного размераFIXEDwidth: 240
Заполняет то, во что положенFILLwidth: 100%, а в авторазметке — flex: 1 0 0
Обтягивает содержимоеHUGwidth: auto

Один компонент бывает в разных режимах в разных местах. Button сам по себе стоит заданной ширины; внутри Group Button он заполняет всё свободное место. Это не противоречие: собственный режим компонента пишется в его карточке, режим в конкретном месте — в узле родителя строкой Переопределено, ярусами: width: 240 FIXED → 100% FILL.

По вертикали такие компоненты почти всегда обтягивают содержимое — height: auto, а высота читается из отступов и внутренних размеров.

Режим берётся из файла, а не из картинки. Источник — layoutSizingHorizontal и layoutSizingVertical через use_figma. Ни прямоугольник из get_metadata, ни отрендеренный CSS для этого не годятся: оба показывают FILL и HUG уже посчитанными, конкретным числом, неотличимым от FIXED.

Ширина мастера — не доказательство FIXED. Мастера стоят внутри документационной раскладки раздела, и их габарит нередко задан сеткой примера. У Format Container / Text Container три мастера лежат колонками по 240 в блоке шириной 808 — 240 здесь ширина колонки. Если разбор уже записал width: 100%, а прямоугольник мастера 240, верно первое: 100 % — это про поведение, 240 — про то, как компонент показали в документации.

Когда число писать обязательно:

Размер задан фиксированнымширина/высота FIXEDwidth: 190
Задан пределmin/max проставленыmin-height: 32
Размер привязан к токенузначение из переменнойwidth: space-2xl
Слой тянется за родителемFILLwidth: 100% или flex: 1 0 0
Слой обтягивает содержимоеHUGheight: auto

Последние две строки — те, на которых чаще всего ошибаются: и FILL, и HUG дают в метаданных конкретное число, и оно ничего не значит.

Итоговый габарит компонента — не свойство корня, а вывод. Если его стоит назвать, он идёт абзацем под деревом: «высота 32 складывается из содержимого 24 и полей по 4». Абзац объясняет, дерево остаётся честным.

Тип слоя

Перед именем в дереве стоит знак типа. Без него Container у одного компонента и Container у другого выглядят одинаково, хотя первый — узел slot, а второй — обычный фрейм со свойством Instance swap. Для реализации это разные вещи.

#FRAME, COMPONENTконтейнер с раскладкой
▣SLOTобласть наполнения, содержимое приходит снаружи
TEXTтекстовый слой, к нему применяется текстовый стиль
○RECTANGLE, ELLIPSE, VECTOR, LINEфигура, обычно рисуется как элемент оформления
◆INSTANCEинстанс другого компонента, на него ведёт ссылка
△GROUPгруппировка без раскладки; в дизайн-системе обычно ошибка макета

Знак ставится перед именем и вне <code>, чтобы имя слоя осталось точным и искалось поиском: <summary># <code>State layer</code> — раскладка строки</summary>.

Легенда обязательна в каждом документе — одной строкой в начале «Анатомии». Документ читают в отрыве от этого шаблона, и расшифровка знаков должна быть под рукой.

Это не то же самое, что префиксы на канвасе. Те (✱, ⚒︎, скобки, слово Slot) помечают роль слоя для дизайнера и в документ не переносятся. Знак типа отвечает на другой вопрос — чем узел является технически, — и нужен именно разработчику.

Типографика

Если на тексте стоит текстовый стиль — пишется его имя, и только оно: text-style: Title/H5. Составляющие стиля — font-size, line-height, font-weight, letter-spacing — не пишутся. Они принадлежат стилю и разъедутся при первой же его правке; смысл стиля именно в том, что он единый источник этих величин.

Токен пишется именем, и только именем. Значение свойства — то, как оно записано в макете, включая коллекционные пути со слэшами: colors/base/transparent, basic/radius-none. Словом — «прозрачный», «белый», «без фона» — не пишется никогда: слово нельзя ни проверить скриптом, ни подставить в код, и оно скрывает, что за ним стоял настоящий токен. Если привязки к токену нет — пишется само значение (transparent, #171a1c, число), и это уже означает «токена нет».

Цвет в стиль не входит. В Figma текстовый стиль несёт гарнитуру, размер, начертание, интерлиньяж и трекинг, а цвет — это заливка. Поэтому цвет пишется отдельной строкой всегда, независимо от того, есть стиль или нет.

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

Как определить, какой стиль применён. MCP перечисляет текстовые стили узла отдельным списком вместе с их составом. Слой сопоставляется со списком по набору токенов: совпал состав — стиль тот самый. Не совпал ни с одним — стиля нет.

Типографика может приходить компонентом. Иногда текст в макете — не слой со стилем, а вложенный компонент типографики: он несёт число строк, обрезку многоточием, размытие и прочее поведение, которого у простого текстового слоя быть не может. Тогда он стоит в дереве узлом «по умолчанию», а его настройки идут в Свойства здесь.

Различать эти два случая обязательно: в первом разработчик применяет текстовый стиль к своему тексту, во втором — берёт готовый компонент со всем его поведением. По документу это должно быть видно сразу.

Варианты

Раздел строится по свойствам компонента, в том же порядке, что и в «Свойствах». Подраздел на свойство, внутри — его значения. Не таблицей: значение может менять структуру, а не только цвет, и таблица это скрывает.

Каждое значение свойства — тоже раскрывающийся узел. Не абзац с жирным именем, а <details> наравне с узлами слоёв: свёрнутый список значений читается как оглавление свойства, раскрытое значение показывает отличия.

Узел значения помечается <b>, узел слоя — <code> со знаком типа. По этой разнице документ и проверка их и различают: у значения нет ни знака типа, ни вердикта — они бывают только у слоёв.

<details>
<summary><b>Default</b> — базовая структура</summary>

Структура совпадает с базовой — целиком описана в [«Анатомии»](#анатомия).

</details>

«Базовая структура» обязана вести ссылкой на «Анатомию». Без ссылки читатель видит только слова и может не связать их с деревом выше — особенно если открыл документ на середине или пришёл по ссылке на конкретное свойство. Одна строка с якорем снимает вопрос.

Отличия описываются тем же деревом узлов, что и анатомия. Один формат на весь документ: читатель, разобравшийся с анатомией, читает варианты без переучивания. Отличие от анатомии одно — в узлах пишутся только изменившиеся свойства, а не весь набор.

**`Read Only`** — строка не должна быть кликабельной.

<details open>
<summary><code>Basic List Item</code> — без изменений</summary>

<details>
<summary><code>State layer</code> — свойства изменены</summary>

- **Отличие:** снято покрытие, принимавшее нажатие. Больше ничего не меняется

</details>
</details>

Правила узла в дереве отличий:

  • что произошло — в заголовке узла, вердиктом из закрытого списка: без изменений, свойства изменены, новый слой, слоя нет, вместо <Имя>. Ничего описательного: «стало компактнее», «строка приподнята» — не вердикты;
  • в теле — только изменившиеся свойства, теми же CSS-именами. Совпадающие не повторяются;
  • слои, через которые проходит цепочка, помечаются без изменений и оставляются пустыми — они показывают, где в дереве лежит изменившийся слой;
  • если объяснить нужно больше, чем помещается в свойство, — строка Отличие.

Значение, ничего не меняющее, описывается строкой «базовая структура» — без дерева. Молчать о нём нельзя: читателю нужно знать, что отличий нет, а не гадать, забыли их описать или их нет.

Отличия бывают трёх родов, и все три показываются одинаково:

  • свойство слоя поменялось — пишется новое значение;
  • слой появился или исчез — вердикт новый слой или слоя нет;
  • в слот подставлен другой компонент — вердикт вместо <Имя>.

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

Здесь же, строкой рядом со значением, пишется, когда состояние возникает. Отдельной таблицы состояний быть не должно: состояние и его условие живут вместе.

Интерактивность

Раздел про виброотклик. Пишется тогда и только тогда, когда в мастере есть слой с барабаном в имени — 🥁 Виброотклик / Обязательный или 🥁 Виброотклик / Опциональный.

Сам слой в дерево «Анатомии» не попадает. Он нулевого размера, ничего не рисует и раскладку не меняет — это метка для разработчика, а не часть компонента. Проверка это ловит: барабан в дереве слоёв — ошибка.

Раздел идёт последним, после «Вариантов», и состоит из одной таблицы:

### Интерактивность

| Действие | Триггер | Haptic ID | Обязательность |
|---|---|---|---|
| Нажатие на кнопку | `onTapStart` | `hp_impact_light` | Опциональный |

Откуда берётся каждый столбец:

ДействиеЧто делает пользователь. Из блока «Интерактивность» Section, а если его нет — по месту слоя в мастере
ТриггерИмя события из таблицы триггеров в haptic. Нет данных — прочерк
Haptic IDЗначение свойства Haptic ID у слоя виброотклика. Не заполнено — так и писать: не задан
ОбязательностьПо имени слоя: Обязательный или Опциональный

Под таблицей — ссылка на фундамент: там вся карта откликов и словарь триггеров.

Словарь триггеров и полный список откликов — в [haptic](../../../../foundations/haptic.md).

Если слоёв виброотклика в мастере несколько и у них разные свойства — строк в таблице тоже несколько. Если у разных вариантов компонента отклик разный, в «Действии» указывается вариант.

Догадываться нельзя ни о чём: не заполнено в макете — в таблице прочерк и строка в audit.md.

История изменений

Первый раздел документа, сразу после шапки. Таблица из ds-log: версия, дата, изменение, автор. Журнал ведётся на семейство — в файлах детей ставится ссылка на ядро. Нет ds-log — раздела нет.

Порядок строк — снизу вверх: свежая запись последняя. Таблица читается как история: 1.0.0 первой строкой, текущая версия — последней. Так видно, откуда компонент пришёл к нынешнему виду, а не только чем закончил. Это обратно порядку в журналах репозиториев, и это намеренно: там ищут «что изменилось на днях», здесь — «как компонент дошёл до такого».

Строка источника

Стоит в шапке, до первого раздела — см. «Шапка» выше. Отдельного раздела внизу файла нет.

Чего в документе нет

Ссылок на Figma, кроме строки источника в шапкеДокумент обязан работать без макета
Node-id узлов, вариантов, блоковЖивут до следующей перестройки макета
Придуманных якорей A-01Есть имена слоёв — искать надо по ним
Значение рядом с токеномТокен и есть значение; дубль удваивает объём и разъезжается
Code syntax MCP (var(--…), ИмяТокенаЗаднегоФона-hover)Это CSS-представление, а не имя переменной
Вводной оговорки под шапкойОбщее знание о ДС живёт в скиле, а не в каждом файле
Пересказа правил фундаментаДостаточно ссылки; правило описано один раз в самом фундаменте
Скобок, слова Slot, значков ✱ и ⚒︎Это разметка канваса для дизайнера, роль слоя пишется словами
Жаргона инструмента (HUG, layoutMode)Читатель не обязан знать Figma
Внутреннего устройства вложенных компонентовУ них своя документация, копия разъедется
Открытых вопросов и находокИх место — audit.md рядом
Пустых разделов и фраз «данных нет»Отсутствие раздела — уже ответ
Props, событий, кода реализацииДокумент описывает макет, а не реализацию
Демо-контента из макетаЭто наполнение примеров, а не описание
Слоя виброотклика в дереве слоёвОн ничего не рисует; его данные идут в «Интерактивность»
Оси Format (Layout / Prototype)Режим работы в Figma, а не свойство интерфейса; документируется ветка Prototype