Tokens

Composition

Complete Composition reference for SXL Studio: root fields, structure, tags, props, component properties, styles, transitions, and theme binding — every section opens with a field table.

Overview

$type: "composition" is the SXL Studio contract that describes a Figma component as JSON. It is the single source of truth that turns design intent into repeatable Generate and Apply runs in Figma.

At a glance, a composition is built from these blocks:

BlockKey(s)Purpose
StructurestructureThe layer tree
Variantsprops / statesVariant axes and mutually-exclusive states
StylingstylesLayout, appearance, and per-variant overrides
PropertiescomponentPropertiesNative Figma properties on instances
Behaviortransitions / themeBindingPrototyping and light/dark theming

Key idea One JSON file describes the whole component set — its shape, its variants, and its styling — so the same result can be generated, updated, and handed off to engineers or agents without manual rebuilding.


Updates

Release history for the Composition type. The newest release is on top; open an entry to see what changed.

2.7.7Latest
  • ref.styles applies normalized styles to the referenced instance root in INSTANCE refs and reference-mode SLOT defaults.
  • refStyles in a style block provides variant-specific overrides for that referenced root without styling the abstract slot host or preferred swaps.
2.7.1Previous
  • Compact states axes for mutually-exclusive states such as loading and disabled — no more full cartesian blow-up.
  • Unified Composition Grid placement model shared by preview and generated component sets.
  • VECTOR shapes for custom silhouettes, including token-driven vectorCornerRadii.
  • Variant-specific ref overrides for wrapper sets that contain one nested INSTANCE.
  • Clearer display vs visible behavior for conditional branches.
  • Faster Generate / Apply in large files, especially for slot- and instance-heavy sets.

Where It's Used

Use Composition whenever you need predictable, repeatable component building in a design system.

Common use cases:

  • generate new components / component sets from a token file;
  • update existing components without rebuilding them (Apply);
  • keep variant structure, slot content, and styling rules in one source;
  • hand a deterministic component model to engineers and agents.

How SXL Studio works with it

  1. You author JSON manually or bootstrap it from Get Code.
  2. SXL Studio parses and validates the file.
  3. Generate creates a new component/set, or Apply updates tracked nodes.
  4. Tracking is stored in diff-id.json so updates stay stable between runs.

React / Vue 3 transformation

Composition JSON is machine-readable and stable enough for custom transformation scripts. The plugin uses it for Figma generation, and you can read structure + styles to map a component to your framework. In Dev Mode, SXL Studio also produces dedicated codegen output (including Vue 3) you can use as a reference.


Generate

Generate builds a new component or component set from the JSON: it reads structure, expands props into variants, applies styles, and resolves token references ({path.to.token}) at build time. Use it for a component that does not exist in Figma yet.

Pre-check before Generate / Apply

Fix JSON editor errors before running. Typical causes:

  • unresolved token references ({path.to.token});
  • invalid alias syntax;
  • schema violations (for example an invalid SLOT shape).

Recommended flow: open JSON → resolve all validation issues → save → run Generate or Apply.

Get Code

Get Code reads the selected Figma node(s) and generates a Composition JSON draft — a fast starting point you then refine by hand. With multi-select, output may contain $synthetic: true to indicate a synthetic set construction (informational only).

Grid settings

Grid controls how variants are laid out on the canvas in a generated component set. It changes placement only — never token semantics or the JSON contract.

  • Columns / Rows assign variant axes explicitly; multiple axes in one zone nest from outer to inner.
  • states axes are not placed in Columns / Rows — they render as a trailing state band below or to the right.
  • spacing is split by meaning: variant gap, prop/group gap, component-set padding, label gap, and state-band gap.
  • grid annotations render outside the set on the same placement tracks, so labels match preview and output.

Tip In large Figma files the same JSON stays stable while SXL Studio reduces redundant restyling for slot-heavy components. Keep class / name values stable on layers targeted by styles, and use states for mutually-exclusive states instead of expanding every boolean combination.


Apply

Apply updates an existing tracked component in place, using diff-id.json tracking so identity and instances are preserved.

ModeBest forResult
GenerateNew component / component setCreates new output
ApplyExisting tracked component updatesUpdates existing nodes with tracking

Automatic mode picks the operation from context (selected anchor, tracking, local component presence).

Target resolution and preservation

