Repository navigation
Releases: DirectedEdges/spectacular
Release list
@directededges/specs-schema v0.39.0
Breaking
Settings.spec.format— defaults toYAML, previouslyJSON(ADR-112)Settings.spec.keys— defaults toCAMEL, previouslySAFE(ADR-112)Settings.spec.color— defaults toHEXA, previouslyHEX(ADR-112)Settings.curation.preserveManualSelections— defaults totrue, previouslyfalse(ADR-112)Settings.spec.collapsePrimitiveWrapper— defaults totrue, previouslyfalse(ADR-112)
Added
Conventions.storybook— typed asStorybookConventions, an open concern-keyed map of feature settings loaded fromconventions/storybook.yaml; also onResolvedConventionsSettings.curation— which components of a library a run specs, defaulting to today's behaviourSettings.curation.defaultSelection—READY_FOR_DEVorALL; defaults toREADY_FOR_DEVSettings.curation.preserveManualSelections— hand edits outrank a changed Figma status; defaults to falseSettings.curation.includeDependencies— select what a component is built from; defaults to trueSpecsConventions.slots.default.match— naming patterns identifying a component's designated default slot, authored inconventions/specs.yaml. A spec convention, not a platform one: what it names is aSlotPropthe spec declares, so every platform reads the same answer (ADR-099)MetadataConventions.specs— the spec conventions a run was performed under, recorded beside the one platform entry. Required by a reader that cannot re-derive one from the spec it holds: a render resolving a nested instance's default slot does not hold that instance's own component spec (ADR-099)SlotProp.defaultSlot— durable marker set at generation time when a slot prop matchedspecs.slots.default.match, read by consumers with no dependency onConventions(ADR-099)PrimitiveEntry.match— layer-name prefixes selecting a promotion entry, ahead of style scoring; longest prefix wins (ADR-100)PrimitiveSlotContent— reservedanyOfvalues naming a primitive a slot accepts:$textand$glyph(ADR-102)SlotProp.anyOf— carriesPrimitiveSlotContentmarkers alongside component names, and only where the slot already names a component. Type and absence semantics unchanged (ADR-102)Element.contentStyles— typed asContentStyles; how primitive content filling an element is formatted, recorded and layered per variant exactly asstylesis (ADR-102)ContentStyles,TextContentStyles,GlyphContentStyles— a$text/$glyphmap over two closed property sets: text takes colour, typography, alignment, overflow and line clamp; a glyph takes fill colour, width and height (ADR-102)PlatformConventions.slotPrimitives— the library's slots carry the primitives they accept in their default content; defaults totrue, and required onResolvedPlatformConventions(ADR-102)ObjectProp— a prop whose value is an object of named scalar members (ADR-111)PropConfigurationValue— accepts a member-keyedObjectPropValueunder an object-prop key (ADR-111)InstanceExample.propConfigurations— acceptsObjectPropValuefor object props (ADR-111)platforms.<platform>.objectProps— opt-in folding of separator-named flat props into object props (ADR-111)
Changed
$extensions['com.figma'].name— documented rule corrected on props, anatomy elements andplatforms.<platform>.naming. The field was described as absent undernaming: NONE; NONE declares no source convention, which means nothing can be re-derived from a key, not that no name is kept. The field is recorded wherever formatting changed the name (ADR-066). Description only — no type or validation change
Migration
Settings.spec.format,Settings.spec.keys,Settings.spec.color,Settings.curation.preserveManualSelections,Settings.spec.collapsePrimitiveWrapper: a workspace that sets the member explicitly is unaffected. One that left it unset now resolves to the new value — set it explicitly to keep the former output.Settings.spec.keyscarries the largest cost: a spec regenerated withCAMELrenames every anatomy key, prop key and reference to one, and every name derived from a key. Treat it as a migration, or setkeys: SAFE.
ADRs
Accepted
@directededges/specs-cli v0.36.0
Breaking
- A workspace that leaves a setting unset now resolves it to a new value for
spec.format,spec.keys,spec.color,curation.preserveManualSelectionsandspec.collapsePrimitiveWrapper. Every workspacespecs initscaffolded already writes four of the five, so only a hand-writtensettings.yamlthat omitted them sees a change (ADR-112)
Added
-
Declaring
objectPropsin your figma conventions folds separator-named properties into one prop with named members —Margin.compactandMargin.expandedbecome a singlemargin(ADR-111).specs initscaffolds the block commented out. Dependency and prop analysis, visual manifests, version grading and generated CSS all read members rather than skipping them -
A slot whose default content holds a text layer or a glyph now says so (ADR-102). The
slotPrimitivesfigma convention is on unless you turn it off:$text/$glyphmarkers join the slot'sanyOfwherever it already names a component, and the formatting that primitive content takes is recorded on the slot element. SetslotPrimitives: falseinconfig/conventions/figma.yamlto stop;specs initscaffolds that off-switch commented out- The generated stylesheets carry that formatting, in both the React and custom-element sheets. Every rule is an ordinary declaration, never
!important— it styles a slot's unstyled content, so anything you write for the content you pass in wins by ordinary cascade - On a slot that accepts a component as well as a primitive, only the inheriting properties are emitted — colour and typography. A glyph's fill and dimensions are withheld there, because nothing in the emitted markup distinguishes a component's own root from a primitive fill, and a rule strong enough to reach one would reshape the other. A mixed slot is the one case where the scaffolds still inline the formatting, because these withheld rules are the only ones that could have carried it
- The generated stylesheets carry that formatting, in both the React and custom-element sheets. Every rule is an ordinary declaration, never
Changed
specs initwrites every setting with a comment saying what that setting decides, rather than a comment describing the value you would get by changing it. Three settings are scaffolded on while the schema defaults them off, and the file says which three and why
Fixed
-
An invalid
spec.collapsePrimitiveWrappernow warns and falls back to the default instead of silently turning the setting off -
specs analyze stylinglists tokens alphabetically in every set of the by-token report — variables, color styles, text styles and effect styles — instead of in the order the catalogue happened to reach them -
specs analyze stylingnow reads the token references a name-shapedspec.tokensemits. UnderTOKEN_NAMEandFIGMA_NAMEa reference serializes to the token's name alone, which the analysis skipped — so every styling report came out empty while the run reported success. Names are now matched against the fetched variables and styles, so these two profiles report whatTOKENreports -
specs analyze stylingsays so whenspec.tokensis a profile it cannot fully read.CUSTOMand the threeFIGMA_SYNTAX_*profiles serialize references into shapes the analysis cannot recognise without reproducing the generator, andFIGMA_SYNTAX_*falls back toTOKENfor tokens with no code syntax — so the reports were quietly partial rather than obviously empty. A warning now names the setting and what it costs. A name-shaped profile with no fetched data files warns too, and points atspecs fetch
Dependency updates
-
Your specs now record the formatting a slot's primitive content takes. Via
@directededges/specs-from-figma0.38.0, a slot whose default content holds a text layer or a glyph carries that formatting on the slot element and marks the primitive kinds it accepts — which is what the generated stylesheets in this release read. A slot whose only content is primitives names no accepted types at all, because listing them would declare a restriction your design never stated -
A prop whose Figma name was changed by key formatting keeps a record of the original spelling. Via
@directededges/specs-from-figma0.38.0, that record is now written undernaming: NONEas well, so a name is recoverable wherever formatting altered it. This matters more in this release than it used to: a separator-named property folded into an object prop puts a dot in the name, which no formatted key can reverse -
A default slot is only marked on a slot that children actually go into. Via
@directededges/specs-from-figma0.38.0, a well-named prop bound to a text, instance, glyph or vector element is refused as the default slot, and the run warns with the prop name and the rule -
The types behind all of it. Via
@directededges/specs-schema0.39.0:ObjectPropand its member-keyed values (ADR-111),ContentStylesfor a slot's primitive formatting (ADR-102), and the five settings whose defaults changed (ADR-112)
@directededges/specs-schema v0.38.0
Breaking
SpecApiDocument,SpecVariantsDocument,SpecExamplesDocument,SpecConcernDocumentrenamed toComponentApiDocument,ComponentVariantsDocument,ComponentExamplesDocument,ComponentConcernDocument(ADR-110)SpecApiSubcomponent,SpecVariantsSubcomponent,SpecExamplesSubcomponentrenamed toComponentApiSubcomponent,ComponentVariantsSubcomponent,ComponentExamplesSubcomponent(ADR-110)schema/concern.schema.jsonrenamed toschema/component-concern.schema.json; the./schema/concernpackage export becomes./schema/component-concern(ADR-110)
Added
CompositionApiDocument,CompositionVariantsDocument,CompositionExamplesDocument— type a composition'ssplitConcernsfiles, each exact to what it carries (ADR-109)CompositionConcernDocumentunions the three composition concern documents, reachable fromroot.schema.json(ADR-109)Composition.imagesandCompositionExamplesDocument.imagesregister image data referenced by a composition's elements, matchingComponent.images(ADR-109 amendment)
Migration
SpecApiDocument→ComponentApiDocument,SpecVariantsDocument→ComponentVariantsDocument,SpecExamplesDocument→ComponentExamplesDocument,SpecConcernDocument→ComponentConcernDocument: update imports to the new names — no shape change.SpecApiSubcomponent→ComponentApiSubcomponent,SpecVariantsSubcomponent→ComponentVariantsSubcomponent,SpecExamplesSubcomponent→ComponentExamplesSubcomponent: update imports to the new names — no shape change../schema/concern→./schema/component-concern: update any package export subpath resolution or direct reference toschema/concern.schema.json.
ADRs
Accepted
@directededges/specs-cli v0.35.0
Fixed
- A composition's split-concern files now carry only what a composition has.
api.yamldrops the meaninglesspropskey,variants.yamlholdselements/layoutinstead ofdefault/variants, andexamples.yamlusesslotContent(ADR-109)
Dependency updates
- A composition's generated output is correct end to end now. Via
@directededges/specs-from-figma0.37.0, the engine itself emits a composition's flat shape — noprops, nodefault— matching this release's file-writer fix, so neither layer reintroduces the old component-shaped output - Composition contract types got names and fields to match. Via
@directededges/specs-schema0.38.0: newCompositionApiDocument/CompositionVariantsDocument/CompositionExamplesDocumenttypes; the component-side equivalents renamed fromSpec*DocumenttoComponent*Document
@directededges/specs-schema v0.37.0
The schema's home is now the DirectedEdges/spectacular repository. The repo that was DirectedEdges/specs was renamed alongside the plugin's rebrand to Spectacular. Nothing in the types or the JSON Schema changes — this release updates the package's repository link so npm points at the right home, and every old link keeps resolving through GitHub's rename redirect.
Changed
- The package's repository link points at
DirectedEdges/spectacular, the renamed home of the schema. OldDirectedEdges/specslinks keep resolving via GitHub's redirect
@directededges/specs-schema v0.36.0
An annotation survives a collapsed layer knowing which layer it came from. When a wrapper is folded into the element it wraps, a role annotated on the wrapper used to be indistinguishable from one annotated on the element itself — so writing the spec back to Figma could put it on the wrong layer. A spec now records the two separately, and a round trip returns each annotation to the layer that carried it. Generated code is unaffected: it still reads only the element's own role.
Added
FigmaAnatomyElementExtension.childRolerecords a role annotated on the layer wrapper collapse folded into an element, sorolekeeps meaning the element itself was annotated and a round trip returns each annotation to its own layer. Onlyrolereaches emitted code (ADR-104)
@directededges/specs-cli v0.34.0
The CLI's documentation and repository links follow the repo's rename to DirectedEdges/spectacular. No command behaves differently — this release repoints the README that npm renders and the package's repository link at the renamed home, and picks up the companion releases of the schema and the Figma transform. Every old DirectedEdges/specs link keeps resolving through GitHub's rename redirect.
Changed
- The README npm renders and the package's repository link point at
DirectedEdges/spectacular, the renamed home of the CLI and schema. Old links keep resolving via GitHub's redirect
Dependency updates
- Specs now name their schema's renamed home. Via
@directededges/specs-from-figma0.36.0, every spec the CLI generates points its$schemaURL atDirectedEdges/spectacular; specs you already generated keep validating through GitHub's redirect @directededges/specs-schema0.37.0 carries the same rename in its package metadata — no change to the types or the JSON Schema
@directededges/specs-cli v0.33.0
Specs is now Spectacular, and the documentation lives at spectacular.systems. Figma shipped a feature of its own called Specs, so the product takes a new name — but nothing you type changes. The command is still specs, and every package keeps its name on npm.
Editing a convention regenerates your specs, not just what was built from them. A convention change used to re-run the analysis and both platform trees against specs written under the old convention, producing a pass that looked clean and emitted from stale input. Declaring a glyphs convention no longer needs a manual specs generate to take effect. Alongside that, a chain now names the steps your workspace has nothing for and why, so three skipped steps of six read as a workspace that has not got there yet rather than a broken run.
Changed
-
The product is Spectacular; the command is still
specs. Printed output and scaffolded config comments say Spectacular, and every documentation link now points atspectacular.systems. The oldspecsplugin.comlinks redirect, so nothing you have saved breaks. Package names are unchanged —@directededges/specs-cli,@directededges/specs-schemaand the rest keep their names permanently, because npm has no rename -
The bridge names the Figma plugin by role rather than by brand. "Enable the CLI Bridge in the Figma plugin to connect" reads the same whether the plugin is listed as Specs 2 or Spectacular, so the guidance stays correct while the Figma Community listing catches up
-
specs initandspecs storybook initprint the chain the docs teach, ending atspecs runrather than atspecs generate, and no longer offering a way of starting Storybook that the setup flow forbids. -
The role-adoption skills say which page answers which question, so a first sweep reads the two that carry the judgment and fetches precedence only when a role, a states classification and an action land on the same element. The apply step now names
conceptsInertInUseas the way to tell a role that could not emit from one that failed to land, and the proposal step's parts checklist covers the slider, progressbar and live-region families.
Fixed
-
Editing a convention now regenerates your specs, not just the reports and the code built from them.
scanandgeneratedid not watchconfig/, so a convention change re-ran the analysis and both platform trees against specs written under the old convention — a pass that looked clean and emitted from stale input. Declaring a glyphs convention needed a manualspecs generateto take effect; it no longer does. A convention edit re-derives the manifest from Figma's dev status, the same as new data arriving, sosettings.curation.preserveManualSelectionsapplies to it too. -
specs runandspecs buildname the steps your workspace has nothing for, and why, so a chain with three of its six steps reads as a workspace that has not got there yet rather than as a broken run:not in this workspace: storybook (no Storybook host scaffolded). -
analyze keysrecords inkeys.yamlwhether it measured anything. Without anamingconvention there is nothing to measure names against, so every name passes and zero divergence means "not checked" — indistinguishable in the file from a library that round-trips perfectly.measuredandnamingConventionnow say which, where a reader opening the committed file will see them. -
specs generateno longer warns that it overwrote files. Overwriting is what it does on every run after the first, and warning about the expected case taught readers to skim the warnings that matter.
@directededges/specs-schema v0.35.0
A library declares the slot its nested layouts fill through. SpecsConventions.slots.default.match names it, SlotProp.defaultSlot marks the prop that matched, and MetadataConventions.specs records the conventions a run used — so a reader holding only a spec can resolve a nested instance's default slot (ADR-099).
Conventions and settings grow two open surfaces. Conventions.storybook is a concern-keyed map a new Storybook concern can join without a schema change, and Settings.curation states which components of a library a run specs (ADR-093, ADR-098).
Glyph membership can be declared by structure rather than by name. PlatformConventions.glyphs.structure derives it from child shapes and fill, which makes glyphs.match optional (ADR-103).
Added
Conventions.storybookcarries a workspace's Storybook settings, typed asStorybookConventions— an open concern-keyed map loaded fromconventions/storybook.yaml, also onResolvedConventions. Adding a concern needs no change to the type or its schema (ADR-098)Settings.curationstates which components of a library a run specs, withdefaultSelection(READY_FOR_DEVorALL),preserveManualSelectionsfor whether hand edits outrank a changed Figma status, andincludeDependencies. Every default matches today's behaviour (ADR-093)SpecsConventions.slots.default.matchnames the patterns identifying a component's designated default slot, authored inconventions/specs.yaml. It is a spec convention, not a platform one — what it names is aSlotPropthe spec declares, so every platform reads the same answer (ADR-099)MetadataConventions.specsrecords the spec conventions a run was performed under, beside the one platform entry, for the reader that cannot re-derive them: a render resolving a nested instance's default slot does not hold that instance's own component spec (ADR-099)SlotProp.defaultSlotmarks a slot prop that matchedspecs.slots.default.match, set at generation time and read by consumers with no dependency onConventions(ADR-099)PrimitiveEntry.matchselects a promotion entry by layer-name prefix, ahead of style scoring; the longest prefix wins (ADR-100)PlatformConventions.glyphs.structurederives glyph membership from child structure and fill, typed asGlyphStructure, naming the glyph from the main component's name (ADR-103)
Changed
PlatformConventions.glyphs.matchbecomes optional, and a declaredglyphsblock now requires at least one ofmatchorstructure(ADR-103)
Migration
glyphs.match→glyphs.matchorglyphs.structure: existing declarations stay valid; code readingglyphs.matchmust handle its absence when onlystructureis declared
ADRs
Accepted
- ADR-093 — Curation is a settings concern
- ADR-094 — Subcomponent Pattern Matching Rules
- ADR-095 — Compositions Are Curated Like Components, From a Smaller Eligible Set
- ADR-096 — The
specs/Directory Names What Kind of Thing Each Folder Is - ADR-097 — A Composition Emits One Story in Its Own Nav Group, and Only Under Pro
- ADR-098 — Storybook Conventions —
config/conventions/storybook.yaml - ADR-099 —
slots.default.match— Naming the Default Slot, and Flattened Instance Nesting - ADR-100 —
PrimitiveEntry.match— Promoting a Layer by Its Name, Short-Circuiting Style Scoring - ADR-101 — The Workspace Stage Graph
- ADR-103 — Structural glyph membership —
glyphs.structureconvention
@directededges/specs-cli v0.32.0
A workspace is now a documented, tested library, and screens are first-class in it. Three themes:
- The CLI specs compositions — the frames your library marks ready for dev — so a screen or a pattern becomes a spec and working code the same way a component does.
specs storybookscaffolds and publishes a workspace Storybook, with generated pages for your foundations, components, analyses and visual test results.specs testing visualmeasures your emitted code against your designs, and against the renders you last accepted.
Breaking
-
specs/now names what each folder holds:components/,compositions/andanalysis/. A directory written before this release is read as legacy and says so once per run; re-runspecs generateto write the current layout. Nothing migrates out of_analysis/(ADR-096). -
--combine-as-libraryand--combine-concernscollapse a catalogue per kind, writingcomponents/andcompositions/each their own set of documents, so a composition and a component of the same name can no longer overwrite each other. -
-onames a directory, and a path ending in.yaml,.ymlor.jsonis refused unless both splits are off. A split run writes a folder per component, so a filename was never what you meant — it used to be honoured literally. -
specs scanmatches subcomponent patterns exactly, every character outside{C}and{S}included, spaces and slashes among them. A library spelling a separator two ways now declares a pattern for each (ADR-094). -
specs fetchwrites each file payload as a page-split<alias>.file/directory —root.jsonplus one JSON per Figma page — so a file of any size flows through the pipeline. Existing single-file payloads still read; only scripts opening<alias>.file.jsondirectly need changing.
Added
Compositions
-
specs scanlists compositions in a third manifest section, curated exactly as components are. A frame earns a row by being marked ready for dev in Figma, and inside another marked frame the outermost wins, so one arrangement is never specced twice (ADR-095). -
specs generatewrites checked composition specs tospecs/compositions/<key>/, carryingmetadata.source.nodeType: FRAMEas the marker for what the spec describes.-cnarrows to one by name, id or key, as it does for a component. -
specs reactandspecs webcomponentsemit a composition as one story in aCompositionsnav group, at<tree>/src/compositions/<Name>/, so screens browse separately from the component list. Compositions are Pro; the free tier skips them with a note (ADR-097). -
--compositionsand--no-compositionsscope a run to one kind, onspecs generate,specs reactandspecs webcomponents, so iterating on component output no longer pays for the slowest specs in the catalogue. A scoped run never prunes the kind it excluded. -
Compositions reach
specs analyzeandspecs version.dependenciesandstylingread them, so which components a screen is built from shows in the blast radius;propsandkeysstay component-only. Every change to a composition is a patch, because it declares no contract.
Storybook
-
specs storybookscaffolds and publishes a workspace Storybook.initwrites the host once and prints the one install you run yourself; a barespecs storybookrewrites the generated content per concern —overview,foundations,components,analysis,versions. The CLI ships no browser. -
specs storybook devis the one process a designer runs. It publishes, re-emits each platform as specs change, serves Storybook, republishes when config or assets move, and recovers an index poisoned by a half-saved story file. One Ctrl-C stops it all. -
Generated pages cover your foundations, components, analyses and visual test results. Colour and typography read your library's own variables and styles, each component gets one entry with React · Web Components · Specs tabs, and a section appears only once its data exists.
-
Your pages win. Copy any generated page to the same path under
storybook/content-overrides/and it is used instead, live and in both directions. No command ever writes there, and publish reports which overrides are in effect. -
A workspace can declare the port its Storybook serves on, as
host.portinconventions/storybook.yaml, so each workspace keeps a localhost you can bookmark. A--portflag outranks it, then the scaffolded npm script, then6006(ADR-098). -
conventions/storybook.yamldeclares how a page presents.modes.collectionsturns each variable collection into a toolbar dropdown,color.collectionsnarrows and orders which collections render, andcolor.rowGroupandcolor.groupLeavesdecide what shares a row. An unknown concern or wrong value shape warns by name (ADR-098).
Visual testing
-
specs testing visualmeasures your emitted Storybook against two baselines. Fidelity mode diffs each variant against a Figma export of the same node; regression mode diffs against the renders you last accepted. A composition is diffed as one full-page pair and reported as advisory, never deciding the run's exit status. -
Each stage is its own subcommand, and each platform is measured separately.
manifestandstatusneed neither browser nor network,baselinecaptures Figma exports and only ever runs when named,shootscreenshots,diffre-scores in seconds, andacceptpromotes reviewed renders.--target <react|webcomponents>selects the platform and keys every artifact. -
specs testing visual initscaffolds the browser dependencies you install, and seedsvisual-ignore.yaml. Playwright, pixelmatch and pngjs are declared intesting/visual/package.jsonfor you to install. The ignore file is seeded only when absent, and--forcenever touches it, because every line in it is a decision. -
specs testing visual baselinescopes itself to what changed. Each capture records a fingerprint of the Figma node it photographed, sostatusreports each baseline as current, changed or missing, and a recapture after a refetch covers only the designs that moved.
The workspace chain
-
specs buildandspecs runperform your whole workspace in order — data, manifest, specs, each platform's code, then Storybook.buildruns the chain once and exits non-zero at the first failure;runruns it once and then watches, redoing only what a change affects. Experimental (ADR-101). -
--renderadds rendering back into Figma as the last step of either command. Off by default, because it is the only step that writes somewhere other than disk and it needs a running bridge.specs run --renderoverwrites the existing same-titled page;specs build --rendertreats a title collision as an error. -
Having nowhere to render to is reported and skipped, not treated as a failure. No bridge running, no Figma file connected, or several with nothing naming one — each says so in a line and the chain carries on. Both commands take
--file <fileKey>rather than prompting.
Conventions and settings
-
conventions/specs.yamlcan name your library's default slot, and a nested hierarchy then reads as one tree. Declareslots.default.matchand content you place in a nested instance's default slot is captured as that instance's children, in the spec's own anatomy, instead of a$slotContentreference. A non-default slot is unchanged (ADR-099). -
Glyphs are detected by structure, not only by name. The
glyphsconvention gains astructureform — child node shapes plus an optional shared fill — so a library whose icons are plainly named gets glyph detection without renames. A property-bearing component is never structurally claimed (ADR-103). -
settings.curationdecides which components a scan selects, withdefaultSelection,preserveManualSelectionsfor whether your manifest edits outrank a changed Figma dev status, andincludeDependencies.--include-alland--keep-checksoverride the first two for one run; every default matches previous behaviour (ADR-093). -
matchinconventions/figma.primitives.yamlnames the layers that promote to a component, for a layout family whose members share one set of styles and so cannot be told apart by them. An entry declaring none is chosen by its styles, as before (ADR-100). -
settings.yamlrejects what the schema does not declare, out loud. An unknown top-level key warns by name and is ignored; an invalid enum value warns with the valid list and uses the default. Nothing is silently transformed on a governed surface.
Analysis
-
specs analyzehas a command reference, and runs every analyzer when you name none. The reference covers its arguments, options and exit codes, including an unknown analyzer name as the warning that skips rather than a failure. -
The props analysis flags a name that means two things.
typeDiscordancenames which components hold which type for a prop they disagree on, andenumDiscordancenow also reports a name some components close with anenumwhile others leave open, markedopenAndClosed.
Roles
-
specs analyze rolesreports which anatomy elements declare a role and which are bare, with the concepts your library uses, the ones it has never reached for, and the concepts in use that nothing emits for yet. An unrecognized value is reported rather than refused, because a role is an open string and a near-miss is almost always a typo. -
specs skills installships two skills that carry role adoption across a library. One proposes a role for every unannotated element as a table you mark up; the other adds the approved ones to your Figma file a page at a time, then regenerates so you can see what landed. Nothing reaches Figma before you have checked the table. -
**The bridge reads a component's structure a...