Releases
Version history for both Specs packages. Each release follows Semantic Versioning.
[0.24.0] - 2026-06-05
Introduces the transformer pipeline’s schema foundation. Config.transformers (a flat TransformEntry[]) replaces the old two-level config.transform.transformers wrapper, and a new workspace.schema.json provides IDE validation for specs.config.yaml. Config.processing.states adds a concept-keyed map that classifies Figma variant props as browser-driven or consumer-controlled states, enabling the css transformer to emit semantic CSS selectors and the contract transformer to omit browser-driven props from Props interfaces.
Added
-
TransformEntrytype (ADR-053) —{ name: string; [option: string]: unknown }. Describes a single transformer entry inconfig.transformers. Transformer-specific options sit inline alongsidename. -
workspace.schema.json(ADR-054) — New top-level JSON schema file for the workspace config file (specs.config.yaml). Defines the full workspace shape includingdataDirectory,outputDirectory,sources,output,author, and theconfigblock (which embeds the componentConfigschema).Config.transformersis defined here rather than in the component schema, keeping transform configuration CLI-only and out of the per-component spec. -
Config.transformers—TransformEntry[]directly onConfig— Replaces the two-levelconfig.transform.transformersshape. The wrapper object is gone;config.transformersis the array. Absence means CLI defaults apply. -
VariantStateEntrytype (ADR-055) — classifies a Figma variant prop as a semantic state concept; fields:prop(string, required),value(string, optional — the Figma enum value activating the concept; defaults to"true"for boolean props),contract('omit'|'keep', optional — overrides the concept’s canonical default) -
Config.processing.states—Record<string, VariantStateEntry>— concept-keyed map (key = concept name, e.g.hover,disabled); classifies variant props for deterministic use by CSS and contract transformers; absence means all props emit asdata-*attribute selectors and all props are retained in contracts
Changed
Config.transformremoved; replaced byConfig.transformers— The intermediateTransformConfigwrapper object is eliminated.transform.transformerscollapses totransformersdirectly onConfig/ResolvedConfig.
Removed
[0.23.0] - Unreleased
Added
Changed
Removed
[0.22.0] - 2026-05-23
Introduces the composition and slot-content model across ADRs 042, 046–052. Composition is a named registry entry with a top-level anatomy + elements + layout triplet and an optional slotContent map of bundled SlotContent fills. SlotContentRef ({ $slotContent }) is the universal pointer for all slot fills — component-scoped examples, intra-composition bundled fills, and cross-composition references all resolve to anatomy + elements + layout. PropConfigurations widens to accept PropBinding and SlotContentRef; InstanceExample.propConfigurations widens to accept SlotContentRef (not PropBinding). Children widens to SlotBinding for slot-bound containers. Config gains the examples-detection settings and output gates (ADR-050).
Added
SlotContent—{ anatomy, elements, layout }; the anonymous structural triplet used as a named slot fill. Carries no metadata; identity lives at the key where it is stored (ADR-046)Composition—{ title?, description?, anatomy, elements, layout, slotContent?: Record<string, SlotContent> }; a named registry entry whose top-level triplet is the primary content. OptionalslotContentbundles named fills alongside it for authoring convenience (ADR-042, ADR-046)Compositions—Record<string, Composition>; registry alias for external composition filesSlotContentRef—{ $slotContent: string }; universal JSON Pointer reference for slot fills. Resolves to aCompositionentry (top-level triplet), aComposition.slotContententry, or aComponent.slotContentExamplesentry — all yieldanatomy + elements + layout(ADR-046)Component.slotContentExamples—Record<string, SlotContent>; named slot-content examples per component, referenceable bySlotContentReffromSlotBinding.examplesand fromElement.propConfigurationsslot-prop entries.specs-from-figmade-duplicates entries by structural equality across variants and slots (ADR-047)SlotBinding— extendsPropBindingwith optionalexamples?: SlotContentRef[]; used inElement.childrenfor slot-bound containers. Eachexamples[i]points to a slot-content entry; emitters currently write a single entry at index 0 — Figma’s authoring default for the slot layer. Non-contractual reference material; code consumers handle missing slots through component logic and ignore it (ADR-047)Component.instanceExamples— typed asInstanceExamples(Record<string, InstanceExample>); named documented usages per component (ADR-048)InstanceExample—{ title?, propConfigurations?: Record<string, string | number | boolean | SlotContentRef> }; scalar props set directly, slot props filled viaSlotContentRef.PropBindingnot accepted — documented configurations are not live bindings (ADR-048)Config.processing.instanceExamples—{ scope?: 'PAGE' | 'FILE', match?: string[], exclude?: string[], parentNames?: string[] }; instance-example detection settings, mirroringprocessing.subcomponents.scopeisPAGE | FILE(NESTEDis inapplicable);matchis an optional secondary name filter (the primary relevance test is structural identity — the candidate is an instance of the component being generated — so absence accepts every in-scope instance, subject toexclude/parentNames);parentNamesfilters candidates by immediate-parent frame/section name. Absence of the block is the off-switch — its presence enables both detection and output (no separate include flag), likeprocessing.subcomponents. Pro-gated. Added toResolvedConfig(withscoperequired,matchoptional) (ADR-050)Config.include.defaultSlotContent—booleanoutput gate, defaultfalse; added toResolvedConfig(required) andDEFAULT_CONFIG. Pro-gated — ignored on the free tier (ADR-050)Config.format.tokens— addsFIGMA_SYNTAX_WEB,FIGMA_SYNTAX_IOS,FIGMA_SYNTAX_ANDROIDprofiles emitting per-platform Figma code syntax, falling back toTOKEN(ADR-051)
Changed
Children— widened fromstring[] | PropBindingtostring[] | SlotBinding; existing{ $binding }values still validate becauseSlotBindingis a structural superset ofPropBinding(ADR-047)PropConfigurations— value union widened fromstring | number | booleantostring | number | boolean | PropBinding | SlotContentRef; enables scalar prop pass-through (PropBinding) and slot-prop fills (SlotContentRef).InstanceExample.propConfigurationswidens separately and does not acceptPropBinding(ADR-049)PropConfigurations.$nested— added reserved key typed asNestedPropConfiguration[]({ path: string[] }plus the prop-config payload, keyed valuePropConfigurationValue); path-addressed configurations of nested descendant instances, wherepathis a list ofinstanceOfelement keys to the configured descendant and the terminal instance owns the filled slot/prop. Deep slot fills reuseSlotContentRef— no new payload, noElementchange.InstanceExample.propConfigurationsdoes not gain$nested(ADR-052)
Removed
[0.21.0] - 2026-05-22
Extends Config.format.tokens with per-platform Figma code syntax profiles. The new FIGMA_SYNTAX_WEB, FIGMA_SYNTAX_IOS, and FIGMA_SYNTAX_ANDROID options emit the platform-specific code syntax authored on Figma variables, falling back to the resolved TOKEN reference when no syntax is defined.
Added
Config.format.tokens— addsFIGMA_SYNTAX_WEB,FIGMA_SYNTAX_IOS,FIGMA_SYNTAX_ANDROIDprofiles emitting per-platform Figma code syntax, falling back toTOKEN
[0.20.0] - 2026-05-06
Adds configurable color output format (Config.format.color) supporting nine format options from hex strings to structured DTCG Color objects. Renames ColorValue to ColorObject for specificity and widens ColorStyle, Shadow.color, and GradientStop.color to accept formatted color strings alongside structured objects and token references.
Added
Config.format.color— typed asColorFormat('HEX' | 'HEXA' | 'RGB' | 'RGBA' | 'HSLA' | 'HSB' | 'OKLCH' | 'OKLAB' | 'OBJECT'); controls color value output format; defaults toHEX
Changed
ColorObject— renamed fromColorValuefor specificity; the type represents a DTCG Color §4.1 structured object, not a generic “color value”ColorStyle— widened tostring | ColorObject | TokenReference | GradientValue | null; thestringarm supports formatted color strings whenConfig.format.coloris non-OBJECTShadow.color— widened tostring | ColorObject | TokenReference; samestringarm rationale asColorStyleGradientStop.color— widened tostring | ColorObject | TokenReference; samestringarm rationale asColorStyle
[0.19.0] - 2026-04-28
Replaces Figma-raw x, y, and layoutPositioning with constraint-based positioning properties. position replaces layoutPositioning as a strict Position enum. Directional offsets (top, bottom, start, end, centerHorizontalOffset, centerVerticalOffset) replace x/y with anchor semantics derived from Figma constraints. See the Layout Positioning guide for constraint mapping rules and platform usage examples.
Added
Styles.position— typed asPosition('AUTO' | 'ABSOLUTE') ornull; structural property, not token-bindable (replaceslayoutPositioning)Styles.top— typed asPositionOffset(number | string | null); block-start offset from vertical MIN, STRETCH, or SCALE constraintStyles.bottom— typed asPositionOffset; block-end offset from vertical MAX or STRETCH constraintStyles.start— typed asPositionOffset; inline-start offset from horizontal MIN, STRETCH, or SCALE constraintStyles.end— typed asPositionOffset; inline-end offset from horizontal MAX or STRETCH constraintStyles.centerHorizontalOffset— typed asPositionOffset; horizontal offset from center (CENTER constraint)Styles.centerVerticalOffset— typed asPositionOffset; vertical offset from center (CENTER constraint)
Removed
Styles.x— replaced byStyles.start,Styles.end, orStyles.centerHorizontalOffsetdepending on constraintStyles.y— replaced byStyles.top,Styles.bottom, orStyles.centerVerticalOffsetdepending on constraintStyles.layoutPositioning— replaced byStyles.position
Migration
Styles.x→Styles.start/Styles.end/Styles.centerHorizontalOffset: determine which property to read based on the node’s horizontal constraint; SCALE values are percentage strings (e.g."25%")Styles.y→Styles.top/Styles.bottom/Styles.centerVerticalOffset: same constraint-based mapping for the vertical axisStyles.layoutPositioning→Styles.position: rename and narrow type — replace genericStylehandling with exhaustive match on'AUTO' | 'ABSOLUTE'
[0.18.0] - 2026-04-24
Replaces Figma-native layout property names with semantic equivalents for auto-layout alignment and wrapping. Introduces a bi-axial itemSpacing model that consolidates counterAxisSpacing into a single property, and narrows layoutMode from a generic Style to a strict enum. Five renamed alignment/wrap fields use plain-language names (mainAxisAlignment, crossAxisAlignment, wrapAlignment, wrap) instead of Figma axis terminology. The documentation site was considerably rearchitected with updated content and navigation.
Added
Styles.itemSpacing— widened fromStyletoStyle | ItemSpacing(scalar when uniform,ItemSpacingobject withhorizontal/verticalwhen axes differ)Styles.wrap— boolean toggle for auto-layout wrapping (replaceslayoutWrap)Styles.wrapAlignment— typed asWrapAlignment('START' | 'SPACE_BETWEEN') ornull; structural property, not token-bindable (replacescounterAxisAlignContent)Styles.mainAxisAlignment— typed asMainAxisAlignment('START' | 'END' | 'CENTER' | 'SPACE_BETWEEN') ornull; structural property, not token-bindable (replacesprimaryAxisAlignItems)Styles.crossAxisAlignment— typed asCrossAxisAlignment('START' | 'END' | 'CENTER' | 'STRETCH' | 'BASELINE') ornull; structural property, not token-bindable (replacescounterAxisAlignItems)
Changed
Styles.layoutMode— narrowed fromStyletoLayoutMode('NONE' | 'HORIZONTAL' | 'VERTICAL') ornull; enum constraint, no TokenReference
Removed
Styles.counterAxisSpacing— consolidated intoStyles.itemSpacingbi-axial modelStyles.layoutWrap— replaced byStyles.wrapStyles.counterAxisAlignContent— replaced byStyles.wrapAlignmentStyles.primaryAxisAlignItems— replaced byStyles.mainAxisAlignmentStyles.counterAxisAlignItems— replaced byStyles.crossAxisAlignment
Migration
Styles.counterAxisSpacing→Styles.itemSpacing.vertical: read theverticalsub-field of theItemSpacingobject when axes differ; scalaritemSpacingapplies uniformlyStyles.layoutMode→LayoutMode | null: replace genericStylehandling with exhaustive match on'NONE' | 'HORIZONTAL' | 'VERTICAL'Styles.layoutWrap→Styles.wrap: rename only; same boolean semanticsStyles.counterAxisAlignContent→Styles.wrapAlignment: rename and remap values —'AUTO'→'START','SPACE_BETWEEN'→'SPACE_BETWEEN'Styles.primaryAxisAlignItems→Styles.mainAxisAlignment: rename and narrow type — replace genericStylehandling with exhaustive match on'START' | 'END' | 'CENTER' | 'SPACE_BETWEEN'; remap Figma values'MIN'→'START','MAX'→'END'Styles.counterAxisAlignItems→Styles.crossAxisAlignment: rename and narrow type — replace genericStylehandling with exhaustive match on'START' | 'END' | 'CENTER' | 'STRETCH' | 'BASELINE'; remap Figma values'MIN'→'START','MAX'→'END'
[0.17.0] - 2026-04-13
Introduces ResolvedConfig as the fully-resolved counterpart to Config, where every property with a default is guaranteed present. Config properties with defaults are now optional, making input configs more tolerant of omissions. Removes the unused Variant.name and Variant.baseline fields.
Added
ResolvedConfig— fully-resolved config type where every property with a default is required; only true feature toggles (subcomponents,glyphNamePattern,codeOnlyPropsPattern) remain optional
Changed
Config— all properties with defaults are now optional (input type tolerant of omissions):processing.variantDepth— optional; defaults to 9999processing.details— optional; defaults to LAYEREDformat.output— optional; defaults to JSONformat.keys— optional; defaults to SAFEformat.layout— optional; defaults to LAYOUT
ResolvedConfig— all properties with defaults are required (post-merge guarantee):processing.slotConstraints— required (defaultfalse)processing.inferNumberProps— required (defaultfalse)format.tokens— required (defaultTOKEN)include.invalidVariants— required (defaultfalse)include.invalidCombinations— required (defaulttrue)include.emptyVariants— required (defaultfalse)subcomponents.scope— required when subcomponents is present (defaultNESTED)
DEFAULT_CONFIG— typed asResolvedConfig; now explicitly includesslotConstraints: false,inferNumberProps: false; removedsubcomponents(feature toggle — off by default, matching the Figma plugin)
Removed
Variant.name— unused optional label; variant identity is fully described byconfigurationVariant.baseline— never populated in output; baseline determination is an internal processing concern
Migration
Variant.name→ removed: delete any references; useVariant.configurationto identify variantsVariant.baseline→ removed: delete any references; no replacement needed (field was never populated)
[0.16.0] - 2026-04-05
Adds Config.include.emptyVariants for controlling inclusion of layered variants with no elements. Makes invalidVariants and invalidCombinations optional with sensible defaults, and removes the unused variantNames field.
Added
Config.include.emptyVariants— optional boolean to control inclusion of layered variants that contain no elements; defaults to false
Changed
Config.include.invalidVariants— now optional; defaults to false when absentConfig.include.invalidCombinations— now optional; defaults to true when absent
Removed
Config.include.variantNames— unused field removed from the API
Migration
Config.include.variantNames→ removed: delete any references to this field from config construction code; regenerate cached specs to remove the field from serialized output
[0.15.0] - 2026-03-25
Adds structured subcomponent discovery via a new Config.processing.subcomponents object with scope, match, and exclude settings, replacing the flat subcomponentNamePattern string. Introduces SubcomponentRef for referencing subcomponent definitions and widens instanceOf on both AnatomyElement and Element to accept it. Corrects Typography field types for leadingTrim, fontFamily, and fontStyle to match actual Figma API values.
Added
SubcomponentRef— reference to a subcomponent definition via{ $ref: "#/subcomponents/{key}" }AnatomyElement.instanceOf— widened to acceptstring | SubcomponentRefElement.instanceOf— widened to acceptstring | PropBinding | SubcomponentRefConfig.processing.subcomponents— optional nested object grouping subcomponent discovery settings:scope,match, andexclude. Absence means no subcomponent detectionConfig.processing.subcomponents.scope— optional enum (NESTED|PAGE) controlling where the transformer searches for subcomponentsConfig.processing.subcomponents.match— required array of{C}/{S}template patterns defining which assets are subcomponentsConfig.processing.subcomponents.exclude— optional array of{C}/{S}template patterns defining which matched assets to exclude
Changed
Typography.leadingTrim— corrected fromnumber | 'mixed' | TokenReferenceto'NONE' | 'CAP_HEIGHT' | 'mixed'per Figma APITypography.fontFamily— corrected fromstring | number | 'mixed'tostring | 'mixed' | TokenReference; removed impossiblenumber, addedTokenReferencefor variable-bound fontsTypography.fontStyle— corrected fromstring | number | 'mixed'tostring | 'mixed' | TokenReference; removed impossiblenumber, addedTokenReferencefor variable-bound fonts
Removed
Config.processing.subcomponentNamePattern— replaced byConfig.processing.subcomponents.matchConfig.include.subcomponents— subcomponent inclusion is now implied by the presence ofmatchpatterns
Migration
Config.processing.subcomponentNamePattern→Config.processing.subcomponents.match: wrap the single pattern string in a one-element arrayConfig.include.subcomponents→ removed: remove the field; subcomponents are included whenprocessing.subcomponentsis defined withmatchpatterns. Omitprocessing.subcomponentsentirely to disable detection
[0.14.0] - 2026-03-18
Introduces code-only props support with FigmaCodeOnlySource provenance metadata and a configurable container-layer naming pattern, plus a new NumberProp type for numeric property values. Adds DTCG-aligned $extensions for platform-specific metadata across all prop types, replacing the prior x-platform convention on BooleanProp. Expands SlotProp with minItems, maxItems, and anyOf constraints consolidatable via the new slotConstraints processing flag.
Added
FigmaCodeOnlySource— provenance metadata for props extracted from a Figma code-only container layer (kind,layer,instanceOf?)FigmaPropExtension.source— optionalFigmaCodeOnlySourceon the Figma extension, present only for code-only propsConfig.processing.codeOnlyPropsPattern— optional naming pattern for detecting the code-only props container layerConfig.processing.inferNumberProps— opt-in flag to inferNumberPropfrom TEXT code-only propsNumberProp— numeric property type for number-valued component propsAnyProp—NumberPropadded as a fifth union member (type: 'number')FigmaPropExtension— Figma-specific metadata for prop definitions (native prop type)PropExtensions— DTCG §5.2.3 platform-specific extensions container, keyed by reverse-domain notationBooleanProp.$extensions— optional platform metadata on boolean propsStringProp.$extensions— optional platform metadata on string propsEnumProp.$extensions— optional platform metadata on enum propsSlotProp.$extensions— optional platform metadata on slot propsSlotProp.minItems— minimum number of items the slot acceptsSlotProp.maxItems— maximum number of items the slot acceptsSlotProp.anyOf— component type names permitted in the slotConfig.processing.slotConstraints— opt-in flag to consolidate slot constraint code-only props into slot properties
Changed
BooleanProp—x-platformreplaced by$extensionsto align with DTCG convention used onTokenReference
[0.13.0] - 2026-03-13
Added
Metadata.schema.latest— optional stable URL pointing to the latest schema on main for discoveryMetadata.generator.license— optional resolved license state:statusandlevelnested inside generatorStyles.fillColor— glyph fill color for GLYPH element typeStringProp.examples— sample values demonstrating typical content for string propsElement.content— unified content for text strings and glyph namesSlotProp.nullable— optional flag indicating the slot prop accepts nullConditional— conditional binding withif/condition/then/elsefor derived valuesConditionExpression— declarative condition pairing anoperation(string) withargsConditionArgs— condition arguments:value(PropBinding) and optionalcompareTo
Changed
SlotProp.default— now optional; omitted when no meaningful default existsColorStyleValue— accepts bare hex strings (e.g.#666E74) matching the existingColorStyletypeBooleanProp,StringProp,EnumProp,SlotProp— allow$-prefixed metadata fields viapatternPropertiesMetadata.schema.url— clarified as versioned schema URL pinned to a git tagElementType—'icon'renamed to'glyph'to distinguish raw visual assets from composed Icon componentsConfig.processing.iconNamePattern→Config.processing.glyphNamePattern— glyph detection patternStringProp.default— now optional; useexamplesfor demo contentStringProp.default— widened tostring | nullfor nullable propsSlotProp.default— widened tostring | nullfor nullable slot propsBindingKey—'text'replaced by'content'
Removed
TextProp— merged intoStringPropIconProp— merged intoStringPropElement.text— useElement.contentinstead
Migration
TextProp/IconProp→StringProp: replace all type imports and references withStringProp; the shape is identicalElement.text→Element.content: read element content fromcontentinstead oftext; applies to both text strings and glyph namesElementType'icon'→'glyph': update all references to the'icon'literal in element type checksConfig.processing.iconNamePattern→Config.processing.glyphNamePattern: update config objects and any code referencing this field
[0.12.0] - 2026-03-05
Added
ElementTypeRef— reference to an external element type definition via$refURIConfig.processing.iconNamePattern— optional naming pattern for detecting icon content assetsSides— per-side composite object with logical directions:top,end,bottom,startCorners— per-corner composite object with logical directions:topStart,topEnd,bottomEnd,bottomStartStyles.padding— scalar when uniform,Sidesobject when per-side values differ
Changed
AnatomyElement.type— widened fromstringtostring | ElementTypeRefStyles.strokeWeight— acceptsStyle | Sidesinstead ofStyle; scalar when uniform,Sidesobject when per-side values differStyles.cornerRadius— acceptsStyle | Cornersinstead ofStyle; scalar when uniform,Cornersobject when per-corner values differ
Removed
Styles.paddingLeft— useStyles.paddingwithSides.startinsteadStyles.paddingRight— useStyles.paddingwithSides.endinsteadStyles.paddingTop— useStyles.paddingwithSides.topinsteadStyles.paddingBottom— useStyles.paddingwithSides.bottominsteadStyles.strokeTopWeight— useStyles.strokeWeightwithSides.topinsteadStyles.strokeBottomWeight— useStyles.strokeWeightwithSides.bottominsteadStyles.strokeLeftWeight— useStyles.strokeWeightwithSides.startinsteadStyles.strokeRightWeight— useStyles.strokeWeightwithSides.endinsteadStyles.topLeftRadius— useStyles.cornerRadiuswithCorners.topStartinsteadStyles.topRightRadius— useStyles.cornerRadiuswithCorners.topEndinsteadStyles.bottomLeftRadius— useStyles.cornerRadiuswithCorners.bottomStartinsteadStyles.bottomRightRadius— useStyles.cornerRadiuswithCorners.bottomEndinstead
Migration
Styles.paddingLeft/paddingRight/paddingTop/paddingBottom→Styles.padding: when all sides are equal, readpaddingas a number; when sides differ, readpaddingas aSidesobject withtop,end,bottom,startusing logical directions (start= left in LTR)Styles.strokeTopWeight/strokeBottomWeight/strokeLeftWeight/strokeRightWeight→Styles.strokeWeight: same scalar-or-Sidespattern; individual per-side stroke weights now live underSides.top,Sides.end,Sides.bottom,Sides.startStyles.topLeftRadius/topRightRadius/bottomLeftRadius/bottomRightRadius→Styles.cornerRadius: same scalar-or-Cornerspattern; per-corner values useCorners.topStart,Corners.topEnd,Corners.bottomEnd,Corners.bottomStart
[0.11.0] - 2026-03-04
Added
PropBinding— component-prop binding type; discriminated by$bindingkey; replacesReferenceValueTokenReference— unified DTCG-aligned token reference; replacesVariableStyleandFigmaStyle;$tokenand$typeare the complete platform-facing APITokenReference.$token— DTCG dot-separated token path, usable directly as a DTCG aliasTokenReference.$type— DTCG token type discriminator:color,dimension,string,number,boolean,shadow,gradient,typography,effectsTokenReference.$extensions["com.figma"]— optional Figma extraction metadata:id,name,collectionName,rawValueStyles.typography— composite typography; value isTokenReference,Typography, or omittedTypography— 13 optional inline text properties:fontSize,fontFamily,fontStyle,lineHeight,letterSpacing,textCase,textDecoration,paragraphIndent,paragraphSpacing,leadingTrim,listSpacing,hangingPunctuation,hangingListStyles.aspectRatio— aspect ratio constraint;AspectRatioValuepair ornullwhen unconstrainedAspectRatioValue— requiredx(numerator) andy(denominator) number fieldsAspectRatioStyle— type aliasAspectRatioValue | nullConfig.format.tokens— token reference serialization profile; five options:TOKEN(default,$token+$type),TOKEN_NAME(dot-delimited path string only),TOKEN_FIGMA_EXTENSIONS($token+$type+$extensions["com.figma"]),FIGMA_NAME(slash-delimited path string only),CUSTOM(same asTOKEN, signals custom post-processing)Metadata.license?— optional{ status: string; description: string }fieldstyles.textColor— style key for text colourstyles.cornerSmoothing— style key for corner smoothing (Figma squircle factor)styles.effects— style key replacingeffectStyleId;TokenReferenceor inlineEffectsShadow— fields:visible,inset?,offsetX,offsetY,blur,spread,colorBlur— fields:visible,radiusEffects— fields:shadows?,layerBlur?,backgroundBlur?GradientStop— fields:position(0–1),colorGradientCenter— fields:x,y(0–1)LinearGradient— discriminated bytype: 'LINEAR'; fields:angle,stopsRadialGradient— discriminated bytype: 'RADIAL'; fields:center,stopsAngularGradient— discriminated bytype: 'ANGULAR'; fields:center,stopsGradientValue— discriminated unionLinearGradient | RadialGradient | AngularGradient
Changed
Element.instanceOf— acceptsstring | PropBinding; bound form is{ $binding: "#/props/..." }Element.text— acceptsstring | PropBinding; bound form is{ $binding: "#/props/..." }Style—PropBindingreplacesReferenceValue;TokenReferencereplacesVariableStyleandFigmaStyleConfig.format—variables,simplifyVariables, andsimplifyStylesreplaced by singletokensfield; token output shape is now controlled entirely by thetokensprofileStyles.backgroundColor— narrowed toColorStyle; inline gradients representableStyles.textColor— narrowed toColorStyle; inline gradients representableStyles.strokes— narrowed toColorStyle; inline gradients representablestyles.fillsrenamed tostyles.backgroundColor
Removed
Config.format.variables— removed; useConfig.format.tokensinsteadConfig.format.simplifyVariables— removed; useConfig.format.tokensprofile insteadConfig.format.simplifyStyles— removed; useConfig.format.tokensprofile insteadReferenceValue— removed; usePropBindinginsteadisReferenceValue— removed; no replacement; prop-binding guards are upstream consumer responsibilityVariableStyle— removed; useTokenReferenceinsteadFigmaStyle— removed; useTokenReferenceinsteadStyles.fontSize— removed; usetypography.fontSizeinsteadStyles.fontFamily— removed; usetypography.fontFamilyinsteadStyles.fontStyle— removed; usetypography.fontStyleinsteadStyles.lineHeight— removed; usetypography.lineHeightinsteadStyles.letterSpacing— removed; usetypography.letterSpacinginsteadStyles.textCase— removed; usetypography.textCaseinsteadStyles.textDecoration— removed; usetypography.textDecorationinsteadStyles.paragraphIndent— removed; usetypography.paragraphIndentinsteadStyles.paragraphSpacing— removed; usetypography.paragraphSpacinginsteadStyles.leadingTrim— removed; usetypography.leadingTriminsteadStyles.listSpacing— removed; usetypography.listSpacinginsteadStyles.hangingPunctuation— removed; usetypography.hangingPunctuationinsteadStyles.hangingList— removed; usetypography.hangingListinsteadStyles.textStyleId— removed; usetypographywith aTokenReferenceinsteadstyles.effectStyleId— removed with no deprecation period; consumers must migrate tostyles.effects
Migration
config.format.variables/simplifyVariables/simplifyStyles→config.format.tokens: settokensto a profile —TOKEN(default) for$token+$type;TOKEN_NAMEfor dot-delimited string;TOKEN_FIGMA_EXTENSIONSfor full object with Figma metadata;FIGMA_NAMEfor slash-delimited string;CUSTOMfor custom post-processingElement.instanceOf→Element.instanceOf: replace{ $ref: "#/props/..." }with{ $binding: "#/props/..." }; update anyisReferenceValueguards to narrow against$bindingdirectlyElement.text→Element.text: same key rename from$refto$bindingStyles.visible→Styles.visible: same key rename from$refto$bindingVariableStyle→TokenReference: replace{ id, variableName, collectionName }with{ $token: "<Collection>.<name>", $type: "<type>", $extensions: { "com.figma": { id, name, collectionName } } }; read token path from$tokenand token category from$typeFigmaStyle→TokenReference: replace{ id, name }with{ $token: "<dot.path>", $type: "typography" | "effects" | ... }and move the Figma UUID to$extensions["com.figma"].idStyles.<typographyProperty>→Styles.typography.<property>: all 14 flat typography properties replaced with single compositetypographyfield; whentypographyis aTokenReference, read$tokenfor the style path; whentypographyis aTypographyobject, read individual fieldsfills→backgroundColor: any consumer readingcomponent.styles.fillsmust update tocomponent.styles.backgroundColoreffectStyleId→effects: any consumer readingstyles.effectStyleIdmust update tostyles.effects; wheneffectsis aTokenReference, read$tokenand$type; wheneffectsis anEffects, read fromshadows,layerBlur, orbackgroundBlurby role
[0.9.0] - 2026-02-12
Added
- DEFAULT_CONFIG - Default configuration constant for transformer setup
- Provides sensible defaults for all Config fields
- Ensures consistent behavior across CLI, MCP, and Plugin environments
- Co-located with Config type definition for easy maintenance
[0.10.0] - 2026-02-16
Added
- Runtime JS build - Compile TypeScript types to
dist/index.jsfor ESM consumers- Enables ESM runtime imports without loading
.tsfiles - Keeps type definitions in
types/for TypeScript tooling
- Enables ESM runtime imports without loading
[0.8.0] - 2026-02-10
Added
- TypeScript type definitions (
types/package): Complete type definitions for all schema entities- Core types: Component, Anatomy, Props, Variant, Metadata, Config, Styles, Element, Layout
- Property types with proper discriminators: BooleanProp, TextProp, IconProp, EnumProp, SlotProp
- Style types supporting all 59 properties with specific value types
- Reference value types for prop bindings and style references
Changed
- Component.metadata - Now optional to support components without full metadata
- Variant.layout - Changed from single LayoutNode to array of LayoutNode for proper hierarchical representation
- Props type values - TextProp, IconProp, and EnumProp now use
type: 'string'with discriminators (matching schema) - Style properties - Changed from generic
Record<string, Style>to specific Partial interface with all 59 style properties - VariableStyle - Now includes all properties from schema: id (required), rawValue, name, variableName, collectionName, collectionId (all optional)
- FigmaStyle - Simplified to match schema: id (required), name (optional)
Fixed
- SlotProp.default - Now accepts both
nullandstringtypes - TextProp/IconProp nullable - Made nullable field optional (was incorrectly required)
- Metadata.source - Added missing nodeType field with enum: COMPONENT | COMPONENT_SET | FRAME
- Metadata.config - Added complete Config definition with processing, format, and include options
- StyleKey type - Expanded from incomplete list to all 59 valid style properties matching schema
[0.7.0]
Changed
- Added distinct
anatomytypes for line, ellipse, star, polygon and rectange, all which were previously vector - Removed autodetecion of icon assets based on
isAssetplugin since this function is unavailable in the REST API
[0.6.0] - 2026-01-27
Added
-
Styles schema: New
styles.schema.jsonproviding complete validation for style properties- Defines 60+ style properties (fills, opacity, cornerRadius, fontSize, etc.)
- Type-specific validation for each property based on Style processor classes
- Supports all style value types: primitives, variables, Figma styles, and prop bindings
- Documents which value shapes depend on config.format settings (simplifyVariables, simplifyStyles)
-
Style value types: Precise type definitions for style property values
NumberStyleValue: number | VariableStyle | nullBooleanStyleValue: boolean | VariableStyle | nullBooleanBindableStyleValue: boolean | VariableStyle | ReferenceValue | null (forvisibleonly)ColorStyleValue: string | VariableStyle | FigmaStyle | nullStringStyleValue: string | VariableStyle | nullMixedNumberStyleValue: number | “mixed” | VariableStyle | nullMixedStringStyleValue: string | VariableStyle | nullStrokeStyleValue: number | “mixed” | VariableStyle | nullCornerStyleValue: number | “mixed” | VariableStyle | nullFontStyleValue: string | number | “mixed” | nullLineHeightStyleValue: string | number | VariableStyle | nullStyleIdValue: string | FigmaStyle | null
-
Styles reference documentation: New
reference/styles.yamlproviding human-readable documentation- Maps style properties to their applicable element types (COMPONENT, FRAME, TEXT, etc.)
- Documents style processing categories from RAW_STYLES_LOOKUP
- Lists all 60+ style properties with descriptions and value types
- Generated from StyleKeys.ts and RawStyles.ts constants
Changed
-
VariableStyle definition: Removed internal-only
rawValueproperty- Only includes properties emitted by Style.data(): id, name, variableName, collectionName, collectionId
rawValueis used internally for rendering but never serialized
-
FigmaStyle definition: Improved descriptions
- Documents simplified vs full object output based on config.format.simplifyStyles
-
Styles property: Now references
styles.schema.json#/definitions/Styles- Replaces generic
additionalProperties: Stylewith precise property-level validation - Each style property validates against its specific value type union
- Replaces generic
[0.5.0] - 2025-12-29
Added
-
Instance of attribute: Added optional
instanceOfproperty to element definitions- Indicates the component or component set name that an instance element references
- Only present for instance elements (Figma INSTANCE nodes)
- Shows ComponentSet name for variant instances, or Component name for standalone instances
- Also added to anatomy element definitions
-
Text property: Added optional
textproperty to element definitions- Contains the text content for text elements
- Can be a string value or a
ReferenceValuewhen bound to a text prop
-
Unified property bindings: Property bindings now appear as
$refvalues on their natural propertieschildrencan now be aReferenceValue(for slot content bindings)instanceOfcan now be aReferenceValue(for instance swap bindings)visiblestyle can now be aReferenceValue(for boolean prop bindings)textcan be aReferenceValue(for text prop bindings)- Added
ReferenceValuedefinition with$refJSON pointer to props
Removed
- propBindings section: Removed separate
propBindingsproperty from elements- Bindings are now represented directly on their target properties using
$ref
- Bindings are now represented directly on their target properties using
[0.4.0] - 2025-12-27
Added
- Layout structure: Added optional
layoutproperty to variant definitions that provides hierarchical element structure as a nested tree- Layout represents element nesting relationships in a recursive format
- Leaf nodes are strings (element names)
- Parent nodes are objects with element name as key and children array as value
- Example:
{ "parentElement": ["childA", { "childB": ["grandchild"] }] }
Changed
- Schema now supports both structure representations:
- New:
layoutat variant level (hierarchical tree) - Existing:
parentandchildrenproperties in each element (maintained for backward compatibility)
- New:
Deprecated
includedproperty has been removed from element definitions- Element inclusion is now determined by presence in the
layouttree - Elements not present in
layoutare considered excluded from that variant
- Element inclusion is now determined by presence in the
[0.3.0] - Previous release
[0.20.0] - 2026-06-07
Introduces the specs analyze command with two analyzers: props (cross-library prop governance aggregate) and styling (token usage dictionary, moved from transforms). Extends the css and contract transformers with subcomponent support, a configurable CSS rule pipeline, and the border-shift-inset-shadow rule. Refines css output: boolean props emit presence selectors and disabled guards wrap hover/active.
Added
-
specs analyzecommand — New command for on-demand analysis passes over component specs. Accepts one or more analyzer names as positional arguments (specs analyze props,specs analyze props styling). Output goes to_analysis/by default; override with--analysis <path>. No config key — analyzers are run on demand, not as part of the regular transform pipeline. -
propsanalyzer — Reads every component’sapi.yamland produces_analysis/props.yaml: a cross-library aggregate with six sections — summary counts, prop name frequency, enum discordance (same prop name, divergent value sets), boolean naming patterns, API surface per component, and a slot inventory. Designed as structured input for LLM-assisted API governance analysis. -
stylinganalyzer — Moved from the transform registry to the analyzer registry. Writes_analysis/styling.byComponent.jsonand_analysis/styling.byToken.json(previously written to_dictionary/). Invoke withspecs analyze styling. -
csstransformer: subcomponent support — For each entry invariants.yaml’ssubcomponentsmap, emits astyles.cssinside a dedicated subfolder named after the subcomponent key (e.g.dsActionList/group/styles.css). BEM selectors are scoped to the subcomponent’s own kebab-case class name (e.g..group,.group__text). Subcomponent subdirs are created automatically. -
contracttransformer: subcomponent support — For each entry inapi.yaml’ssubcomponentsmap, emits acontract.tsinside a dedicated subfolder named after the subcomponent key (e.g.dsActionList/group/contract.ts). Interface and enum type names are prefixed with both the component and subcomponent name (e.g.DsActionListGroupProps). Subcomponent subdirs are created automatically. -
csstransformer: configurable rule pipeline — Thecsstransformer now accepts aruleslist under its transformer options. Rules run as pre-passes on the structured variants data before CSS emission, enabling semantic rewrites that require cross-variant context. Configure per-component inspecs.config.yaml:transformers:- name: cssrules:- border-shift-inset-shadow -
csstransformer:border-shift-inset-shadowrule — Eliminates layout shift from border-width changes across variants. Detects elements whosestrokeWeightorstrokeschange in any variant (INSIDE strokes only), rewrites the default block to reserve space with a transparent border (border: Npx solid transparent), and rewrites variant stroke changes tobox-shadow: inset 0 0 0 Npx <color>. OUTSIDE and CENTER strokes are unaffected (they useoutline, which is already layout-safe).
Changed
-
csstransformer: presence selectors for boolean props — Data attribute selectors for variant props whose value is a JS booleantruenow emit[data-x](presence) instead of[data-x="true"](value). String-valued props (e.g.checked: "checked",checked: "indeterminate") continue to emit value selectors. Matches the TSX idiomdata-x={value || undefined}. -
csstransformer::not(:disabled)guard on hover/active — When thedisabledconcept is configured inprocessing.states,:hoverand:activeselectors (including compound variants like[data-checked="checked"]:hover) are automatically guarded with:not(:disabled):not([aria-disabled="true"])to prevent interaction styles from applying to disabled elements.
Removed
Dependency updates
- No upstream dependency changes since 0.19.0. Continues to reference
@directededges/specs-schema ^0.24.0and@directededges/specs-from-figma ^0.22.0.
[0.19.0] - 2026-06-05
Introduces the specs transform command and three transformers: contract (typed Props interface + Defaults), css (BEM stylesheet with token vars and concept-driven state selectors via processing.states), and styling (token usage dictionary). Specs generated by the CLI now correctly identify themselves in metadata.generator instead of reporting the Figma plugin. Upstream: specs-from-figma v0.22.0 adds RestFoundations.generator for caller identity and fixes INSTANCE width/height variable bindings; specs-schema v0.24.0 adds Config.transformers, workspace.schema.json, and Config.processing.states.
Added
-
specs transformcommand — Discovers component subfolders under the output directory (each must containapi.yaml) and runs one or more named transformers against every component. Transformer names resolve in priority order: positional arguments →config.transformers→ CLI default (contract). Accepts-o/--output,--config, and--verboseoptions. Closes DirectedEdges/specs#137 -
contracttransformer — Emitscontract.tsper component: a typedPropsinterface (enum union types, nullable types, slot props excluded) and aDefaultsconst (satisfies Props) for every prop with a declared default. Default transformer when none are specified. Closes DirectedEdges/specs#137 -
csstransformer — Emitsstyles.cssper component fromvariants.yaml. Default element styles become root/BEM-child selectors; variant configurations become[data-propName="value"]attribute selectors (camelCase prop names kebabized); multi-prop configurations produce compound selectors. Token references resolve tovar(--)based onconfig.format.tokens: path-derived kebab forTOKEN/TOKEN_NAME/FIGMA_NAME; verbatim forFIGMA_SYNTAX_WEBvars that already start with--;$cssVarfield or path derivation forCUSTOM. Closes DirectedEdges/specs#139 -
stylingtransformer — Emitsstyling.jsonper component: token/style usage grouped by category (variables,colorStyles,textStyles,effectStyles), each row recordingname,appliedAs, andappliedTo(anatomy element → occurrence count). After all components run,finalize()writes two aggregate files to_dictionary/:styling.byComponent.json(all components combined) andstyling.byToken.json(token-first index: token name → components/elements using it). Subcomponents appear as dot-separated keys. Closes DirectedEdges/specs#138 -
config.transformersblock ininitconfig template —specs initnow generates a commented-outtransformers:block showing all three transformers, ready to uncomment. -
processing.statessupport incssandcontracttransformers — Both transformers now readconfig.processing.states, a concept-keyed map that classifies Figma variant props as semantic states. Thecsstransformer emits real CSS pseudo-classes (:hover,:active,:focus-within) and ARIA attribute selectors ([aria-disabled="true"],[aria-invalid="true"]) for classified props instead ofdata-*attribute selectors. Thecontracttransformer omits browser-driven props from generated Props interfaces automatically — no additional config flag required.propandvaluebridge any naming gap between library conventions (e.g.isDisabled,pressed) and canonical concept names. Absence ofprocessing.statesis backward-compatible: all props continue to emit asdata-*selectors and appear in contracts unchanged.
Fixed
metadata.generatornow correctly identifies the CLI — Previously, specs generated by the CLI reported the Figma plugin name, version, and URL inmetadata.generator. The CLI now passes{ name: '@directededges/specs-cli', version, url }viaRestFoundations.generator, and the version is stamped from__SPECS_CLI_VERSION__injected at build time. Closes DirectedEdges/specs#133
Changed
Removed
Dependency updates
@directededges/specs-schema→^0.24.0— addsConfig.transformers(flat array, replacesconfig.transform.transformers),workspace.schema.jsonfor IDE validation ofspecs.config.yaml, andConfig.processing.states(Record<string, VariantStateEntry>) for concept-keyed variant state classification.@directededges/specs-from-figma→^0.22.0—RestFoundations.generatorlets the CLI supply its own identity tometadata.generator; fixesINSTANCEwidth/height variable bindings; colorcomponentsvalues rounded to 4dp.
[0.18.0] - 2026-05-29
Hardens license-key validation to hard-fail on transient errors and improves actionable error messages for stale or inaccessible Figma file keys.
Fixed
generateno longer silently produces free-tier output when a license key can’t be validated (#119) — if a key was supplied (via-l,SPECS_LICENSE_KEY, orANOVA_LICENSE_KEY) but the license check could not be completed — a transient proxy/network failure or a rate-limit (e.g. the upstream validator returning 429) — the run previously fell back to FREE and wrote free-tier specs under a valid paid key, with only an easily-missed status line.generatenow hard-fails on these transient states (network-error,error, and a futurerate-limited) with an actionable message (“retry in a few seconds, or remove the key for free-tier output”) and a retryable exit code (NETWORK_ERROR, orRATE_LIMITonce the validator distinguishes it). Definitive key rejections (invalid/removed/expired) still fall back to free-tier output as before.- Actionable
fetch/generateerror messages for stale or inaccessible Figma file keys (#125) — afetchthat hit a 404 previously surfaced onlyHTTP 404 while fetching <alias>.<kind>, with no hint that the cause was the file key pinned in config. The 404 case now explains that the configured key is likely stale or out of reach (file moved/deleted/recreated, or the token can’t open it) and points atsources.<alias>.keyin the config file. Auth failures are split: 401 reports a missing/invalid/expiredFIGMA_TOKENand where to set it (.env), while 403 reports valid-but-no-access and points at the same config key. Ingenerate, a missing source.file.jsonnow tips the user to runspecs fetchor checksources.<alias>.key.
Dependency updates
@directededges/specs-from-figma^0.20.0 → ^0.21.0 — SLOT elements now evaluate the same container-surface styles as frames: auto-layout config, strokes, padding, and corner-smoothing. Slot styling in generated specs is more complete.@directededges/specs-schema^0.22.0 — no version change.
[0.17.0] - 2026-05-23
Surfaces the new composition/examples model to CLI users: config loading now accepts include.defaultSlotContent and the processing.instanceExamples block, so generated specs can include slot-content examples and pre-configured instance examples (both Pro-gated). --split-concerns gains a third examples.yaml output for these examples, and a fix ensures they’re no longer silently dropped when splitting concerns.
Added
- Examples config (ADR-050) —
ConfigLoadernow accepts the newinclude.defaultSlotContentflag (added to the include allowlist) and validatesprocessing.instanceExamples(scope∈PAGE/FILEwithPAGEfallback;matchrequired;exclude/parentNamesarray-checked), passing them through to the engine.instanceExamplesoutput is driven by the presence ofprocessing.instanceExamples(noinclude.instanceExamplesflag), mirroringsubcomponents. Both example features are Pro-gated — silently omitted on the free tier. SurfacesslotContentExamples/instanceExamplesgeneration to CLI users.
Changed
--split-concernsnow emits a thirdexamples.yaml— slot-content and instance examples are written toexamples.yaml(per-concern mode) or<component>/examples.yaml(combined mode), alongsideapi.yamlandvariants.yaml. The file is emitted only when at least one component has examples, and components without examples are omitted from it.
Fixed
--split-concernsno longer drops examples —slotContentExamplesandinstanceExamples(component- and subcomponent-level) were assigned to neither theapinorvariantsconcern, so--split-concernssilently discarded them, leaving the$slotContentreferences indefault/variantsdangling. They are now routed intoexamples.yaml.
Removed
Dependency updates
@directededges/specs-schema^0.21.0 → ^0.22.0 — adds the composition and slot-content model (ADR-042, 046–052):Composition,SlotContent, and the universalSlotContentRef({ $slotContent }) pointer, plusComponent.slotContentExamplesandComponent.instanceExamples. Specs can now carry named slot-content examples and documented instance examples, andConfiggainsprocessing.instanceExamples(example detection) andinclude.defaultSlotContent(output gate).@directededges/specs-from-figma^0.19.0 → ^0.20.0 — detects and emits slot-content examples (de-duplicated across variants and slots) and instance examples (pre-configured usages of a component), both Pro-gated. REST page/file-scoped discovery now correctly finds candidates underCANVASnodes, and a plugin-only hashing bug that collapsed all slot fills into a single example is fixed.
[0.16.0] - 2026-05-22
Adds the platform code-syntax token profiles (FIGMA_SYNTAX_WEB/IOS/ANDROID) to config loading and templates, so specs can emit each Figma variable’s per-platform code syntax. Picks up upstream transformer improvements: conditional-visibility boolean prop exposure, detection of subcomponents nested inside sections/frames, and a fix for SLOT properties that previously leaked raw GUID objects into output.
Added
- Platform code-syntax token profiles (ADR-051, DirectedEdges/specs#103) —
format.tokensnow acceptsFIGMA_SYNTAX_WEB,FIGMA_SYNTAX_IOS, andFIGMA_SYNTAX_ANDROIDin config loading and templates, surfacing the platform code-syntax profiles to CLI users. The transformer (specs-from-figma) emits each variable’s FigmacodeSyntaxfor the selected platform, falling back to the standard token output when a platform has no code syntax defined.
Dependency updates
@directededges/specs-schema^0.20.0 → ^0.21.0 — adds theFIGMA_SYNTAX_WEB/IOS/ANDROIDformat.tokensprofiles.@directededges/specs-from-figma^0.18.0 → ^0.19.0 — serializes the new platform code-syntax profiles from FigmacodeSyntax; specs now expose a boolean-prop reference for conditional visibility bindings; subcomponents nested inside sections/frames are now detected undersubcomponents.scope: PAGE; SLOT properties no longer emit raw GUID objects into output.
[0.15.1] - 2026-05-20
Patch release fixing the scan → generate round-trip. generate now accepts the v2 (markdown-table) manifests that scan has emitted since 0.15.0, restoring the documented workflow.
Fixed
generatenow accepts v2 (table) manifests —generate’s source auto-detection only recognized the legacy v1 checkbox-list format (- [), so any manifest produced byspecs scanin 0.15.0 failed withError: Unrecognized source format, breaking the documentedscan && generateround-trip.generatenow detects v2 manifests via the**Scan format version:**header and dispatches toManifestParserV2, while continuing to support v1 manifests and raw JSON files. (#101)- Escaped pipes in component names round-trip —
scanescapes|as\|in manifest table cells;ManifestParserV2now unescapes them so names likeToggle | On/Offparse back to their literal form.
Dependency updates
- No upstream dependency changes since 0.15.0. Continues to reference
@directededges/specs-schema ^0.20.0and@directededges/specs-from-figma ^0.18.0.
[0.15.0] - 2026-05-15
scan now drives curation from Figma’s Ready for Dev signal and merges intelligently with prior manifests, preserving manual edits except where Figma’s devStatus has changed. Introduces a new v2 manifest format with automatic migration from v1.
Added
- Ready-for-Dev curation in
scan— Components withdevStatus: READY_FOR_DEVare now checked by default. Libraries with no devStatus signal anywhere fall back to the legacy heuristic. - Manifest merge on rescan — Re-running
scanpreserves manual checkbox edits unlessdevStatuschanged for a row (Figma wins on flips).--keep-checkslocks manual edits regardless of devStatus changes;--reset-checksignores the prior manifest entirely. - Glyphs section in scan manifest — When
config.processing.glyphNamePatternis set, top-level components matching the pattern are routed to a read-only## Glyphssection after## Components. Glyph rows have no checkboxes and are excluded fromspecs generate. The section is omitted when no matches are found. Pattern matching reuses the same{i}-placeholder semantics as the engine’s glyph detection. - v2 manifest format — New markdown-table format with a
Dev Statuscolumn,**Scan format version:**header, and**File last modified:**metadata. Legacy v1 (checkbox-list) manifests are detected and migrated automatically.
Changed
- Docs updated —
cli/commands/scan.md,cli/getting-started.md, andcli/workflows.mdreflect the new curation flow and manifest format.
Dependency updates
- No upstream dependency changes since 0.14.0. Continues to reference
@directededges/specs-schema ^0.20.0and@directededges/specs-from-figma ^0.18.0.
[0.14.0] - 2026-05-15
Dependency-only release that picks up specs-from-figma 0.18.0. No CLI source changes.
Dependency updates
- @directededges/specs-from-figma 0.18.0 — Restores ~170 invalid variants previously dropped by the empty-variant filter, accounting for the bulk of test-round 0009 parity diffs. Fixes a
figma.mixedSymbol crash on text nodes with mixed text styles.TEXTelements now emit explicit size styles (width,height,minWidth,minHeight,maxWidth,maxHeight);GLYPHelements now emit shadow/blur effects and aspect-ratio constraints alongside their fill color.
[0.13.1] - 2026-05-08
Patch fix for --split-concerns output shape.
Fixed
propsdefaults to{}instead of[]when a component has no props (#84) —splitComponentByConcernandextractApiFromSubcomponentswere filling missingpropswith an empty array, producing output that violated the schema (Propsis an object). Components with no props (e.g. a divider) now emitprops: {}. TheComponentApiDataandSubcomponentApiDatainterfaces are corrected to typepropsasRecord<string, any>.
[0.13.0] - 2026-05-06
Adds configurable color output format (config.format.color) with nine options from hex strings to structured DTCG Color objects. Fixes EISDIR crash when outputDirectory targets a directory, and corrects config template URLs.
Added
config.format.color— validates and normalizes the newColorFormatoption (HEX,HEXA,RGB,RGBA,HSLA,HSB,OKLCH,OKLAB,OBJECT); defaults toHEX. Config template updated with inline docs. (ADR 043)
Fixed
- EISDIR when outputDirectory is a directory in single-file mode (#34) — When
outputDirectorypointed to an existing directory (e.g. from a prior--split-componentsrun),specs generatewithout split flags crashed with EISDIR. Now appendslibrary.{format}as the default filename. - Config template URLs now point to the doc site (
directededges.github.io/specs/config/...) instead of 404ing GitHub raw paths (#43)
Dependency updates
- @directededges/specs-schema 0.20.0 — New
Config.format.coloroption typed asColorFormat.ColorValuerenamed toColorObject.ColorStyle,Shadow.color, andGradientStop.colorwidened to accept formatted color strings alongside structured objects and token references. - @directededges/specs-from-figma 0.17.0 — Ten bug fixes: INSTANCE_SWAP prop resolution now correctly resolves names, strips glyph patterns, and formats keys;
visible: trueno longer leaks into default variant output; auto-layout fallback elements emit correct width/height; glyph fill colors on nested instances now resolve to token references instead of raw hex. RAW colors emit structuredColorValueobjects per ADR-009.
[0.12.2] - 2026-04-28
Dependency update: picks up constraint-based layout positioning from specs-schema 0.19.0 and specs-from-figma 0.16.0. Also fixes glyph instanceOf leaking into variant output.
Dependency updates
- @directededges/specs-schema 0.19.0 — Positioning properties
x,y, andlayoutPositioningare replaced by constraint-based equivalents:position('AUTO' | 'ABSOLUTE'),top,bottom,start,end,centerHorizontalOffset, andcenterVerticalOffset. Offset values are pixel numbers for MIN/MAX/CENTER/STRETCH constraints and percentage strings (e.g."25%") for SCALE constraints. - @directededges/specs-from-figma 0.16.0 — Specs now emit constraint-based positioning instead of raw pixel coordinates. A new
postEvaluatepipeline step ensures positioning values are computed before variant differencing, so variant diffs correctly capture position changes. Fixes glyph elements incorrectly emittinginstanceOfin variant output, and SCALE constraints now emit both start and end percentages.
[0.12.1] - 2026-04-24
Dependency patch: picks up specs-from-figma 0.15.1 subcomponent indexer propagation fix.
Dependency updates
- @directededges/specs-from-figma 0.15.1 — Fixed subcomponent indexer propagation:
getMainComponentAsync()now correctly sets the indexer on parent COMPONENT_SET nodes, eliminating “[RestInstanceNode] No indexer set” warnings during subcomponent discovery.
[0.12.0] - 2026-04-24
Dependency update: picks up specs-schema 0.18.0 layout property renames and specs-from-figma 0.15.0 bi-axial spacing model.
Dependency updates
- @directededges/specs-schema 0.18.0 — Layout alignment properties renamed from Figma axis terminology to platform-neutral names:
primaryAxisAlignItems→mainAxisAlignment,counterAxisAlignItems→crossAxisAlignment,counterAxisAlignContent→wrapAlignment,layoutWrap→wrap.counterAxisSpacingconsolidated into a bi-axialitemSpacingmodel.layoutModenarrowed from generic style to strict'NONE' | 'HORIZONTAL' | 'VERTICAL'enum. - @directededges/specs-from-figma 0.15.0 — Wrap-enabled auto-layout frames now emit bi-axial
itemSpacingwithhorizontal/verticalfields instead of separateitemSpacing/counterAxisSpacingkeys. Alignment values remapped from Figma terminology (MIN→START,MAX→END).wrapAlignmentis only emitted whenwrap: true, stripped as dead otherwise.
[0.11.0] - 2026-04-16
Fix: config file split options (splitComponents, splitConcerns, useSubfolders) were ignored because Commander’s explicit false default shadowed the config values. Also makes generate and scan runnable with zero arguments using a default file path derived from config.
Added
generatenow runs without arguments. When no source argument is provided,generateresolves the manifest path to{dataDirectory}/{alias}.manifest.mdusing the source alias fromspecs.config.yaml(preferslibrary, otherwise the first source alias whosedataincludesfile). Runningspecs generatewith no flags now uses this default manifest plusoutputDirectoryfrom config — matching the zero-arg ergonomics ofscanandfetch. If no source alias hasdata: [file], an error suggests runningspecs scanfirst.scannow runs without arguments. The<file>positional argument is now optional. With one source configured inspecs.config.yaml(whosedataincludesfile),specs scanauto-resolves{dataDirectory}/{alias}.file.json. An explicit file path still works and takes precedence.scan --source <alias>flag. When two or more sources inspecs.config.yamlhavedata: [file, ...],scanfails loudly with the list of available aliases and requires--source <alias>to disambiguate. Passing both[file]and--sourceis rejected as a conflict. This stricter convention (vs.generate’s preference-based fallback) prevents silent behavior changes when a second source is added to config.
Fixed
- Config file split options now take effect. Commander boolean flags (
--split-components,--split-concerns,--use-subfolders) previously defaulted tofalse, which caused the nullish coalescing chain (options.flag ?? config.value ?? false) to short-circuit before consultingspecs.config.yaml. Removed the Commander defaults so the flags areundefinedwhen absent, allowing config values to flow through.
Docs
- Updated
docs/cli/(index, getting-started, claude-onboarding, examples, commands/generate, commands/scan, commands/index, commands/apply-custom-tokens) to document and demonstrate the zero-argspecs generateworkflow. Existingspecs generate components.mdexamples are preserved where they document explicit-argument option behavior.
Dependency updates
- No upstream package changes. Continues to target
@directededges/specs-schema ^0.17.0and@directededges/specs-from-figma ^0.14.2.
[0.10.2] - 2026-04-14
Dependency patch: picks up the DTCG-compliant token path separator fix from specs-from-figma.
Changed
- Bump
@directededges/specs-from-figmato ^0.14.2 — Picks up the fix for$tokenpath separators violating DTCG character restrictions.
Dependency updates
- @directededges/specs-from-figma 0.14.2 — Token references (
$tokenvalues) in TOKEN, TOKEN_NAME, TOKEN_FIGMA_EXTENSIONS, and CUSTOM profiles now use/as the path separator instead of.(period). The DTCG spec (§5.1.1) prohibits.in token and group names, and this aligns all profiles with the existing FIGMA_NAME behavior.
[0.10.1] - 2026-04-14
Dependency patch: picks up a fix from specs-from-figma for empty variant filtering in LAYERED mode.
Changed
- Bump
@directededges/specs-from-figmato ^0.14.1 — Picks up the fix for empty variant filtering in LAYERED mode, where variants with configuration but no element or layout differences were not being excluded whenemptyVariants: false.
Dependency updates
- @directededges/specs-from-figma 0.14.1 — Fixes the
emptyVariants: falsefilter in LAYERED mode. Previously, variants that had a configuration object but noelementsarray (e.g., variants with only layout differences removed) were incorrectly retained in output. The filter now correctly excludes variants that lack both element and layout differences from their layered baseline.
[0.10.0] - 2026-04-13
Renames audit → scan, sourceDirectory → dataDirectory, and the config file from .specs.config.yaml to specs.config.yaml. Adds a --data-dir flag and makes config format values case-insensitive. Config types now use ResolvedConfig for full default guarantees.
Added
--data-dirflag onfetch,scan, andgenerate— Override the configdataDirectoryper run from the command line. Onfetch,--outDiris retained as a deprecated alias.
Changed
- Config file renamed from
.specs.config.yamltospecs.config.yaml— The config file is no longer a hidden dotfile. Specs config is actively edited (Figma file keys, output directories, format options), so it belongs visible in the filesystem — consistent withvite.config.ts,tailwind.config.js, and other tools where config is a first-class part of the workflow. The JSON variant is also renamed from.specs.config.jsontospecs.config.json. The global config path (~/.specs/config.yaml) is unchanged since home-directory configs are conventionally hidden. auditcommand renamed toscan— Better describes the command’s purpose: scan a file and produce a manifest for component curation.specs auditstill works as a deprecated alias with a stderr warning.sourceDirectoryconfig field renamed todataDirectory— Aligns the field name with its default value (./data) and removes confusion with thesourcesconfig block.sourceDirectorystill works as a deprecated alias with a stderr warning.generatemanifest mode falls back toconfig.outputDirectory—--outputis no longer required whenoutputDirectoryis set in the config file, fixing the main flag/config inconsistency reported by users.- Config types now distinguish partial input from resolved output —
CLIConfig.configandConfigLoader.mergeConfiguseResolvedConfig(all defaults guaranteed) instead ofConfig(which now permits omitted fields) inittemplate annotates license-gated configs — The YAML config template generated byspecs initnow notes thatformat.tokensandinclude.invalidCombinationsrequire a license key to produce output.
Fixed
- Config format values are now case-insensitive — Lowercase values like
yaml,camel, orlayoutin config files were silently rejected by validation and reset to defaults (e.g.,output: yamlalways produced JSON). Allformat.*values are now normalized to uppercase before validation, matching how the CLI flag already works. - YAML options type annotation includes
CreateNodeOptions—FileWriter.YAML_OPTIONSwas typed withoutParseOptions & CreateNodeOptions, causing a compile error foraliasDuplicateObjects - Config merge test references legacy
subcomponentNamePattern— Updated to usesubcomponents(the current property shape) withtoEqualinstead oftoBe - Vitest mock type mismatch in
GenerateCommandtests — ReplacedReturnType<typeof vi.spyOn>withMockInstancefor vitest 3.x compatibility
Deprecated
sourceDirectoryconfig field — UsedataDirectoryinstead. Will be removed in a future release.specs auditcommand — Usespecs scaninstead. Will be removed in a future release.fetch --outDirflag — Use--data-dirinstead. Will be removed in a future release.
Dependency updates
- @directededges/specs-schema v0.17.0 — Introduces
ResolvedConfig(fully-resolved config with all defaults guaranteed) alongside the now-more-permissiveConfig(optional fields with defaults). Removes unusedVariant.nameandVariant.baselinefields from output. - @directededges/specs-from-figma v0.14.0 — Fixes
format.keysnot applying when config values are lowercase and not formattinginvalidVariantCombinationsdimension names. Internal types updated toResolvedConfig.
[0.9.0] - 2026-04-09
Adds complete config template generation, better Figma rate-limit error messages, and fixes for YAML-only-comments config crashes, output format not being respected, and stale command references.
Changed
initconfig template includes all settings — The YAML template generated byspecs initnow includes everyConfigfield andoutputsection option:codeOnlyPropsPattern,slotConstraints,inferNumberProps,emptyVariants,splitComponents,splitConcerns, anduseSubfolders. All new entries are commented out with their defaults. (#16)fetchrate-limit errors surface Figma response headers — When Figma returns HTTP 429, the error message now includes theRetry-Afterduration (formatted as seconds, minutes, hours, or days), seat tier (Viewer/CollaborDev/Full), plan tier, and a link to Figma’s rate limit documentation.
Fixed
- Config validation crashes on YAML sections with only comments — When
specs.config.yamlcontains sections likeinclude:with only commented-out fields, the YAML parser producesnullinstead of an empty object. The config validator’sdeepMergeandvalidateAndCorrectConfigmethods now guard against null values, preventingTypeError: Cannot convert undefined or null to objectcrashes. Null values from YAML parsing no longer overwrite defaults. (spec-demo) - File output ignores
config.format.output— Thegeneratecommand always wrote YAML files regardless of theformat.outputconfig setting. TheOutputFormattype,FileManifestextensions, and all writers now respect the configured format (JSON or YAML). TheDEFAULT_OUTPUT_CONFIG.defaultFormatis aligned with specs-schema’sDEFAULT_CONFIG.format.output(JSON). (#14) auditandinitterminal output references defunctbatchcommand — Post-run help text inauditandinittold users to runspecs batch …. Thebatchcommand was consolidated intogenerate; all references now point tospecs generate.audit --variablesflag not writing to manifest header — The-v/--variablesflag was parsed but never passed to the manifest generator. The**Variables:**metadata line is now written when the flag is provided, andManifestParserextracts it back into metadata. (#18)
Dependency updates
- @directededges/specs-from-figma v0.13.0 — Slot
anyOfvalues now respect the configured key format instead of using raw Figma component names. Width/height fallback warnings for rotated nodes are more informative when geometry data is absent.
[0.8.0] - 2026-04-07
Adds fetch UX improvements (animated spinner, elapsed time, --no-geometry), renames the config key from model to config, and fixes style reconciliation and large-file fetch crashes.
Added
fetchanimated spinner with elapsed time — Thefetchcommand now displays an animated braille spinner with a live elapsed-time counter while downloading each payload. The final success line includes the total duration per request (e.g.,✓ Downloaded: kds file (14s)). Non-TTY environments fall back to a static log line.fetch --no-geometryflag — Omits?geometry=pathsfrom Figma file requests, reducing payload size by roughly half. Vector path data (fillGeometry,strokeGeometry,size,relativeTransform) is excluded; width/height fall back toabsoluteBoundingBoxduring processing.auditdefault output path —-ois now optional. When omitted, the manifest writes to{sourceDirectory}/{alias}.manifest.mdusing the config’ssourceDirectoryand the input filename. Added--configflag for config file resolution.
Changed
- Config key rename:
model→config— The YAML keymodel:and internal propertyCLIConfig.modelare renamed toconfig:/CLIConfig.configto align with the upstreamConfigtype from @directededges/specs-schema. Updates source, tests, and all documentation.
Refactored
- Remove dead styles payload shapes from
loadFoundations— Removed two unused code paths: theall_stylessimplified format and thestylesobject-map format. Only the Figma REST API format (meta.styles) is retained. Updated JSDoc to describe the two actual data sources (file seed + styles endpoint). - Comment out false-defaulting include fields in config template —
invalidVariantsandinvalidCombinationsnow have schema-level defaults and no longer need explicit values in the generated config template.
Fixed
- Style
$customreconciliation inloadFoundations— Published Figma styles referenced by components use file-local IDs (e.g.,19108:2530) that differ from the styles endpoint’snode_id(e.g.,5115:6703). The sharedkeyhash now bridges these two sources, ensuring$customtoken objects from the styles endpoint are available when resolving style references during spec generation. generate -cuses component ID as output key — In file mode (generate <file> -c <id>), the component’s Figma node ID was used as the output key instead of its name. Now resolves the display name from the file’scomponentSetsorcomponentsmetadata.fetchwrites raw response directly to disk — Previously, fetch parsed the Figma response into JSON and re-serialized it with pretty-printing before writing. This causedInvalid string lengthcrashes on large files (400MB+). Now writes the raw response body straight to disk, eliminating unnecessary memory overhead.
Dependency updates
- @directededges/specs-from-figma v0.12.0 — When the REST API response lacks geometry data (e.g., when using
--no-geometry), width and height fall back toabsoluteBoundingBox. A console warning now surfaces when this fallback is used on a rotated node, where bounding box dimensions may be inflated.
[0.7.0] - 2026-04-05
Rebrands from anova-cli to specs-cli and publishes to npmjs.org. Updates all dependencies to published npm packages. Removes the deprecated variantNames config field per specs-schema v0.16.0.
Changed
- Package rename —
@directededges/anova-cli→@directededges/specs-cli - Config file names —
.anova.config.yaml→specs.config.yaml(and.jsonvariant) - Config search path —
~/.anova/config.yaml→~/.specs/config.yaml - Dependencies — switched from local
file:references to published npm packages:@directededges/specs-schema@^0.16.0,@directededges/specs-from-figma@^0.11.0 - Publishing target — npm registry (was GitHub Packages)
Removed
Config.include.variantNames— removed from config template, validation, and documentation per specs-schema v0.16.0 (ADR 034). AddedemptyVariantsto valid config include keys.
Dependency updates
- @directededges/specs-schema v0.16.0 — removes
variantNames, adds optionalemptyVariants, makesinvalidVariantsandinvalidCombinationsoptional with defaults - @directededges/specs-from-figma v0.11.0 — renames from
anova-transformer, adds pageId resolution, empty variant filtering, stroke align fix, license metadata in output
[0.6.0] - 2026-03-25
Adds a new applyCustomTokens CLI command for injecting custom token objects into fetched foundation data, enabling the CUSTOM token profile in the transformer. Implements anova v0.15.0 schema with restructured subcomponent configuration and corrected typography types, and anova-transformer v0.10.0 with expanded subcomponent discovery and custom token support.
Added
- License key support —
-l, --license <key>flag andANOVA_LICENSE_KEYenvironment variable for Pro features (token references, variable bindings, visibility bindings) - License status display — shows
License: PRO (active)or fallback reason (e.g.,License: FREE (invalid — key not recognized)) when a key is provided - Manifest mode for
generate—generatenow accepts markdown manifests directly, replacing the separatebatchcommand; processes all[x]marked components in a single pass applyCustomTokenscommand — New CLI command that injects$customtoken objects from a JSON mapping file into fetched variables and styles JSON files. Config-aware with-v/-soverride flags. Supports idempotent re-application, status summary, and validation of mapping entries.$custompassthrough inloadFoundations— Variables and styles map entries now preserve$customproperties when loading from JSON files, enabling the transformer to read custom token objects.
Changed
generatecommand unified — accepts both JSON files (with-cfor single component) and markdown manifests (for multiple components); all split flags (--split-components,--split-concerns,--use-subfolders) work in both modes- ManifestParser — fixed regex to correctly parse component entries from manifest markdown
- Documentation — updated all docs to reflect unified
generatecommand, removedbatchreferences, added license setup guide and Free vs. Pro feature comparison
Dependency updates
- @directededges/anova v0.15.0 — Subcomponent configuration moves from a flat
subcomponentNamePatternstring to a structuredsubcomponentsobject supporting multiple match patterns, an exclude list, and a page-level search scope. Subcomponent references in anatomy and element output now use$refpointers. Typography fieldsleadingTrim,fontFamily, andfontStyleare corrected to match actual Figma API values. - @directededges/anova-transformer v0.10.0 — Specs now support page-level subcomponent discovery (scanning beyond the component tree), multiple and excludable match patterns for subcomponent detection, and alphabetically ordered subcomponent output. Subcomponent references in
instanceOffields emit as$refpointers. The new CUSTOM token profile uses$customobjects from variables and styles as style property values. Fixes detection of subcomponents nested as instances and inconsistent slash spacing in Figma component names.
Removed
batchcommand — consolidated intogenerate; useanova generate manifest.mdinstead ofanova batch manifest.md
[0.5.0] - 2026-03-18
Supports code-only props extraction: the transformer now detects a configurable container layer, excludes it from anatomy, and surfaces its children as props with $extensions provenance metadata. Slot constraint code-only props are promoted to SlotProp.minItems, maxItems, and anyOf fields. Also introduces NumberProp inference from text-based code-only props when enabled.
Added
ConfigLoadervalidation for new processing config fields:codeOnlyPropsPattern(string),slotConstraints(boolean), andinferNumberProps(boolean)
Changed
- Compatible with
@directededges/anovav0.14.0 and@directededges/anova-transformerv0.9.0
Fixed
ConfigLoaderwas validatingiconNamePatterninstead ofglyphNamePattern, the field name used inConfig.processingsince v0.3.0. The validator now correctly referencesglyphNamePatternBatchCommandmanifest parser regex now handles component names containing parentheses
[0.4.0] - 2026-03-13
Changed
- Compatible with
@directededges/anovav0.13.0 and@directededges/anova-transformerv0.8.0
[0.3.0] - 2026-03-08
Added
iconNamePatternconfig support for detecting icon content assets
Changed
- Compatible with
@directededges/anovav0.12.0 and@directededges/anova-transformerv0.7.1
[0.2.0] - 2026-03-04
Changed
- Updated for
@directededges/anovav0.11.0 compatibility - Config defect fix
[0.1.0] - 2026-02-10
Added
- Initial CLI and MCP server for design system operations
Component.fromRestApiintegration with anova-transformer