Every selected composition root is processed with its own explicit anchor. SXL Studio resolves the complete selection before the first canvas mutation, so a chain such as sm → md and md → lg cannot make the first rename steal the second JSON file's target.

  • A unique live identity updates that Component or ComponentSet in place. Its Figma node ID stays stable, so existing instances keep their component link.
  • In Automatic mode, a stale or cross-file identity with no live candidate in the current Figma document creates new output.
  • An explicit Apply without a matching target asks you to select the intended root or use Generate. Ambiguous, conflicting, or incompletely scanned candidates stop before mutation.
  • Apply preserves consumer overrides. It does not blanket-reset text, component properties, instance swaps, or properties the composition does not author.
  • Generate and Apply from the plugin UI do not require Bridge. Remote Connect execution and Git Sync Local Storage still use Bridge for their own transport/storage contracts.

SXL Studio manages a portable source identity under $extensions["sxl.studio"].composition. Older JSON without this block remains supported; a successful Generate or anchored Apply attempts to backfill it without rewriting unrelated $extensions:

JSON
{
  "$extensions": {
    "sxl.studio": {
      "composition": {
        "id": "11111111-1111-4111-8111-111111111111",
        "roots": [
          {
            "nodeId": "21649:1480",
            "key": "figma-component-key",
            "type": "COMPONENT_SET"
          }
        ]
      }
    }
  }
}

This block is managed metadata, not an authoring input. If storage is temporarily unavailable, the canvas operation stays successful and reports a warning; identity persistence is retried on a later run.

Adopt & Audit existing components

A component created manually before Composition JSON existed does not need to be deleted. Use the composition badge context menu:

ActionWhat it doesWhen to use it
Adopt compositionLinks the selected component/set to the JSON without rebuilding layers.When the Figma component exists and future Apply should update it.
Audit compositionCompares the selected/linked component against the JSON and reports sync status.Before adopting a legacy component, after manual edits, or before repair.

Adopt is conservative: it keeps component identity (instances elsewhere keep their link), checks that variant names match the JSON matrix, and does not rewrite layers, styles, or properties until an audit/apply confirms the real structure.


Root fields

The top-level keys of a composition file.

FieldTypeRequiredNotes
$type"composition"File type marker
namestringComponent / component-set name
structurenodeLayer tree
stylesobjectBase styles + selector rules
propsobjectVariant axes
statesstring[]Mutually-exclusive axes taken from props
componentPropertiesobjectNative Figma properties
transitionsobjectPrototype transitions (transition singular accepted as a legacy alias)
themeBindingobjectVariant prop → variable-collection modes
componentbooleanDefault true; false builds plain nodes and forbids non-empty props
$descriptionstringFigma description written on the root component/set
$metadataanyOpaque tooling metadata; ignored by generation
size / styleobjectOptional embedded token blocks (see Advanced fields)
slotHostPipelineobjectAdvanced host/slot systems (see Advanced fields)
segmentIconPostPassRefMarkersstring[]Advanced icon-sync markers
selectorsautoautoGenerated by the parser from styles keys — do not author manually

Warning Removed legacy keys error on parse: adapters, sizeStyles, colorStyles. And component: false is incompatible with non-empty props.


Structure

structure is the layer tree the generator builds. Every node needs a tag and a class; everything else is optional.

Node fields

FieldRequiredWhat it doesFigma equivalentFrontend equivalent
tagNode kind to createLayer typeElement type
classStyle/target binding keyLayer selectorclassName
nameExplicit Figma layer nameLayer namedata-name / label
layerAlias for name when name is unsetLayer namedata-name
contentText content (mainly TEXT)Text valuetext node / children
descriptionHuman note, no visual effectLayer descriptioncode comment
refComponent to instantiate + configInstance → main comp.imported component
slotNative slot config (reference mode)Slot property<slot> / children
childrenNested child nodesNested layerschild elements
vectorPathsSVG-like path data (VECTOR only)Vector paths<path d="…">
vectorNetworkEditable vertex geometry (VECTOR)Vector network
viewBox[x, y, w, h] coordinate box (VECTOR)SVG viewBox
JSON
"structure": {
  "tag": "FRAME",
  "class": "root",
  "name": "Root",
  "children": []
}

Per-field JSON examples:

JSON
{ "tag": "FRAME", "class": "card" }
JSON
{ "tag": "TEXT", "class": "label", "name": "Label", "content": "Continue" }
JSON
{ "tag": "INSTANCE", "class": "icon", "ref": { "component": "circle-info", "properties": { "size": "md" } } }
JSON
{ "tag": "SLOT", "class": "content-slot", "slot": { "default": "WButton", "preferred": ["WButton", "WChip"] } }
JSON
{ "tag": "FRAME", "class": "row", "description": "Header row", "children": [{ "tag": "TEXT", "class": "title", "content": "Title" }] }

