Repository navigation
Releases: sandroleks/spec-layer
Release list
Spec Layer v7.0.0
Plugin 7.0.0 is a major release because every existing document asks to
be updated once. EXTRACTOR_VERSION moves from '3' to '4' for Figma
slots, so a component doc built by 6.0.0 or earlier reads "Rebuild
needed". Annotate in Dev Mode, motion variables and the Motion section,
Figma slots, placeholders for empty writing sections and the Library's
incremental source check all ship here, with Foundation Context v5 schema
5.2.0 and Component Context v5 schema 5.4.0. It is the first plugin built
and attested by release-plugin.yml. The proxy it needs deploys from
main ahead of it, and spec-layer@0.13.0 is latest on npm, which a
pulled Markdown page needs before it says what the canvas says about a
slot.
The CLI portion of this section ships as 0.12.0, a minor release because
pull, status and list gain --json, and because --help and
--version now print to stdout and exit 0. It is also the first CLI that can
pull a library with an EASING variable: 0.11.0 does not know the easing
token type, refuses the Foundation as failing schema validation, and asks for
a republish that cannot help. A plugin build that exports easing tokens must
not reach the listing before 0.12.0 is latest on npm. A repository on 0.11.0
re-projects once on its first pull with 0.12.0 through the
manifest.cliVersion check. 0.12.0 is the first version published by the
release workflow, so it is the first to carry npm provenance.
The CLI changes merged after 0.12.0 ship as 0.13.0, a minor release: pulled
Markdown prints each Figma slot's facts (preferred components, "only
preferred", the limits and default content) where 0.12.0 lists a slot with
its type alone, and spec-layer skill says what a slot asks of code (Figma
slots, under Added). A plugin build that documents slots should not reach the
listing before 0.13.0 is latest on npm, so a pulled Markdown page says what
the canvas says.
Added
-
Annotate in Dev Mode. A Library row's menu and Settings, Export can now write a
component doc back into Figma's own fields, so Dev Mode and the REST API
show what the canvas shows. The Usage text the doc shows (Overview, When to
use, When not to use) goes into the component description with one
provenance line, and is cleared again once the doc no longer has it. When a
file link is saved in Settings, the documentation link opens the doc's
Section, follows it when an Update rebuilds the Section, and is cleared when
the doc is deleted. When the doc draws its Anatomy legend, each top-level
part gets a native annotation numbered as the legend numbers it, with
padding, spacing, radius and type pinned as live properties, so no number is
ever typed; an annotation on a part the legend no longer numbers is removed,
and a doc whose parts changed since it was built places no annotation until
it is updated. Every run you start asks first and names AI text nobody has
edited: the confirmation says what it writes in one line and lists each
caution as its own point, naming which kinds of value (description, link,
annotations) a change made in Figma keeps. A description, link or annotation set, changed or removed in Figma
is kept, and replacing it is a separate, labelled step that also names AI
text: it is offered once, when a run finds a value newly changed in
Figma, and again from a component's Library row; a run over every
component names what it kept in a toast instead. A part shown by a
property is labelled "Shown when … is true", as the legend says. A record
of what was written lives in the component's plugin data,
and the serializer reads a description or link that matches it as absent,
so an annotated doc never reports drift against its own words and the AI
prompt is never fed the sync's own text. A description a person edits in
Figma becomes theirs and reads as source like any other, without the
provenance line. A record from a newer build is left untouched. Nothing it
writes enters a hash or an artifact, andEXTRACTOR_VERSIONis unchanged.
An optional switch annotates again after an Update, only for components
already annotated by hand, never writes text that was edited in Figma or
written with AI, and names a component it held back once, not on every
Update. Whether Figma's MCP server or Make reads the descriptions is not
verified. -
Motion variables in the Foundation. Figma's
TIMINGandEASING
variable types (Plugin API update 133) are read asdurationtokens in
seconds and a neweasingtoken type (a named preset, a cubic bezier, a
spring bounce, or hold). Foundation rows read "0.3 s" or the easing name,
and a cubic bezier draws its curve. Foundation Context v5 moves to schema
5.2.0 (foundation-5.2.0.json); the envelope sits outside the content
hash, so no artifact identity moves for a file without these variables.
DTCG projectsdurationand a cubic beziercubicBezier; a preset,
spring or hold is omitted and named inreport.json, never expanded to
numbers the API did not return. Before this, aTIMINGvariable exported
as a unitlessnumberand anEASINGvariable read "Not resolved".
@figma/plugin-typingsmoves to 1.140.0. -
Motion section for variant transitions. Prototype interactions that
change a component set's variant (hover, press, click, delay) appear as a
Motion section after States, with From, To, Trigger, Transition, Duration
and Easing, and asmotion.transitionsin the YAML brief, Component
Context v5 (since schema 5.3.0), Markdown and the AI
profile. ACHANGE_TOoutside the set is reported as
transition-target-outside-set. The section is marked Beta in the
section picker; existing documents gain it when it is ticked on. The
motionkey exists only when a component has transitions, so every
document without them keeps a byte-identical hash: the content hash of an
affected document moves on its own, which is what drift detection
reports. (The release still movesEXTRACTOR_VERSIONto'4', for Figma
slots, below.) An easing on a prototype
transition that Figma stores as a physical spring (mass, stiffness,
damping) is reported as not supported, never converted to a bounce,
because the conversion is Figma's own and not available to a plugin. -
Figma slots. A component's slot properties and slot layers are read
and documented. Each slot is a property underapi.slotswith
type: slot, and Figma's own facts about it: the designer's
description, the preferred components (named from the instances inside
the component, then the file's own components, and never over the
network, so a drift check does not depend on library access), "only
preferred", the minimum and maximum, "display empty
by default" and "stretch child on insert". Each slot layer is one anatomy
part carryingslot(its property) anddefault_content, what it holds
in the default variant. Default content is an example, so it is no longer
documented as anatomy parts, related components, token bindings or
unbound values; a slot inside a nested instance keeps its content, which
is fixed content of the component. On canvas, Properties reads Type
Slot with the preferred names, "only these", the limits and the
default content, and the Anatomy legend marks each slot. A preferred key
the document cannot name is counted, never named, and reported as
unresolved-slot-content; a slot linked to no property is reported as
unlinked-slot; a property type the extractor does not know is left out
and reported asunsupported-property-type. The Library's change list
itemizes each slot change. Component Context v5 schema 5.4.0
(component-5.4.0.json) gainsanatomy_node.slot,
anatomy_node.default_contentand a typedapi.slotsentry. 5.3.0,
the motion contract above, was published on spec-layer.com before
slots and stays there unchanged; no release writes it.
EXTRACTOR_VERSIONmoves to'4', so every document built by 6.0.0
reads Rebuild needed once. Annotate in Dev Mode never
pins a corner radius on a slot, which Figma refuses. Eachapi.slotsslot
entry also carriesdefault_content, so the Markdown page prints the same
Values and Default text as the canvas, adds a Description column only for
a slot's own description, and says in Anatomy what each slot holds. A
published library versions a slot by direction: narrowing what may go in
it (only preferred turned on, a raised minimum, a lowered or new maximum, a
preferred component removed while only preferred is on) is major,
widening is minor, and its default content and display settings are
patch; each is a property change in the history, named by its facet, so a
6.0.0 plugin's history pane still reads it. The AI writing prompt gets
each slot's facts in the same words. An instance swap property now reads
"Instance swap" in Markdown rather than "InstanceSwap". The agent guide
spec-layer skillwrites, and the plugin's skill download, say what a
slot asks of code: build it as the framework's children or a named slot,
accept only the preferred components when only preferred is on, keep to
the limits, and treat default content as an example, never as fixed
markup. The guide uses the YAML keys or the Markdown page's words,
whichever format the components were written in.
Changed
- CI's dependency review exempts
@figma/plugin-typingsfrom the licence
allow-list. The package ships a verbatim MIT licence but declares it as the
non-SPDX string "MIT License", which GitHub reports as
LicenseRef-bad-mit-license, so every version bump failed the gate. The
terms are unchanged; only the metadata is exempted. - The component screen's main button says Update docs (and
**Updating d...
Spec Layer v6.0.0
Plugin 6.0.0 ships everything merged since 5.1.0 (#59 to #89). It pairs with spec-layer@0.11.0 on npm: pull with npx spec-layer@latest.
This is a major release because every existing document asks to be updated once. EXTRACTOR_VERSION moves from '2' to '3', so a component doc built by 5.1.0 or earlier reads "Rebuild needed", and a foundation doc built by 5.1.0 reads "Update available" with one "New layout" item. The proxy still accepts the prose requests 5.1.0 sends, so a plugin that has not updated keeps generating.
Highlights
Documents
- Component documents read as authored pages. The component's own Figma description becomes the overview, a Properties table replaces the Configuration section, and empty sections are left out, with the result saying so.
- AI writing is one structured call per component. When to use and Do and don't answer different questions, and rebuilding a doc written by an earlier version fills in only what is missing.
- Foundation docs draw number tokens to scale, show text styles as specimens, carry reference names, and get one overview per collection. Effect styles are a new foundation source, with their own row, document and Copy for AI target.
- Stale docs are explained once, in a banner, and "Update all docs" now rebuilds docs made by an earlier version.
Copy for AI and outputs
- Components can copy, snapshot and pull as Markdown as well as YAML. Choose the format in Settings, or use
spec-layer pull --component-format md. - The DTCG output describes itself: a census of what each file holds, a hash of the options that produced it, and a sidecar recording each value's transform and what each alias resolves to. A reference is written only into a file where it resolves, and a token that is also a group is omitted and reported rather than dropped silently.
- Foundation Context artifacts are stamped with schema 5.1.1, which pins the envelope, diagnostics and statistics.
spec-layer.com/schemasserves it.
Publishing
- Published libraries have a semantic version. Every changed publish is diffed over Figma facts, and the Publish screen proposes the minimum bump, which you can raise but never lower. Token bindings are diffed per variant, and a Version history pane lists what changed.
- Every doc frame carries a publish pill. Publish refuses a file with nothing in it, and a library can be downloaded as a self-contained agent skill.
Reliability
- The Library always replies to a refresh, says so when it could not read part of the file, and only one canvas build runs at a time. Publish and Library show only what they actually know.
- Proxy hardening: one origin, request bodies capped as they stream in, atomic publishes, and a response cache per key.
The full list, including the CLI 0.9.0 to 0.11.0 changes, is in the changelog.
Known limits
- The manual Figma test matrix in
packages/plugin/TESTING.mdhas no recorded run for this release. Rows 16 to 36 cover the new documents and none has been run, so unit tests are the only evidence for how they draw. - The real size and paste behaviour of the DTCG clipboard has not been measured in Figma.
- The first publish of an existing library from 6.0.0 records one patch version with no listed changes, because the library bundle hashes the new 5.1.1 envelope.
- An artifact from 5.1.0 still says schema 5.1.0 and validates against the 5.1.0 download, not
v5.json.spec-layer pullaccepts both. - The setup command the plugin copies still passes the pull key on the command line. CLI 0.11.0 can read it from stdin with
--key -, but the plugin does not use that yet.
Spec Layer v5.1.0
Published to the Figma Community listing on 2026-09-10. Tagged retroactively on 2026-09-16, so this release records what already shipped.
Pull the matching CLI with npx spec-layer@latest.
Fixed
- Token bindings on a hidden part are no longer dropped. Token extraction
pruned every invisible subtree, so a layer a boolean property reveals had its
bindings discarded before any consumer could ask for them: with "Document
hidden elements" on, the revealed icons were drawn but the Tokens section
showed nothing for them. Extraction now prunes only what no consumer could
ever reveal, marks a rule from a revealed subtree withshownBy, and filters
throughtokensFor, the token counterpart ofanatomyFor. Anatomy and
token extraction share one definition of "hidden but documentable"
(hiddenPartRules). With the option off, every rendered table and every
drift hash is byte-identical to before. The exported YAML brief and Component
Context v5 artifact still omit these rules, because a v5 token rule has no
field in which to say it applies only once a boolean property is on;
carryingshown_byonto token rules needs schema 5.3.0. One export does
change: the brief'sunboundlist weighs gaps against the unfiltered rules,
so a hidden layer whose fill is bound is no longer reported as having no
token binding. - Anatomy callouts read in order. Depth-0 parts are now listed in real
layer order, hidden ones interleaved where they sit, instead of visible-first;
a Chip's pins read 1, 2, 3 left to right rather than 2, 1, 4. Naming is
decided separately from ordering, on the visible-first basis, so a hidden
sibling cannot take a visible part's name and a doc with the option off keeps
the anatomy, names andspecContentHashit already had. - Anatomy callout numbers are hierarchical. A nested legend row is now
"2.1" rather than consuming the next whole number, so the pinned depth-0 set
counts 1, 2, 3 with no holes. The number badge is a pill with a floor of its
old diameter, so a single digit still draws the same circle. - "Document hidden elements" moved out of the Anatomy row. It is a switch
above "Sections to include", because the flag feeds Anatomy, States,
Variants, the per-variant token pane and Measurements alike. It no longer
disappears, or silently disarms, when Anatomy is unchecked. Its caption is
gone from the page: it was two sentences set in an uppercase micro-caps
style built for two-word labels, so the explanation moved to the same
info-icon tooltip the AI writing switch uses. The two switches now share one
set of CSS rules, which is what the label's weight disagreeing with "AI
writing" was about: the second row's own copy of those rules set the font on
the wrapper, so the innerstrongfell back to the browser'sbolder. - Four quadratic regexes are now linear scans.
cleanPartName,
stateBaseName, and the prose prompt's dash and code-fence rules each ran
in time quadratic in their input, so a long run of hashes, parentheses, or
spaces could stall the plugin's UI thread: 40,000 trailing hashes in a layer
name measured 6754 ms. The two prose rules read model output arriving
through the proxy, the one input here that nobody in this repository
controls, where an unclosed code fence was enough to hang the UI. Output is
unchanged for every input, andpackages/extractor/test/redos.test.tskeeps
the old regexes as executable oracles to prove it, which is load-bearing
becausecleanPartNameandstateBaseNamefeed canvas drift hashes. Closes
CodeQL alerts 1 to 4 (js/polynomial-redos).
Added
- Hidden elements in component docs. A layer that a boolean component
property hides by default (a Chip'sIcon left) is now an anatomy part.
The YAML brief and the Component Context v5 artifact always include such
parts, each carryingshown_by, the property that reveals it. Component
Context schema5.2.0addsshown_byas an optional string on anatomy
nodes; the schema URL is unchanged. The envelope now requires
schema_version5.2.0, so once the site redeploys, an artifact still
stamped5.1.0no longer validates against the published schema. This
moves the semantic content hash of
every component artifact that has such parts, and nothing else: hidden
layers with no property binding stay excluded, canvas drift hashes are
unchanged, andEXTRACTOR_VERSIONis unchanged. - "Document hidden elements", a per-component switch on the component
screen, shown only when the component has a layer that a boolean property
hides by default. On, the doc lists those parts in Anatomy with
"Shown when is true", sets every boolean property to true on the
instances placed in Anatomy, States, Variants, and Measurements, and hashes
the revealed parts so drift detection covers them. Off, or on any doc
generated before this release, nothing changes: rendered output and the
drift hash are identical. Update keeps the stored choice. Anatomy pins are
now measured on the placed instance rather than the source component, which
is what makes a revealed part pinnable. The per-variant token pane preview
in the Variants section also reveals when the toggle is on. When the option
is on, the Measurements diagram measures its size rails, padding bands, and
spacing from the revealed instance rather than the source component, so the
overlay matches what is drawn. - A self-describing DTCG output (CLI 0.8.2). The document extension now
carries acensusof what each emitted file holds and aconfig_hashof the
projection options that produced it, andresolver.jsoncarries that
extension instead of only the clipboard document doing so. The sidecar
records thetransformbehind each token's value in each mode, and the value
each alias resolves to. Every field is descriptive: no canvas hash and no
artifact identity moved, andEXTRACTOR_VERSIONis unchanged, so no document
needs regenerating. A repository pulling with an earlier CLI gets the same
files without these fields. The census reports what the projection produced;
report.jsonkeeps its own job of naming what it could not express.
Removed
- The non-public trees.
apps/website(the marketing site),docs/(the
prose specs, plans, reviews, brand system, and writing guides),
project-docs/(the archived vault), andscreenshots/(the capture
library) are no longer part of this repository; they are maintained
privately. This repository is the open-source product: the plugin, the
extractor, the proxy, the CLI, the shared brand package, their tests, and
the published JSON Schemas. No shipping code changed. - The two tests asserting that the website's schema copies stayed
byte-identical to the extractor's. There is no second copy left to drift, so
the assertion had nothing to compare. The contract they protected is
unchanged and is now checked end to end bynpm run check:site-live, which
compares the livespec-layer.com/schemas/**bytes against
packages/extractor/src/v5/schema/*.jsondirectly. The site is deployed
from the private repository, so a schema change here is not live until that
side redeploys, and the live check is what proves it.
Changed
- "Document hidden elements" now starts on for a component that has such
layers, and stays off for one that does not. A layer a boolean property
reveals is part of the component someone is trying to understand, so leaving
it out by default made the common case the one that needed a click.
defaultIncludeHiddenis the one source for that default, seeded once
extraction has produced facts, so a fresh screen never draws the switch on
for a component with nothing to reveal. Documents generated before this
release are untouched: Update keeps each doc's stored choice. - CI no longer builds the website;
verifyisnpm run check:cialone. The
job id is unchanged, so the required status check onmainstill applies. npm run check:nulscanspackages/andscripts/. The prose trees it also
covered, where the NUL trap bit three times, are scanned in the private
repository instead.
Spec Layer v5.0.0
Spec Layer v5.0.0
Documentation you generate in Figma, and context a coding agent can actually use.
Highlights
- Pulled tokens are files your build compiles.
spec-layer pullwrites atokens/directory with one CSS file per collection and mode, plus anindex.cssthat imports them in resolver order, so you can take a single mode and wireprefers-color-schemeyourself. Component briefs sit beside it incomponent-specs/, byte-identical to Copy for AI. Names come from the designer'scode_syntax.WEBwhen Figma declares one. Two tokens that would collide are both omitted and reported rather than guessed. - Foundations copy as DTCG. A Foundation copies as a Design Tokens Format Module 2025.10 resolver document. What the format cannot express is omitted and written to the report, never replaced with a plausible default or a fake reference.
- Free plans can publish a library. One Figma file, 10 changed publishes a UTC month. A publish counts only when it commits, and republishing unchanged content is answered as unchanged without a write or a count. Pro keeps 10 libraries.
- Publish screen, rebuilt. A status pill, the last publish date in local time, the free plan's updates left, and the two things you came for: the developer setup command and the AI agent prompt, each in full with a Copy. A library published by an earlier build reads "date not recorded" rather than a guessed date.
- Your coding agent gets a guide.
spec-layer skill --installwrites instructions into whichever agent host your repository uses, built from the command catalogue, your last pull, and a reading of your stack.spec-layer toolsprints every command with whether it reaches the network, whether it needs the pull key, what it writes, and what each exit code means. - Settings has an About section, giving the plugin version and the extractor version as separate rows. They answer different questions: a Library reporting a rebuild on every row is reacting to an extractor bump, which the plugin version alone cannot show.
- Component and Foundation Context v5, schema
5.1.0, with stable ids, complete alias chains, external references, composite typography and effect styles, and optionalcode_syntax. A component copy embeds only its own dependency closure.
Getting Started
Install from the Figma Community listing.
The CLI is on npm:
npx spec-layer setup
To run this release from source, with Node.js 22 or newer:
npm ci
npm run build:plugin
Then in Figma desktop choose Plugins → Development → Import plugin from manifest and select packages/plugin/manifest.json.
Security Model
Deterministic sections run entirely inside the plugin and send nothing. AI writing routes through the Spec Layer proxy, which holds the Anthropic credential; no user API key is involved. Component requests include a derived summary and, when it fits the limits, a rendered image. The proxy validates raw license keys with Lemon Squeezy but uses SHA-256 digests for its own cache keys, quota identities, and logs. Keep credentials and private design-system data out of Git.
Verification
This release candidate passed, on 2026-09-09:
npm ci
npm run check:ci # lint, typecheck, NUL scan, coverage, builds,
# CLI bundle smoke test, sandbox scan,
# proxy deploy dry run, dependency audit
git diff --check
npm run check:site-live
npm audit reports 0 vulnerabilities. npm run check:site-live confirms that https://spec-layer.com/schemas/foundation-context/v5.json and its component counterpart serve exactly the bytes committed in packages/extractor/src/v5/schema/, with the expected $id, and that a missing path returns 404.
The plugin's networkAccess and packages/plugin/src/ui/proxy.ts both point at https://api.spec-layer.com.
The manual Figma pass in packages/plugin/TESTING.md covers what unit tests cannot reach: component docs, Foundation docs, Library, and Copy for AI against a real file.
Known Limitations
- Workspace packages other than the CLI are not published to npm.
- AI prose is the only part of the product that uses a model. Extraction, rendering, drift detection, and Copy for AI are deterministic, and that split is deliberate.
- Free publishing is budgeted per self-asserted Figma identity, so the per-IP rate limiter is the only ceiling against a client that misreports it. See the accepted risks in
packages/proxy/README.md.
Full Changelog
See CHANGELOG.md.