Composition
Полный справочник по типу Composition в SXL Studio: корневые поля, структура, теги, props, component properties, styles, transitions и theme binding — каждая секция начинается с таблицы полей.
Overview
$type: "composition" — это контракт SXL Studio, который описывает Figma-компонент в виде JSON. Это единый источник правды, превращающий замысел дизайна в повторяемые запуски Generate и Apply в Figma.
Вкратце, composition собирается из этих блоков:
| Блок | Ключ(и) | Назначение |
|---|---|---|
| Структура | structure | Дерево слоёв |
| Варианты | props / states | Оси вариантов и взаимоисключающие состояния |
| Стилизация | styles | Layout, оформление и переопределения по вариантам |
| Свойства | componentProperties | Нативные свойства Figma на инстансах |
| Поведение | transitions / themeBinding | Прототипирование и светлая/тёмная тема |
Ключевая идея Один JSON-файл описывает весь набор компонентов — его форму, варианты и стилизацию — поэтому один и тот же результат можно сгенерировать, обновить и передать инженерам или агентам без ручной пересборки.
Updates
История релизов типа Composition. Новый релиз — сверху; раскройте запись, чтобы увидеть изменения.
2.7.7Актуальный
ref.stylesприменяет нормализованные стили к корневому referenced-инстансу вINSTANCErefs и default-инстансахSLOTв reference mode.refStylesв style-блоке задаёт вариантные переопределения для этого referenced root, не стилизуя абстрактный host слота и preferred swaps.
2.7.1Предыдущий
- Компактные оси
statesдля взаимоисключающих состояний вродеloadingиdisabled— без раздувания декартова произведения. - Единая модель раскладки Composition Grid — общая для превью и сгенерированных наборов.
- Фигуры
VECTORдля кастомных силуэтов, включаяvectorCornerRadiiна токенах. - Вариантные переопределения
refдля wrapper-наборов с одним вложеннымINSTANCE. - Более понятное поведение
displayиvisibleдля условных веток. - Быстрее
Generate/Applyв больших файлах, особенно для наборов с множеством слотов и инстансов.
Где используется
Используйте Composition, когда нужна предсказуемая и повторяемая сборка компонентов в дизайн-системе.
Частые сценарии:
- генерация новых компонентов / наборов из token-файла;
- обновление существующих компонентов без пересборки (
Apply); - хранение структуры вариантов, содержимого слотов и правил стилизации в одном источнике;
- передача детерминированной модели компонента инженерам и агентам.
Как это работает в SXL Studio
- Вы пишете JSON вручную или бутстрапите его через Get Code.
- SXL Studio парсит и валидирует файл.
Generateсоздаёт новый компонент/набор,Applyобновляет отслеживаемые ноды.- Трекинг хранится в
diff-id.json, чтобы обновления были стабильны между запусками.
Трансформация в React / Vue 3
Composition JSON машиночитаем и достаточно стабилен для собственных скриптов-трансформеров. Плагин использует его для генерации в Figma, а вы можете читать structure + styles и мапить компонент в свой фреймворк. В Dev Mode SXL Studio также выдаёт готовый codegen (в том числе Vue 3), который можно взять за эталон.
Generate
Generate собирает новый компонент или набор из JSON: читает structure, разворачивает props в варианты, применяет styles и резолвит ссылки на токены ({path.to.token}) на этапе сборки. Используйте для компонента, которого ещё нет в Figma.
Предпроверка перед Generate / Apply
Сначала исправьте ошибки в JSON-редакторе. Типичные причины:
- нерезолвнутые ссылки на токены (
{path.to.token}); - невалидный синтаксис алиасов;
- нарушения схемы (например, некорректная форма
SLOT).
Рекомендуемый порядок: открыть JSON → устранить все ошибки валидации → сохранить → запустить Generate или Apply.
Get Code
Get Code читает выбранную ноду(ы) в Figma и генерирует черновик Composition JSON — быстрая стартовая точка, которую вы затем дорабатываете вручную. При множественном выборе в выводе может появиться $synthetic: true — признак синтетической сборки набора (только информационно).
Grid settings
Grid управляет тем, как варианты раскладываются на канвасе в сгенерированном наборе. Меняется только размещение — не семантика токенов и не контракт JSON.
- Columns / Rows явно назначают оси вариантов; несколько осей в одной зоне вкладываются снаружи внутрь.
- оси
statesне размещаются в Columns / Rows — они выводятся отдельной state-полосой снизу или справа. - отступы разделены по смыслу: variant gap, prop/group gap, padding набора, label gap и state-band gap.
- аннотации сетки рендерятся вне набора на тех же дорожках размещения, поэтому подписи совпадают с превью и выводом.
Рекомендация В больших файлах Figma тот же JSON остаётся стабильным, а SXL Studio уменьшает лишнюю перестилизацию для слот-тяжёлых компонентов. Держите значения class / name стабильными на слоях, которые адресуются из styles, и используйте states для взаимоисключающих состояний вместо разворачивания всех булевых комбинаций.
Apply
Apply обновляет существующий отслеживаемый компонент на месте, используя трекинг из diff-id.json, поэтому идентичность и инстансы сохраняются.
| Режим | Для чего | Результат |
|---|---|---|
Generate | Новый компонент / набор | Создаёт новый вывод |
Apply | Обновление отслеживаемого компонента | Обновляет существующие ноды с трекингом |
Автоматический режим выбирает операцию по контексту (выбранный якорь, трекинг, наличие локального компонента).
Разрешение цели и сохранение идентичности
Каждый выбранный корень composition обрабатывается со своим явным anchor. SXL Studio разрешает всё выделение до первой мутации canvas, поэтому цепочка sm → md и md → lg не позволяет первому переименованию забрать цель второго JSON-файла.
- Уникальная живая identity обновляет этот Component или ComponentSet на месте. ID ноды Figma остаётся стабильным, поэтому существующие инстансы сохраняют связь с компонентом.
- В режиме Automatic stale или cross-file identity без живого кандидата в текущем документе Figma создаёт новый результат.
- Явный Apply без подходящей цели просит выбрать нужный корень или использовать Generate. Неоднозначные, конфликтующие или не полностью просканированные кандидаты останавливаются до мутации.
- Apply сохраняет overrides пользовательских инстансов. Он не выполняет blanket reset текста, component properties, instance swaps и свойств, которых нет в composition.
- Generate и Apply из UI плагина не требуют Bridge. Remote Connect и Git Sync Local Storage по-прежнему используют Bridge для собственных transport/storage-контрактов.
SXL Studio управляет переносимой source identity в $extensions["sxl.studio"].composition. Старые JSON без этого блока остаются поддержанными; успешный Generate или anchored Apply пытается добавить его, не переписывая посторонние $extensions:
{
"$extensions": {
"sxl.studio": {
"composition": {
"id": "11111111-1111-4111-8111-111111111111",
"roots": [
{
"nodeId": "21649:1480",
"key": "figma-component-key",
"type": "COMPONENT_SET"
}
]
}
}
}
}
Это управляемые метаданные, а не авторский input. Если storage временно недоступен, canvas-операция остаётся успешной и сообщает warning; persistence identity повторяется в одном из следующих запусков.
Adopt и Audit для существующих компонентов
Компонент, созданный вручную до появления Composition JSON, удалять не нужно. Используйте контекстное меню бейджа composition:
| Действие | Что делает | Когда использовать |
|---|---|---|
Adopt composition | Связывает выбранный компонент/набор с JSON без пересборки слоёв. | Когда компонент в Figma уже есть и будущий Apply должен обновлять его. |
Audit composition | Сравнивает выбранный/связанный компонент с JSON и сообщает статус синхронизации. | Перед adopt для легаси-компонента, после ручных правок или перед repair. |
Adopt консервативен: сохраняет идентичность компонента (инстансы в других файлах не теряют связь), проверяет соответствие имён вариантов матрице из JSON и не переписывает слои, стили и свойства, пока audit/apply не подтвердит реальную структуру.
Root fields
Корневые ключи файла composition.
| Поле | Тип | Обязательно | Примечания |
|---|---|---|---|
$type | "composition" | ✅ | Маркер типа файла |
name | string | ✅ | Имя компонента / набора |
structure | нода | ✅ | Дерево слоёв |
styles | объект | ✅ | Базовые стили + правила селекторов |
props | объект | — | Оси вариантов |
states | string[] | — | Взаимоисключающие оси, взятые из props |
componentProperties | объект | — | Нативные свойства Figma |
transitions | объект | — | Прототип-переходы (transition в ед. ч. принимается как легаси-алиас) |
themeBinding | объект | — | Ось варианта → режимы коллекции переменных |
component | boolean | — | По умолчанию true; false строит plain-ноды и запрещает непустые props |
$description | string | — | Описание Figma на корневом компоненте/наборе |
$metadata | any | — | Непрозрачные метаданные для тулинга; на генерацию не влияют |
size / style | объект | — | Опциональные встроенные token-блоки (см. Advanced-поля) |
slotHostPipeline | объект | — | Продвинутые host/slot-системы (см. Advanced-поля) |
segmentIconPostPassRefMarkers | string[] | — | Продвинутые маркеры синхронизации иконок |
selectors | auto | auto | Генерируется парсером из ключей styles — вручную не писать |
Внимание Удалённые легаси-ключи дают ошибку парсинга: adapters, sizeStyles, colorStyles. А component: false несовместим с непустыми props.
Structure
structure — это дерево слоёв, которое строит генератор. У каждой ноды обязательны tag и class; всё остальное опционально.
Поля ноды
| Поле | Обязательно | Что делает | Аналог в Figma | Аналог во Frontend |
|---|---|---|---|---|
tag | ✅ | Тип создаваемой ноды | Тип слоя | Тип элемента |
class | ✅ | Ключ привязки стилей/цели | Селектор слоя | className |
name | — | Явное имя слоя в Figma | Имя слоя | data-name / подпись |
layer | — | Алиас name, если name не задан | Имя слоя | data-name |
content | — | Текстовое содержимое (в основном TEXT) | Значение текста | текст-нода / children |
description | — | Заметка для человека, без визуала | Описание слоя | комментарий в коде |
ref | — | Какой компонент инстанцировать + конфиг | Instance → main comp. | импортированный компонент |
slot | — | Конфиг нативного слота (reference mode) | Свойство Slot | <slot> / children |
children | — | Вложенные дочерние ноды | Вложенные слои | дочерние элементы |
vectorPaths | — | SVG-подобные path-данные (только VECTOR) | Vector paths | <path d="…"> |
vectorNetwork | — | Редактируемая геометрия вершин (VECTOR) | Vector network | — |
viewBox | — | Координатный бокс [x, y, w, h] (VECTOR) | — | SVG viewBox |
"structure": {
"tag": "FRAME",
"class": "root",
"name": "Root",
"children": []
}
JSON-пример на каждое поле:
{ "tag": "FRAME", "class": "card" }
{ "tag": "TEXT", "class": "label", "name": "Label", "content": "Continue" }
{ "tag": "INSTANCE", "class": "icon", "ref": { "component": "circle-info", "properties": { "size": "md" } } }
{ "tag": "SLOT", "class": "content-slot", "slot": { "default": "WButton", "preferred": ["WButton", "WChip"] } }
{ "tag": "FRAME", "class": "row", "description": "Строка шапки", "children": [{ "tag": "TEXT", "class": "title", "content": "Title" }] }
Примечание class — ключ стилей, name — имя слоя в Figma. Для вложенной адресации (ref.nested, NESTED_INSTANCE) всегда задавайте явный стабильный name.
Поддерживаемые значения tag
| Tag | Что создаёт | Аналог в Figma | Аналог во Frontend | Ограничения |
|---|---|---|---|---|
FRAME | Контейнер / auto-layout фрейм | Frame / Auto Layout | <div> (flex-контейнер) | — |
TEXT | Текстовый слой | Text | <span> / <p> | значение из content |
COMPONENT | Вложенная нода main-компонента | Component | определение компонента | — |
INSTANCE | Инстанс из ref | Instance | использование <Component /> | нужен ref; плейсхолдер, если не резолвится |
ICON | Иконка-инстанс, резолвится из ref | Icon instance | <Icon /> | как INSTANCE; color перекрашивает глиф |
SLOT | Нативный слот (или fallback) | Slot | <slot> / {children} | reference или children mode, не оба |
RECTANGLE | Прямоугольник | Rectangle | блочный <div> | заливка через fill |
ELLIPSE | Эллипс | Ellipse | border-radius: 50% div | — |
LINE | Линия | Line | <hr> / разделитель | — |
VECTOR | Кастомный вектор из path / vector network | Vector (Pen) | инлайновый <svg><path> | не импорт «сырого» SVG |
JSON-пример на каждый тег:
{ "tag": "FRAME", "class": "card", "children": [] }
{ "tag": "TEXT", "class": "label", "content": "Continue" }
{ "tag": "COMPONENT", "class": "chip" }
{ "tag": "INSTANCE", "class": "cta", "ref": { "component": "WButton", "properties": { "variant": "primary" } } }
{ "tag": "ICON", "class": "leading-icon", "ref": { "component": "circle-info" } }
{ "tag": "SLOT", "class": "content-slot", "slot": { "default": "WButton" } }
{ "tag": "RECTANGLE", "class": "bg" }
{ "tag": "ELLIPSE", "class": "avatar-mask" }
{ "tag": "LINE", "class": "divider" }
{
"tag": "VECTOR",
"class": "body-shape",
"vectorPaths": [{ "data": "M 0 140 L 564 140 L 540 0 L 24 0 Z", "windingRule": "NONZERO" }],
"viewBox": [0, 0, 564, 140]
}
Используйте VECTOR, когда фигура не является прямоугольником, эллипсом или линией — скошенное тело кнопки, волна, вырез вкладки, кастомный бейдж. Он принимает SVG-подобные vectorPaths или Figma-подобный vectorNetwork для редактируемых вершин и получает те же визуальные стили, что и другие фигуры. Скругление вершин можно токенизировать из styles через vectorCornerRadii.
Паттерн скошенной кнопки
Держите редактируемую геометрию фигуры в structure, а цвет/размер/скругление управляйте из styles. Середина остаётся обычным auto-layout FRAME, поэтому текст и иконки ведут себя как обычно.
{
"tag": "FRAME",
"class": "wrap",
"children": [
{ "tag": "VECTOR", "class": "left-shape", "vectorNetwork": { "vertices": [{ "x": 18, "y": 56 }, { "x": 0, "y": 56 }, { "x": 10, "y": 0 }, { "x": 18, "y": 0 }], "segments": [{ "start": 0, "end": 1 }, { "start": 1, "end": 2 }, { "start": 2, "end": 3 }, { "start": 3, "end": 0 }], "regions": [{ "windingRule": "NONZERO", "loops": [[0, 1, 2, 3]] }] } },
{ "tag": "FRAME", "class": "content", "children": [{ "tag": "TEXT", "class": "label", "content": "PRIMARY" }] }
]
}
".left-shape": { "widthType": "fixed", "heightType": "fixed", "width": 18, "height": 56, "fill": "{button.bg.accent}", "vectorCornerRadii": [0, "{radius.md}", "{radius.md}", 0] }
vectorCornerRadii принимает массив (в порядке вершин) или объект ({ "1": "{radius.md}" }). Индексы вершин с нуля. VECTOR — не импорт «сырого» SVG: кривые, скос и вырезы кладите в vectorPaths / vectorNetwork.
Режимы и правила SLOT
У SLOT строгие режимы: reference mode (конфиг slot с опциональным шаблоном ref) или children mode (инлайновые children). Никогда оба сразу.
{
"tag": "SLOT",
"class": "content-slot",
"slot": { "default": "WButton", "preferred": ["WButton", "WChip"] },
"ref": { "component": "WButton", "properties": { "size": "md" } }
}
{
"tag": "SLOT",
"class": "rows",
"children": [{ "tag": "FRAME", "class": "row", "children": [] }]
}
Ограничения: у SLOT не может быть одновременно slot и непустых children; SLOT.ref валиден только в reference mode. SLOT ровно с одним дочерним INSTANCE (с ref, без slot) трактуется как сокращение reference mode.
Примечание Канонический и переносимый между схемами формат для slot.preferred и componentProperties.*.preferred — массив имён компонентов. Apply также принимает записи из Figma API, например { "type": "COMPONENT_SET", "key": "..." }; дополнительная отображаемая метка вроде component игнорируется. Используйте имена компонентов, если файл должен проходить внешнюю схему, разрешающую только строки.
ref для INSTANCE / SLOT
| Поле | Что делает | Пример |
|---|---|---|
component | Имя целевого компонента (обязательно при наличии ref) | "component": "WButton" |
library | Подсказка библиотеки | "library": "SXL DS" |
key | Подсказка publish-key | "key": "abc123" |
properties | Примитивные значения свойств инстанса | "properties": { "state": "active" } |
styles | Стили корня referenced-инстанса | "styles": { "width": "{size.icon}" } |
iconBindProperty | Явное имя свойства для icon swap | "iconBindProperty": "icon" |
icon | Дескриптор вложенного icon swap | "icon": { "component": "fire-3" } |
overrides | Переопределения содержимого детей | "overrides": { "label": "Apply" } |
slots | Переопределения слотов у referenced-инстанса | см. ниже |
nested | Обновления вложенных инстансов по имени слоя | см. ниже |
description | Опциональная заметка | "description": "Primary CTA" |
ref.styles относится к материализованному корню referenced INSTANCE. Используйте его, когда самому default-инстансу нужны layout- или paint-bindings, например binding размера иконки внутри SLOT. Поддерживаются те же aliases и token refs, что и в обычных styles: widthType, heightType, width, height, fill, color.
Style-блок host по-прежнему стилизует сам composition node. В reference-mode слотах используйте refStyles внутри обычного style-блока, если варианты должны переопределять referenced root. refStyles мержится поверх ref.styles и применяется только пока текущий slot child совпадает с ref.component; он не каскадится на preferred/user swap вроде WBadge.
{
"tag": "SLOT",
"class": "leading-slot",
"slot": { "default": "circle-info", "preferred": ["circle-info", "WBadge"] },
"ref": {
"component": "circle-info",
"properties": { "style": "filled" },
"styles": {
"widthType": "fixed",
"heightType": "fixed",
"width": "{sz.fixed.reg.xs}",
"height": "{sz.fixed.reg.xs}"
}
}
}
"styles": {
"$placement=inner .leading-slot": {
"refStyles": {
"width": "{sz.fixed.reg.3xs}",
"height": "{sz.fixed.reg.3xs}"
}
}
}
ref.nested — адресует вложенные инстансы по имени слоя:
"nested": {
"Badge": {
"properties": { "label": "3" },
"icon": { "component": "fire-3", "properties": { "style": "filled" } }
}
}
ref.slots — переопределяет слоты внутри referenced-инстанса (op: replace, append, patch):
"slots": {
"footer": {
"op": "replace",
"nodes": [{ "component": "WButton", "name": "apply", "properties": { "variant": "primary" } }]
}
}
Вариант набора может оборачивать другой существующий компонент, переопределяя ref из вариантного селектора, — тогда одна нода INSTANCE обслуживает весь wrapper-набор:
"styles": {
"$item=neutral-secondary-sm .item": {
"ref": { "component": "WButton", "properties": { "style": "neutral", "variant": "secondary", "size": "sm" } }
}
}
Props
| Ключ | Тип | Что делает |
|---|---|---|
props | Record<string, (string | boolean | number)[]> | Объявляет каждую ось вариантов и её допустимые значения |
states | string[] | Имена осей из props, которые являются взаимоисключающими состояниями |
props объявляет все допустимые значения для каждой оси; каждая комбинация становится одним сгенерированным вариантом.
"props": {
"size": ["sm", "md", "lg"],
"state": ["default", "hover", "active"],
"compact": [true, false]
}
Поведение: при сопоставлении селекторов значения сравниваются как строки; ось с default использует его как fallback для отсутствующих правил, иначе fallback — первое значение. Держите число осей осознанным — комбинации растут мультипликативно.
states (взаимоисключающие оси)
states перечисляет оси из props, которые ведут себя как взаимоисключающие UI-состояния (:disabled, :loading), а не как комбинируемые варианты. Они исключаются из декартова произведения — каждое не-дефолтное значение добавляет один вариант поверх дефолтов.
"props": {
"_state": ["default", "hover", "focus", "select"],
"loading": ["false", "true"],
"disabled": ["false", "true"]
},
"states": ["loading", "disabled"]
Без states это матрица 4 × 2 × 2 = 16 вариантов; со states она схлопывается до 6 (4 интерактивных + loading=true + disabled=true). Правила: off-значение — первое значение оси; каждое имя в states должно быть в props; Apply декларативно пере-схлопывает существующий набор. Обычные визуальные выборы (size, variant, tone, theme) держите только в props.
Маппинг в codegen:
- React / Vue: ось, значения которой ровно
false/true, становитсяboolean-пропом (напр.loading?: boolean); - DivKit: наборы эмитятся через
card.states/state_id;statesлишь управляет тем, какие варианты материализуются, и не создаёт фиктивныйcustom_type; - ось с префиксом
_(напр._state) — внутренняя ось interaction-состояния, исключена из публичного codegen-API (её ведёт собственный:hover/:focusCSS потребителя); не-_оси вродеloading/disabledэмитятся как реальные пропсы.
Расширенная объектная форма с combineWith (скрещивание состояния со структурными осями) зарезервирована, но пока не реализована — её использование вызывает ошибку валидации.
Component Properties
componentProperties задаёт нативные свойства Figma, доступные на инстансах.
| Тип | На что указывает layer | defaultValue |
|---|---|---|
TEXT | class в structure | Обязателен; авто-выводится из content, если опущен |
BOOLEAN | class в structure | Обязателен; по умолчанию true, если опущен |
INSTANCE_SWAP | class в structure | Обязателен |
SLOT | class в structure | Обязателен |
NESTED_INSTANCE | Имя слоя вложенного инстанса (не class) | Не обязателен |
Пример на каждый тип:
"title": { "type": "TEXT", "layer": "title", "defaultValue": "Promo title" }
"showMeta": { "type": "BOOLEAN", "layer": "meta", "defaultValue": true }
"icon": { "type": "INSTANCE_SWAP", "layer": "icon", "defaultValue": "circle-info", "preferred": ["circle-info", "star"] }
"media": { "type": "SLOT", "layer": "media-slot", "defaultValue": "WImageTile" }
"badge": { "type": "NESTED_INSTANCE", "layer": "Badge" }
Правило layer: для TEXT, BOOLEAN, INSTANCE_SWAP, SLOT используйте class из structure; для NESTED_INSTANCE — реальное имя вложенного слоя в Figma.
Styles
styles управляет оформлением слоя, layout и переопределениями по вариантам. Ключ — это CSS-подобный класс (.card), легаси-класс (card) или вариантный селектор ($state=hover .card); блоки можно вкладывать. Каждое свойство ниже показано с точным написанием в JSON, сгруппировано по назначению:
| Группа | Что покрывает |
|---|---|
| Контейнер & auto-layout | direction, justifyContent, alignItems, gap, padding, … |
| Размер слоя | widthType, width, min/maxWidth, aspectRatio, … |
| Позиционирование | position, top/right/bottom/left, x/y, alignSelf, … |
| Grid | rows/columns, rowGap/columnGap, выравнивание/спан grid-ребёнка |
| Фон/заливка/цвет | background, fill, color |
| Границы & углы | border, borderRadius, strokeAlign, cornerSmooth, … |
| Тени & эффекты | boxShadow, backgroundBlur, layerBlur, opacity, glass |
| Типографика | fontFamily, fontSize, lineHeight, textCase, … |
| Видимость & сброс | visible, display, очистка через none |
| Управление инстансом | component, instanceProperties, nestedInstanceProperties, ref |
| Переменные & метаданные | explicitVariableModes, layoutGrids, exportSettings, mask |
"styles": {
".root": { "direction": "row", "gap": 8, "padding": "12 16" },
"$state=hover .root": { "background": "{color.brand.hover}" }
}
Селекторы и каскад
- сначала применяются базовые стили; затем совпавшие селекторы по специфичности (больше условий — выше приоритет);
- одинаковая специфичность → побеждает более позднее объявление в JSON;
- descendant-селекторы резолвятся через dot-пути (
.footer .item→footer.item); - когда класс повторяется в разных ветках, предпочитайте полный путь (
.header .item) вместо голого.item.
"styles": {
".wrap": {
"padding": 8,
".item": { "widthType": "fill" },
"$state=active": { ".item": { "opacity": 1 } }
}
}
Контейнер и auto-layout
| Свойство | Что делает — значения | Пример |
|---|---|---|
direction | Ось раскладки — row, column, grid, none | "direction": "row" |
justifyContent | Распределение по главной оси — start, center, end, space-between | "justifyContent": "space-between" |
alignItems | Выравнивание по поперечной оси — start, center, end, baseline | "alignItems": "center" |
alignContent | Распределение перенесённых рядов — auto, space-between | "alignContent": "space-between" |
flexWrap | Перенос — nowrap, wrap | "flexWrap": "wrap" |
gap | Отступ между детьми (px) | "gap": 12 |
wrapGap | Поперечный отступ между перенесёнными рядами (px) | "wrapGap": 8 |
padding | Внутренние отступы — число, "T R B L" или "none" | "padding": "16 16 20 16" |
overflow | Обрезка — hidden, clip, visible, auto, scroll | "overflow": "hidden" |
primaryAxisSizingMode | Размер контейнера по главной оси — auto, fixed | "primaryAxisSizingMode": "auto" |
counterAxisSizingMode | Размер контейнера по поперечной оси — auto, fixed | "counterAxisSizingMode": "fixed" |
boxSizing | Обводка учитывается в layout — border-box, content-box | "boxSizing": "border-box" |
canvasStacking | Z-порядок сиблингов — first-on-top, last-on-top | "canvasStacking": "first-on-top" |
".header": { "direction": "row", "justifyContent": "space-between", "alignItems": "center", "gap": 8, "padding": "12 16" }
Размер слоя
| Свойство | Что делает — значения | Пример |
|---|---|---|
widthType / heightType | Режим размера — fixed, hug, fill | "widthType": "fill" |
width / height | Фиксированный размер (px) | "width": 360 |
minWidth / maxWidth | Границы ширины (px) | "maxWidth": 480 |
minHeight / maxHeight | Границы высоты (px) | "minHeight": 40 |
aspectRatio | Фиксация пропорции — "1/1", или "none" / false для сброса | "aspectRatio": "1/1" |
constrainProportions | Блокировка пропорций ширина/высота — true, false | "constrainProportions": true |
targetAspectRatio — принимаемый алиас aspectRatio.
Позиционирование
| Свойство | Что делает — значения | Пример |
|---|---|---|
position | Поток — relative, absolute, none | "position": "absolute" |
top / right / bottom / left | Absolute-инсеты (px) | "top": 8 |
x / y | Абсолютные координаты на канвасе (px) | "x": 24 |
alignSelf | Переопределение поперечного выравнивания — inherit, stretch, start, center, end | "alignSelf": "stretch" |
flexGrow | Коэффициент роста | "flexGrow": 1 |
rotate | Поворот (град) | "rotate": 45 |
constraintH / constraintV | Констрейнты Figma — left, center, right, scale, stretch | "constraintH": "center" |
".badge": { "position": "absolute", "top": 8, "right": 8 }
Grid-раскладка
Задаётся, когда direction равен grid.
| Свойство | Что делает | Пример |
|---|---|---|
rows / columns | Число дорожек | "columns": 3 |
rowGap / columnGap | Отступы дорожек (px) | "columnGap": 8 |
gridTemplateRows / gridTemplateColumns | Размеры дорожек | "gridTemplateColumns": "1fr 1fr" |
justifySelf / gridAlignSelf | Выравнивание grid-ребёнка (H / V) — start, center, end, auto | "justifySelf": "center" |
gridRowSpan / gridColumnSpan | Растяжение grid-ребёнка (дорожки) | "gridColumnSpan": 2 |
Фон, заливка, цвет
| Свойство | Что делает — значения | Пример |
|---|---|---|
background | Заливка контейнера — hex, градиент, массив слоёв или "none" / "transparent" | "background": "#FFFFFF" |
fill | Заливка фигуры / вектора / иконки — hex или токен, "none" очищает | "fill": "{button.primary}" |
color | CSS-подобный currentColor (текст + краска иконки) — hex или токен, "none" | "color": "{text.primary}" |
color — это CSS-подобный currentColor: на TEXT задаёт заливку текста; на ICON / иконках-инстансах перекрашивает глиф (fill-иконки получают заливку, stroke-иконки — обводку). Ссылки на токены сохраняют привязку к переменной; литеральные цвета её заменяют. fill и strokes — низкоуровневые: используйте их для заливок фигур или прямой обводки. Для stroke-only иконки "fill": "none" оставляет только краску обводки.
Градиентный фон:
".hero": { "background": "linear-gradient(135deg, #7B5CFA 0%, #FA5CB4 100%)" }
Многослойный фон (изображение + оверлей):
".media-slot": {
"background": [
{ "type": "image", "url": "https://images.unsplash.com/photo-1498050108023-c5249f4df085", "scaleMode": "FILL", "opacity": 0.72, "blendMode": "NORMAL" },
"rgba(0,0,0,0.24)"
]
}
Границы, обводка, скругления
| Свойство | Что делает — значения | Пример |
|---|---|---|
border | Shorthand "<толщина> <стиль> <цвет>" | "border": "1px solid #E7EAF1" |
borderColor | Цвет обводки | "borderColor": "#E7EAF1" |
borderWidth | Толщина обводки (px) | "borderWidth": 1 |
borderStyle | Стиль обводки — solid, dashed | "borderStyle": "dashed" |
borderRadius | Радиус скругления (px) | "borderRadius": 20 |
cornerSmooth | Сглаживание углов 0–1 (squircle) | "cornerSmooth": 0.6 |
strokeAlign | Положение обводки — inside, center, outside | "strokeAlign": "inside" |
strokeCap / strokeJoin | Стиль конца / соединения линии | "strokeCap": "ROUND" |
strokeTopWeight … strokeLeftWeight | Толщина обводки по стороне (px) | "strokeTopWeight": 2 |
dashPattern | Пунктир — массив длин штрих/пропуск | "dashPattern": [4, 4] |
outline / outlineOffset | Внешняя обводка + отступ | "outline": "2px solid #0D6EFD" |
vectorCornerRadii | Радиусы по вершинам VECTOR (в порядке вершин) | "vectorCornerRadii": [0, 8, 8, 0] |
".card": { "border": "1px solid #E7EAF1", "borderRadius": 20 },
"$state=active .card": { "outline": "2px solid #0D6EFD", "outlineOffset": "2px" }
Тени и эффекты
| Свойство | Что делает — значения | Пример |
|---|---|---|
boxShadow | Тени — массив объектов теней, или "none" | см. блок |
backgroundBlur | Радиус размытия фона (px), или "none" | "backgroundBlur": 12 |
layerBlur | Радиус размытия слоя (px), или "none" | "layerBlur": 8 |
opacity | Прозрачность слоя 0–1, или "none" (= 1) | "opacity": 0.56 |
glass | Эффект стекла, или "none" для сброса | "glass": "none" |
effects | Полный композит эффектов (алиас токена или слои) | "effects": "{shadow.md}" |
".card": { "boxShadow": [{ "x": 0, "y": 8, "blur": 24, "spread": 0, "color": "rgba(16,24,40,0.14)" }] },
"$state=hover .card": { "boxShadow": [{ "x": 0, "y": 14, "blur": 36, "spread": 0, "color": "rgba(16,24,40,0.20)" }] }
Типографика
Задаётся на ноде TEXT.
| Свойство | Что делает — значения | Пример |
|---|---|---|
fontFamily | Семейство шрифта | "fontFamily": "Inter" |
fontWeight | Насыщенность | "fontWeight": 600 |
fontSize | Размер (px) | "fontSize": 18 |
lineHeight | Межстрочный интервал | "lineHeight": "24px" |
letterSpacing | Трекинг | "letterSpacing": "0px" |
textAlign | По горизонтали — left, center, right, justify | "textAlign": "left" |
verticalAlign | По вертикали — top, center, middle, bottom | "verticalAlign": "center" |
textCase | Регистр — none, uppercase, lowercase, capitalize, small-caps | "textCase": "uppercase" |
textDecoration | Оформление — none, underline, line-through | "textDecoration": "underline" |
textSizing | Авто-размер — fixed, height-auto, auto, truncate | "textSizing": "height-auto" |
textTruncation | Обрезка — disabled, ending (многоточие) | "textTruncation": "ending" |
maxLines | Максимум строк до обрезки | "maxLines": 2 |
paragraphSpacing | Отступ между абзацами (px) | "paragraphSpacing": 8 |
paragraphIndent | Отступ первой строки (px) | "paragraphIndent": 16 |
leadingTrim / verticalTrim | Обрезка leading строки — CAP_HEIGHT, NONE | "leadingTrim": "CAP_HEIGHT" |
".title": { "fontFamily": "Inter", "fontWeight": 600, "fontSize": 18, "lineHeight": "24px", "textAlign": "left", "textSizing": "height-auto" }
Композитный ключ typography / font применяет полный текстовый стиль сразу.
Видимость и сброс
visible: false— слой остаётся в варианте, но скрыт в Figma;display: "none"— ветка исключается из собранного варианта;- если заданы оба, побеждает
display: "none". Когда более поздний селектор снова показывает ветку, укажите нужные ей размеры/layout.
"$loading=true": {
".button": {
".label": { "visible": false },
".spinner": { "display": "flex", "visible": true, "width": "{button.size.icon}" }
}
}
CSS-подобные очищающие значения (пишутся, чтобы сбросить свойство):
| Написание сброса | Эффект |
|---|---|
"background": "none" / "transparent" | Очищает заливки |
"color": "none" / "fill": "none" | Очищает текст/currentColor/краску |
"boxShadow": "none" / "effects": "none" | Очищает эффекты |
"padding": "none" | Все паддинги 0 |
"opacity": "none" | Резолвится в 1 |
"mask": "none" / false | Снимает маску |
"aspectRatio": "none" / false | Разблокирует пропорцию |
Управление инстансом
Задаётся на ноде INSTANCE.
| Свойство | Что делает | Пример |
|---|---|---|
component | Смена main-компонента (swap по имени) | "component": "WButton" |
instanceProperties / properties | Значения свойств инстанса | см. блок |
nestedInstanceProperties | Свойства вложенных инстансов по имени слоя | см. блок |
ref | Полное переопределение цели инстанса | см. раздел ref |
".action-main": { "component": "WButton", "instanceProperties": { "variant": "primary", "size": "md", "label": "Continue" } }
".badge": { "nestedInstanceProperties": { "Badge": { "label": "2" } } }
Режимы переменных и Figma-метаданные
| Свойство | Что делает | Пример |
|---|---|---|
explicitVariableModes | Принудительный режим переменной по коллекции | "explicitVariableModes": { "Themes": "Dark" } |
variableModes | Алиас explicitVariableModes | "variableModes": { "Themes": "Dark" } |
layoutGrids | Массив layout-сеток Figma | см. блок |
exportSettings | Массив настроек экспорта Figma | см. блок |
mask / maskType | Маска слоя — alpha, vector, luminance, none | "mask": "alpha" |
".root": {
"explicitVariableModes": { "Themes": "Dark" },
"layoutGrids": [{ "pattern": "GRID", "sectionSize": 8, "color": { "r": 0, "g": 0, "b": 1, "a": 0.12 } }],
"exportSettings": [{ "format": "PNG", "suffix": "@2x", "constraint": { "type": "SCALE", "value": 2 } }]
}
Сбросить явный режим для коллекции можно через null, false, "none", "auto" или "unset".
Transitions
Прототип-переходы задаются на корне composition (shorthand или объектная форма), но никогда как ключ стиля слоя.
| Поле | Что делает | Значения |
|---|---|---|
trigger | Когда срабатывает | on-hover, on-click, on-press, on-drag, on-enter, on-leave, mouse-up, mouse-down, after-timeout: 500 |
animation | Тип перехода | smart-animate, dissolve, instant, scroll-animate, slide-in, slide-out, push, move-in, move-out |
duration | Длительность | напр. 200ms |
easing | Кривая | linear, ease-in, ease-out, ease-in-out, ease-in-back, ease-out-back, ease-in-out-back, gentle, quick, bouncy, slow, cubic-bezier(...), spring(...) |
direction | Опциональное направление | left, right, top, bottom |
condition | Опциональное условие по переменной | { "variable": …, "op": "==", "value": … } |
Shorthand — порядок: trigger animation duration easing [direction]:
"transitions": { "hover-in": "on-hover smart-animate 200ms ease-out" }
Объектная форма с условием:
"transitions": {
"hover-in": {
"trigger": "on-hover",
"animation": "smart-animate",
"duration": "200ms",
"easing": "ease-out",
"direction": "right",
"condition": { "variable": "motion.enabled", "op": "==", "value": true }
}
}
Theme binding
themeBinding связывает ось варианта (обычно theme) с режимами коллекции переменных для корректного рендера светлой/тёмной темы.
| Поле | Что делает | Пример |
|---|---|---|
target | Метка цели привязки | "target": "root" |
prop | Имя оси из props | "prop": "theme" |
modes | Карта значение оси → конфиг режима | { "light": { "type": "local", "collection": "Themes", "mode": "Light" } } |
applyTo | Какие домены переключать | ["variables", "textStyles", "effectStyles"] |
"themeBinding": {
"target": "root",
"prop": "theme",
"modes": {
"light": { "type": "local", "collection": "Themes", "mode": "Light" },
"dark": { "type": "local", "collection": "Themes", "mode": "Dark" }
},
"applyTo": ["variables", "typography", "textStyles", "effectStyles", "paintStyles"]
}
prop должен существовать в props. У источника режима есть type (local / library), collection, mode и опциональный library. Каждый modes.<value> — это либо плоский источник, либо обёртка { primary, fallback[] }.
Advanced-поля
Встроенные size / style
Файл composition может встраивать DTCG-блоки токенов size и style для самодостаточных пакетов компонентов (неймспейс по имени компонента). Они опциональны и сами по себе на layout не влияют.
"size": { "md": { "height": "40px", "paddingX": "16px" } },
"style": { "primary": { "bg": "{color.brand.primary}" } }
Slot-host pipeline
Для host/item-систем (segmented controls, вкладки, группы элементов) slotHostPipeline связывает host-набор с его item-набором. slotItemComponentSetName обязателен. Если любой composition-файл определяет slotHostPipeline, легаси-дефолты WSegmentControl не применяются — объявляйте каждый host/item-набор, на который опираетесь.
"slotHostPipeline": {
"hostComponentSetName": "WSegmentControl",
"hostCompositionTokenName": "WSegmentControl",
"slotName": "item-group",
"slotItemComponentSetName": "WSegmentControlItem"
},
"segmentIconPostPassRefMarkers": ["WSegmentControl", "WSegmentControlItem"]
Оба ключа также принимаются под обёрткой $figma ($figma.slotHostPipeline, $figma.segmentIconPostPassRefMarkers).
Полный пример
Полный набор компонентов, затрагивающий большинство возможностей: многоосевые props, componentProperties, SLOT и INSTANCE, переопределения по селекторам, корневые transitions и themeBinding.
{
"$type": "composition",
"name": "WPromoCard",
"$description": "Промо-карточка с медиа-слотом, действием и состояниями.",
"props": {
"theme": ["light", "dark"],
"size": ["sm", "md"],
"state": ["default", "hover", "disabled"]
},
"states": ["disabled"],
"componentProperties": {
"title": { "type": "TEXT", "layer": "title", "defaultValue": "Promo title" },
"showMeta": { "type": "BOOLEAN", "layer": "meta", "defaultValue": true },
"media": { "type": "SLOT", "layer": "media-slot", "defaultValue": "WImageTile" }
},
"structure": {
"tag": "FRAME",
"class": "card",
"name": "Card",
"children": [
{ "tag": "SLOT", "class": "media-slot", "slot": { "default": "WImageTile" } },
{ "tag": "TEXT", "class": "title", "content": "Promo title" },
{ "tag": "TEXT", "class": "meta", "content": "Meta" },
{ "tag": "INSTANCE", "class": "cta", "ref": { "component": "WButton", "properties": { "variant": "primary" } } }
]
},
"styles": {
".card": {
"direction": "column",
"gap": 12,
"padding": 16,
"background": "{card.bg}",
"borderRadius": 16,
"widthType": "fixed",
"width": 320
},
"$size=sm .card": { "width": 260 },
"$state=hover .card": { "boxShadow": [{ "x": 0, "y": 12, "blur": 32, "spread": 0, "color": "rgba(0,0,0,0.18)" }] },
"$state=disabled .card": { "opacity": 0.56 },
".title": { "fontFamily": "Inter", "fontWeight": 600, "fontSize": 16 }
},
"transitions": { "hover-in": "on-hover smart-animate 160ms ease-out" },
"themeBinding": {
"target": "root",
"prop": "theme",
"modes": {
"light": { "type": "local", "collection": "Themes", "mode": "Light" },
"dark": { "type": "local", "collection": "Themes", "mode": "Dark" }
},
"applyTo": ["variables", "textStyles", "effectStyles"]
}
}
Чеклист адаптации: замените имена компонентов (WImageTile, WButton) на имена из своей библиотеки; создайте коллекцию Themes или уберите themeBinding; начните с Generate, затем итерируйте Apply; держите name стабильным на нодах, адресуемых вложенно.
Для агентов и трансформеров
A) Figma → Composition JSON
- Задайте
$type: "composition"и стабильныйname. - Стройте
propsтолько из реальных осей вариантов; добавляйтеstatesдля взаимоисключающих условий. - Для каждой ноды пишите валидные
tagиclass; добавляйте стабильныйnameдля вложенно-адресуемых нод. - Для инстансов пишите
ref.componentи только валидные примитивныеref.properties. - Для слотов используйте reference mode (
slot) или children mode (children), никогда оба. - Все визуальные/layout-правила кладите в
styles(база + переопределения по селекторам). - Используйте корневые
transitionsиthemeBinding, а не хаки на уровне слоя. - Проверьте, что referenced-компоненты/токены существуют.
B) Composition JSON → React / Vue
- Читайте
structureкак дерево элементов; используйтеclass/path-ключи для резолва стилей. - Резолвьте вариантные селекторы (
$prop=value .class) против входящих пропсов. - Уважайте видимость веток (
display: "none"). - Трактуйте
ref.component→ импорт компонента,ref.properties→ передаваемые пропсы. - Трактуйте
componentProperties,themeBinding,slotHostPipelineи$figmaкак метаданные дизайн-тайма, если рантайм их не поддерживает. - Сохраняйте ссылки на токены (
{...}) или пре-резолвьте их через свой token-движок.
C) Pre-flight валидация
structureсуществует и у каждой ноды естьtag+class;stylesсуществует и содержит только объектные значения;themeBinding.propсуществует вprops;- нет удалённых легаси-ключей (
adapters,sizeStyles,colorStyles); - нет конфликта режимов SLOT (
slotс непустымиchildren).
Авторский ключ → runtime-ключ Figma (справка)
| Авторский ключ | Runtime-ключ |
|---|---|
direction | layoutMode |
justifyContent | primaryAxisAlignItems |
alignItems | counterAxisAlignItems |
alignContent | counterAxisAlignContent |
flexWrap | layoutWrap |
gap / wrapGap | itemSpacing / counterAxisSpacing |
widthType / heightType | layoutSizingHorizontal / Vertical |
alignSelf / flexGrow | layoutAlign / layoutGrow |
position | layoutPositioning |
top / right / bottom / left | insets → layout/position |
rows / columns | gridRowCount / gridColumnCount |
rowGap / columnGap | gridRowGap / gridColumnGap |
borderWidth / borderAlign | strokeWeight / strokeAlign |
borderRadius / cornerSmooth | cornerRadius / cornerSmoothing |
textSizing | textAutoResize |
textAlign / verticalAlign | textAlignHorizontal / Vertical |
rotate | rotation |
boxSizing / canvasStacking | strokesIncludedInLayout / itemReverseZIndex |
justifySelf / gridAlignSelf | gridChildHorizontalAlign / VerticalAlign |
Авторское значение → enum Figma (справка)
| Свойство | Автор → enum |
|---|---|
direction | row/column/none/grid → HORIZONTAL/VERTICAL/NONE/GRID |
justifyContent | start/center/end/space-between → MIN/CENTER/MAX/SPACE_BETWEEN |
alignItems | start/center/end/baseline → MIN/CENTER/MAX/BASELINE |
widthType / heightType | fixed/hug/fill (алиасы: auto→HUG, stretch→FILL) → FIXED/HUG/FILL |
position | relative/absolute/none → AUTO/ABSOLUTE/AUTO |
textSizing | fixed/height-auto/auto/truncate → NONE/HEIGHT/WIDTH_AND_HEIGHT/TRUNCATE |
textCase | none/uppercase/lowercase/capitalize/small-caps → ORIGINAL/UPPER/LOWER/TITLE/SMALL_CAPS |
strokeAlign | inside/center/outside → INSIDE/CENTER/OUTSIDE |
mask | alpha/vector/luminance/none → ALPHA/VECTOR/LUMINANCE или isMask:false |
Примечания: ссылки на токены ({...}) сохраняются и резолвятся во время apply; числовые строки и строки с единицами (px, rem, deg) нормализуются в числа там, где нужно.
Ограничения и предупреждения
- Неподдерживаемые сочетания свойств/нод игнорируются.
- Внутренности сторонних/библиотечных инстансов могут быть частично защищены Figma.
NESTED_INSTANCEиref.nestedзависят от стабильных имён слоёв.- Большие матрицы
propsдают тяжёлое число вариантов — где уместно, применяйтеstates. - Generate/Apply в Figma тихо пропускает неизвестные ключи стилей, не блокируя документированные свойства и алиасы. Оставляйте unknown keys только если их использует другой output, например code generation.
- Удалённые легаси-ключи дают ошибку парсинга:
adapters,sizeStyles,colorStyles.component: falseнесовместим с непустымиprops.