Note class is the style key, name is the Figma layer name. For nested targeting (ref.nested, NESTED_INSTANCE), always set an explicit, stable name.

Supported tag values

TagWhat it createsFigma equivalentFrontend equivalentConstraints
FRAMEContainer / auto-layout frameFrame / Auto Layout<div> (flex container)
TEXTText layerText<span> / <p>value comes from content
COMPONENTNested main component nodeComponentcomponent definition
INSTANCEInstance from refInstance<Component /> usageneeds ref; placeholder if unresolved
ICONIcon instance resolved from refIcon instance<Icon />like INSTANCE; color recolors the glyph
SLOTNative slot (or fallback)Slot<slot> / {children}reference or children mode, not both
RECTANGLERectangle shapeRectangle<div> blockshape paint via fill
ELLIPSEEllipse shapeEllipseborder-radius: 50% div
LINELine shapeLine<hr> / divider
VECTORCustom vector from path data / vector networkVector (Pen)inline <svg><path>not a raw SVG import

Per-tag JSON examples:

JSON
{ "tag": "FRAME", "class": "card", "children": [] }
JSON
{ "tag": "TEXT", "class": "label", "content": "Continue" }
JSON
{ "tag": "COMPONENT", "class": "chip" }
JSON
{ "tag": "INSTANCE", "class": "cta", "ref": { "component": "WButton", "properties": { "variant": "primary" } } }
JSON
{ "tag": "ICON", "class": "leading-icon", "ref": { "component": "circle-info" } }
JSON
{ "tag": "SLOT", "class": "content-slot", "slot": { "default": "WButton" } }
JSON
{ "tag": "RECTANGLE", "class": "bg" }
JSON
{ "tag": "ELLIPSE", "class": "avatar-mask" }
JSON
{ "tag": "LINE", "class": "divider" }
JSON
{
  "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]
}

Use VECTOR when a shape is not a rectangle, ellipse, or line — a slanted button body, a wave, a tab notch, a custom badge. It accepts SVG-like vectorPaths or a Figma-like vectorNetwork for editable vertices, and takes the same visual styles as other shapes. Vertex rounding can be tokenized from styles via vectorCornerRadii.

Slanted button pattern

Keep editable shape geometry in structure, and drive color/size/rounding from styles. The middle stays a normal auto-layout FRAME so text and icons behave normally.

JSON
{
  "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" }] }
  ]
}
JSON
".left-shape": { "widthType": "fixed", "heightType": "fixed", "width": 18, "height": 56, "fill": "{button.bg.accent}", "vectorCornerRadii": [0, "{radius.md}", "{radius.md}", 0] }

vectorCornerRadii accepts an array (by vertex order) or an object ({ "1": "{radius.md}" }). Vertex indexes are zero-based. VECTOR is not a raw SVG import — put curves, skew, and cut-outs into vectorPaths / vectorNetwork.

SLOT modes and rules

SLOT has strict modes: reference mode (a slot config with an optional ref template) or children mode (inline children). Never both.

JSON
{
  "tag": "SLOT",
  "class": "content-slot",
  "slot": { "default": "WButton", "preferred": ["WButton", "WChip"] },
  "ref": { "component": "WButton", "properties": { "size": "md" } }
}
JSON
{
  "tag": "SLOT",
  "class": "rows",
  "children": [{ "tag": "FRAME", "class": "row", "children": [] }]
}

Constraints: a SLOT cannot have both slot and non-empty children; SLOT.ref is only valid in reference mode. A SLOT with exactly one INSTANCE child (with ref, no slot) is treated as reference-mode shorthand.

Note The canonical, schema-portable form for both slot.preferred and componentProperties.*.preferred is an array of component names. Apply also accepts Figma API-derived entries such as { "type": "COMPONENT_SET", "key": "..." }; an extra display label such as component is ignored. Use component names when the file must validate against a string-only external schema.

ref for INSTANCE / SLOT

FieldWhat it doesExample
componentTarget component name (required when ref exists)"component": "WButton"
libraryLibrary hint"library": "SXL DS"
keyPublish-key hint"key": "abc123"
propertiesPrimitive instance property values"properties": { "state": "active" }
stylesStyles for the referenced instance root"styles": { "width": "{size.icon}" }
iconBindPropertyExplicit property name for icon swap"iconBindProperty": "icon"
iconNested icon swap descriptor"icon": { "component": "fire-3" }
overridesChild content overrides"overrides": { "label": "Apply" }
slotsSlot overrides on the referenced instancesee below
nestedNested instance updates by layer namesee below
descriptionOptional note"description": "Primary CTA"

ref.styles is for the materialized referenced INSTANCE root. Use it when the default instance itself needs layout or paint bindings, for example an icon size binding inside a SLOT. It supports the same style aliases and token refs as normal styles, including widthType, heightType, width, height, fill, and color.

The host style block still styles the composition node itself. In reference-mode slots, use refStyles inside a normal style block when variants need to override the referenced root. refStyles merges over ref.styles and is applied only while the current slot child still matches ref.component; it is not cascaded to a preferred/user swap such as WBadge.

JSON
{
  "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}"
    }
  }
}
JSON
"styles": {
  "$placement=inner .leading-slot": {
    "refStyles": {
      "width": "{sz.fixed.reg.3xs}",
      "height": "{sz.fixed.reg.3xs}"
    }
  }
}

ref.nested — target nested instances by layer name:

JSON
"nested": {
  "Badge": {
    "properties": { "label": "3" },
    "icon": { "component": "fire-3", "properties": { "style": "filled" } }
  }
}

ref.slots — override slots inside the referenced instance (op: replace, append, patch):

JSON
"slots": {
  "footer": {
    "op": "replace",
    "nodes": [{ "component": "WButton", "name": "apply", "properties": { "variant": "primary" } }]
  }
}

A component-set variant can wrap a different existing component by overriding its ref from a variant selector, so one INSTANCE node serves the whole wrapper set:

JSON
"styles": {
  "$item=neutral-secondary-sm .item": {
    "ref": { "component": "WButton", "properties": { "style": "neutral", "variant": "secondary", "size": "sm" } }
  }
}

Props

KeyTypeWhat it does
propsRecord<string, (string | boolean | number)[]>Declares each variant axis and its allowed values
statesstring[]Names of props axes that are mutually-exclusive states

props declares every allowed value for each axis; each combination becomes one generated variant.

JSON
"props": {
  "size": ["sm", "md", "lg"],
  "state": ["default", "hover", "active"],
  "compact": [true, false]
}

Behavior: selector matching compares values as strings; an axis with default uses it as fallback for missing value-specific rules, otherwise the first value is the fallback. Keep the axis count intentional — variant combinations grow multiplicatively.

states (mutually-exclusive axes)

states lists axes from props that behave like mutually-exclusive UI conditions (:disabled, :loading) rather than combinable variants. They are excluded from the cartesian product — each non-default value adds a single variant on top of the defaults.

JSON
"props": {
  "_state": ["default", "hover", "focus", "select"],
  "loading": ["false", "true"],
  "disabled": ["false", "true"]
},
"states": ["loading", "disabled"]

Without states this is a 4 × 2 × 2 = 16-variant matrix; with states it compacts to 6 (4 interaction variants + loading=true + disabled=true). Rules: the off value is the axis's first value; every name in states must exist in props; Apply re-compacts an existing set declaratively. Keep normal visual choices (size, variant, tone, theme) in props only.

Codegen mapping:

  • React / Vue: an axis whose options are exactly false / true becomes a boolean prop (e.g. loading?: boolean);
  • DivKit: component sets are emitted through card.states / state_id; states only controls which variants are materialized, it does not generate a fake custom_type;
  • a _-prefixed axis (e.g. _state) is an internal interaction-state axis, excluded from the public codegen API (the consumer's own :hover / :focus CSS drives it); non-_ axes like loading / disabled are emitted as real props.

The extended object form with combineWith (crossing a state with structural axes) is reserved but not yet implemented — using it raises a validation error.


Component Properties

componentProperties defines native Figma properties exposed on instances.

Typelayer refers todefaultValue
TEXTclass in structureRequired; auto-derived from text content if omitted
BOOLEANclass in structureRequired; defaults to true if omitted
INSTANCE_SWAPclass in structureRequired
SLOTclass in structureRequired
NESTED_INSTANCENested instance layer name (not class)Not required

One example per type:

JSON
"title": { "type": "TEXT", "layer": "title", "defaultValue": "Promo title" }
JSON
"showMeta": { "type": "BOOLEAN", "layer": "meta", "defaultValue": true }
JSON
"icon": { "type": "INSTANCE_SWAP", "layer": "icon", "defaultValue": "circle-info", "preferred": ["circle-info", "star"] }
JSON
"media": { "type": "SLOT", "layer": "media-slot", "defaultValue": "WImageTile" }
JSON
"badge": { "type": "NESTED_INSTANCE", "layer": "Badge" }

layer rule: for TEXT, BOOLEAN, INSTANCE_SWAP, SLOT use the structure class; for NESTED_INSTANCE use the real nested Figma layer name.


Styles

styles controls layer appearance, layout, and per-variant overrides. A key is a CSS-like class (.card), a legacy class (card), or a variant selector ($state=hover .card); blocks can be nested. Every property below is shown with its exact JSON spelling, grouped by purpose:

GroupCovers
Container & auto-layoutdirection, justifyContent, alignItems, gap, padding, …
Layer sizewidthType, width, min/maxWidth, aspectRatio, …
Positioningposition, top/right/bottom/left, x/y, alignSelf, …
Gridrows/columns, rowGap/columnGap, grid-child align/span
Background/fill/colorbackground, fill, color
Borders & cornersborder, borderRadius, strokeAlign, cornerSmooth, …
Shadows & effectsboxShadow, backgroundBlur, layerBlur, opacity, glass
TypographyfontFamily, fontSize, lineHeight, textCase, …
Visibility & resetvisible, display, none-clearing
Instance controlcomponent, instanceProperties, nestedInstanceProperties, ref
Variables & metadataexplicitVariableModes, layoutGrids, exportSettings, mask
JSON
"styles": {
  ".root": { "direction": "row", "gap": 8, "padding": "12 16" },
  "$state=hover .root": { "background": "{color.brand.hover}" }
}

Selectors and cascade

  • base styles apply first; matching selectors apply by specificity (more conditions win);
  • same specificity → later JSON declaration wins;
  • descendant selectors resolve through dot paths (.footer .itemfooter.item);
  • when a class repeats in different branches, prefer a full path (.header .item) over a bare .item.
JSON
"styles": {
  ".wrap": {
    "padding": 8,
    ".item": { "widthType": "fill" },
    "$state=active": { ".item": { "opacity": 1 } }
  }
}

Container and auto-layout

PropertyWhat it does — valuesExample
directionLayout axis — row, column, grid, none"direction": "row"
justifyContentMain-axis distribution — start, center, end, space-between"justifyContent": "space-between"
alignItemsCross-axis alignment — start, center, end, baseline"alignItems": "center"
alignContentWrapped-rows distribution — auto, space-between"alignContent": "space-between"
flexWrapWrapping — nowrap, wrap"flexWrap": "wrap"
gapSpacing between children (px)"gap": 12
wrapGapCross-axis spacing between wrapped rows (px)"wrapGap": 8
paddingInner padding — number, "T R B L", or "none""padding": "16 16 20 16"
overflowClipping — hidden, clip, visible, auto, scroll"overflow": "hidden"
primaryAxisSizingModeContainer main-axis sizing — auto, fixed"primaryAxisSizingMode": "auto"
counterAxisSizingModeContainer cross-axis sizing — auto, fixed"counterAxisSizingMode": "fixed"
boxSizingStrokes counted in layout — border-box, content-box"boxSizing": "border-box"
canvasStackingSibling z-order — first-on-top, last-on-top"canvasStacking": "first-on-top"
JSON
".header": { "direction": "row", "justifyContent": "space-between", "alignItems": "center", "gap": 8, "padding": "12 16" }

Layer size

PropertyWhat it does — valuesExample
widthType / heightTypeSizing mode — fixed, hug, fill"widthType": "fill"
width / heightFixed size (px)"width": 360
minWidth / maxWidthWidth bounds (px)"maxWidth": 480
minHeight / maxHeightHeight bounds (px)"minHeight": 40
aspectRatioLock ratio — "1/1", or "none" / false to unlock"aspectRatio": "1/1"
constrainProportionsLock width/height proportions — true, false"constrainProportions": true

targetAspectRatio is an accepted alias of aspectRatio.

Positioning

PropertyWhat it does — valuesExample
positionFlow — relative, absolute, none"position": "absolute"
top / right / bottom / leftAbsolute insets (px)"top": 8
x / yAbsolute canvas coordinates (px)"x": 24
alignSelfOverride cross alignment — inherit, stretch, start, center, end"alignSelf": "stretch"
flexGrowGrow factor"flexGrow": 1
rotateRotation (deg)"rotate": 45
constraintH / constraintVFigma constraints — left, center, right, scale, stretch"constraintH": "center"
JSON
".badge": { "position": "absolute", "top": 8, "right": 8 }

Grid layout

Set when direction is grid.

PropertyWhat it doesExample
rows / columnsTrack counts"columns": 3
rowGap / columnGapTrack gaps (px)"columnGap": 8
gridTemplateRows / gridTemplateColumnsTrack sizes"gridTemplateColumns": "1fr 1fr"
justifySelf / gridAlignSelfGrid-child align (H / V) — start, center, end, auto"justifySelf": "center"
gridRowSpan / gridColumnSpanGrid-child span (tracks)"gridColumnSpan": 2

Background, fill, color

PropertyWhat it does — valuesExample
backgroundContainer fill — hex, gradient, layer array, or "none" / "transparent""background": "#FFFFFF"
fillShape / vector / icon paint — hex or token, "none" clears"fill": "{button.primary}"
colorCSS-like currentColor (text + icon glyph) — hex or token, "none""color": "{text.primary}"

color is CSS-like currentColor: on TEXT it sets the text fill; on ICON / icon instances it recolors the glyph (fill-based icons get a fill, stroke-based icons get a stroke). Token refs keep the variable binding; literal colors replace it. fill and strokes are lower-level — use them to target shape fills or a direct stroke. For a stroke-only icon, "fill": "none" keeps just the stroke paint.

Gradient background:

JSON
".hero": { "background": "linear-gradient(135deg, #7B5CFA 0%, #FA5CB4 100%)" }

Multi-layer background (image + overlay):

JSON
".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)"
  ]
}

Borders, stroke, corners

PropertyWhat it does — valuesExample
borderShorthand "<width> <style> <color>""border": "1px solid #E7EAF1"
borderColorStroke color"borderColor": "#E7EAF1"
borderWidthStroke weight (px)"borderWidth": 1
borderStyleStroke style — solid, dashed"borderStyle": "dashed"
borderRadiusCorner radius (px)"borderRadius": 20
cornerSmoothCorner smoothing 01 (squircle)"cornerSmooth": 0.6
strokeAlignStroke position — inside, center, outside"strokeAlign": "inside"
strokeCap / strokeJoinLine cap / join style"strokeCap": "ROUND"
strokeTopWeightstrokeLeftWeightPer-side stroke weight (px)"strokeTopWeight": 2
dashPatternDashed stroke — array of dash/gap lengths"dashPattern": [4, 4]
outline / outlineOffsetOutside stroke + offset"outline": "2px solid #0D6EFD"
vectorCornerRadiiPer-vertex radii for a VECTOR (by vertex order)"vectorCornerRadii": [0, 8, 8, 0]
JSON
".card": { "border": "1px solid #E7EAF1", "borderRadius": 20 },
"$state=active .card": { "outline": "2px solid #0D6EFD", "outlineOffset": "2px" }

Shadows and effects

PropertyWhat it does — valuesExample
boxShadowDrop shadows — array of shadow objects, or "none"see block
backgroundBlurBackground blur radius (px), or "none""backgroundBlur": 12
layerBlurLayer blur radius (px), or "none""layerBlur": 8
opacityLayer opacity 01, or "none" (= 1)"opacity": 0.56
glassGlass effect, or "none" to clear"glass": "none"
effectsFull effects composite (token alias or layers)"effects": "{shadow.md}"
JSON
".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)" }] }

Typography

Set on a TEXT node.

PropertyWhat it does — valuesExample
fontFamilyFont family"fontFamily": "Inter"
fontWeightWeight"fontWeight": 600
fontSizeSize (px)"fontSize": 18
lineHeightLine height"lineHeight": "24px"
letterSpacingTracking"letterSpacing": "0px"
textAlignHorizontal — left, center, right, justify"textAlign": "left"
verticalAlignVertical — top, center, middle, bottom"verticalAlign": "center"
textCaseCase — none, uppercase, lowercase, capitalize, small-caps"textCase": "uppercase"
textDecorationDecoration — none, underline, line-through"textDecoration": "underline"
textSizingAuto-resize — fixed, height-auto, auto, truncate"textSizing": "height-auto"
textTruncationTruncation — disabled, ending (ellipsis)"textTruncation": "ending"
maxLinesMax lines before truncation"maxLines": 2
paragraphSpacingSpace between paragraphs (px)"paragraphSpacing": 8
paragraphIndentFirst-line indent (px)"paragraphIndent": 16
leadingTrim / verticalTrimTrim line-box leading — CAP_HEIGHT, NONE"leadingTrim": "CAP_HEIGHT"
JSON
".title": { "fontFamily": "Inter", "fontWeight": 600, "fontSize": 18, "lineHeight": "24px", "textAlign": "left", "textSizing": "height-auto" }

The composite typography / font key applies a full text style at once.

Visibility and reset

  • visible: false keeps the layer in the variant but hides it in Figma;
  • display: "none" omits the branch from the resolved variant;
  • if both are set, display: "none" wins. When a later selector re-shows a branch, include the size/layout it needs.
JSON
"$loading=true": {
  ".button": {
    ".label": { "visible": false },
    ".spinner": { "display": "flex", "visible": true, "width": "{button.size.icon}" }
  }
}

CSS-like clearing values (write these to reset a property):

Reset spellingEffect
"background": "none" / "transparent"Clears fills
"color": "none" / "fill": "none"Clears text/currentColor/paints
"boxShadow": "none" / "effects": "none"Clears effects
"padding": "none"All paddings 0
"opacity": "none"Resolves to 1
"mask": "none" / falseClears the mask
"aspectRatio": "none" / falseUnlocks the ratio

Instance control

Set on an INSTANCE node.

PropertyWhat it doesExample
componentSwap the main component (name-only swap)"component": "WButton"
instanceProperties / propertiesSet instance property valuessee block
nestedInstancePropertiesSet nested instance props by layer namesee block
refFull instance target overridesee ref section
JSON
".action-main": { "component": "WButton", "instanceProperties": { "variant": "primary", "size": "md", "label": "Continue" } }
JSON
".badge": { "nestedInstanceProperties": { "Badge": { "label": "2" } } }

Variable modes and Figma metadata

PropertyWhat it doesExample
explicitVariableModesForce a variable mode per collection"explicitVariableModes": { "Themes": "Dark" }
variableModesAlias of explicitVariableModes"variableModes": { "Themes": "Dark" }
layoutGridsFigma layout grids arraysee block
exportSettingsFigma export settings arraysee block
mask / maskTypeLayer mask — alpha, vector, luminance, none"mask": "alpha"
JSON
".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 } }]
}

Clear an explicit mode for a collection with null, false, "none", "auto", or "unset".


Transitions

Prototype transitions are defined at the composition root (shorthand or object form), never as a layer style key.

FieldWhat it doesValues
triggerWhen it fireson-hover, on-click, on-press, on-drag, on-enter, on-leave, mouse-up, mouse-down, after-timeout: 500
animationTransition typesmart-animate, dissolve, instant, scroll-animate, slide-in, slide-out, push, move-in, move-out
durationLengthe.g. 200ms
easingCurvelinear, ease-in, ease-out, ease-in-out, ease-in-back, ease-out-back, ease-in-out-back, gentle, quick, bouncy, slow, cubic-bezier(...), spring(...)
directionOptional directionleft, right, top, bottom
conditionOptional variable gate{ "variable": …, "op": "==", "value": … }

Shorthand — order is trigger animation duration easing [direction]:

JSON
"transitions": { "hover-in": "on-hover smart-animate 200ms ease-out" }

Object form with a condition:

JSON
"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 maps a variant axis (usually theme) to variable-collection modes for correct light/dark rendering.

FieldWhat it doesExample
targetBinding target label"target": "root"
propAxis name from props"prop": "theme"
modesMap of axis value → mode config{ "light": { "type": "local", "collection": "Themes", "mode": "Light" } }
applyToWhich domains to switch["variables", "textStyles", "effectStyles"]
JSON
"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 must exist in props. A mode source has type (local / library), collection, mode, and optional library. Each modes.<value> is either a flat source or a wrapped { primary, fallback[] }.


Advanced fields

Embedded size / style

A composition file can embed DTCG size and style token blocks for self-contained component packs (namespaced by component name). They are optional and do not affect layout on their own.

JSON
"size": { "md": { "height": "40px", "paddingX": "16px" } },
"style": { "primary": { "bg": "{color.brand.primary}" } }

Slot-host pipeline

For host/item systems (segmented controls, tabs, item groups), slotHostPipeline wires a host set to its item set. slotItemComponentSetName is required. If any composition file defines slotHostPipeline, the legacy WSegmentControl defaults are not applied — declare every host/item set you rely on.

JSON
"slotHostPipeline": {
  "hostComponentSetName": "WSegmentControl",
  "hostCompositionTokenName": "WSegmentControl",
  "slotName": "item-group",
  "slotItemComponentSetName": "WSegmentControlItem"
},
"segmentIconPostPassRefMarkers": ["WSegmentControl", "WSegmentControlItem"]

Both keys are also accepted under a $figma wrapper ($figma.slotHostPipeline, $figma.segmentIconPostPassRefMarkers).


Full example

A complete component set touching most features: multi-axis props, componentProperties, a SLOT and an INSTANCE, selector overrides, root transitions, and themeBinding.

JSON
{
  "$type": "composition",
  "name": "WPromoCard",
  "$description": "Promo card with media slot, action and variant states.",
  "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"]
  }
}

Adaptation checklist: replace component names (WImageTile, WButton) with names from your library; create a Themes collection or drop themeBinding; start with Generate, then iterate with Apply; keep name stable on nested-targeted nodes.


For agents & transformers

A) Figma → Composition JSON

  1. Set $type: "composition" and a stable name.
  2. Build props only from real variant axes; add states for mutually-exclusive conditions.
  3. For each node write a valid tag and class; add stable name for nested-targeted nodes.
  4. For instances write ref.component and only valid primitive ref.properties.
  5. For slots use reference mode (slot) or children mode (children), never both.
  6. Put all visual/layout rules into styles (base + selector overrides).
  7. Use root-level transitions and themeBinding, not layer-level hacks.
  8. Validate that referenced components/tokens exist.

B) Composition JSON → React / Vue

  1. Read structure as the element tree; use class/path keys for style resolution.
  2. Resolve variant selectors ($prop=value .class) against incoming props.
  3. Respect branch visibility (display: "none").
  4. Treat ref.component → component import, ref.properties → passed props.
  5. Treat componentProperties, themeBinding, slotHostPipeline, and $figma as design-time metadata unless your runtime supports them.
  6. Preserve token refs ({...}) or pre-resolve them through your token engine.

C) Pre-flight validation

  • structure exists and every node has tag + class;
  • styles exists and only contains object values;
  • themeBinding.prop exists in props;
  • no removed legacy keys (adapters, sizeStyles, colorStyles);
  • no SLOT mode conflict (slot with non-empty children).

Author key → Figma runtime key (reference)

Author keyRuntime key
directionlayoutMode
justifyContentprimaryAxisAlignItems
alignItemscounterAxisAlignItems
alignContentcounterAxisAlignContent
flexWraplayoutWrap
gap / wrapGapitemSpacing / counterAxisSpacing
widthType / heightTypelayoutSizingHorizontal / Vertical
alignSelf / flexGrowlayoutAlign / layoutGrow
positionlayoutPositioning
top / right / bottom / leftinsets → layout/position
rows / columnsgridRowCount / gridColumnCount
rowGap / columnGapgridRowGap / gridColumnGap
borderWidth / borderAlignstrokeWeight / strokeAlign
borderRadius / cornerSmoothcornerRadius / cornerSmoothing
textSizingtextAutoResize
textAlign / verticalAligntextAlignHorizontal / Vertical
rotaterotation
boxSizing / canvasStackingstrokesIncludedInLayout / itemReverseZIndex
justifySelf / gridAlignSelfgridChildHorizontalAlign / VerticalAlign

Author value → Figma enum (reference)

PropertyAuthor → enum
directionrow/column/none/gridHORIZONTAL/VERTICAL/NONE/GRID
justifyContentstart/center/end/space-betweenMIN/CENTER/MAX/SPACE_BETWEEN
alignItemsstart/center/end/baselineMIN/CENTER/MAX/BASELINE
widthType / heightTypefixed/hug/fill (aliases: auto→HUG, stretch→FILL) → FIXED/HUG/FILL
positionrelative/absolute/noneAUTO/ABSOLUTE/AUTO
textSizingfixed/height-auto/auto/truncateNONE/HEIGHT/WIDTH_AND_HEIGHT/TRUNCATE
textCasenone/uppercase/lowercase/capitalize/small-capsORIGINAL/UPPER/LOWER/TITLE/SMALL_CAPS
strokeAligninside/center/outsideINSIDE/CENTER/OUTSIDE
maskalpha/vector/luminance/noneALPHA/VECTOR/LUMINANCE or isMask:false

Notes: token references ({...}) are preserved and resolved at apply time; numeric and unit strings (px, rem, deg) are normalized to numbers where needed.


Limitations & warnings

  • Unsupported property/node combinations are ignored.
  • Third-party/library instance internals can be partially protected by Figma.
  • NESTED_INSTANCE and ref.nested rely on stable layer names.
  • Large props matrices produce heavy variant counts — use states where appropriate.
  • Figma Generate/Apply silently skips unknown style keys without blocking documented properties and aliases. Keep unknown keys only when another output, such as code generation, consumes them.
  • Removed legacy keys error on parse: adapters, sizeStyles, colorStyles. component: false is incompatible with non-empty props.