Skip to content

Spec Layer v5.1.0

Choose a tag to compare

@sandroleks sandroleks released this 16 Sep 21:26
· 70 commits to main since this release
28a55c9

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 with shownBy, and filters
    through tokensFor, the token counterpart of anatomyFor. 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;
    carrying shown_by onto token rules needs schema 5.3.0. One export does
    change: the brief's unbound list 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 and specContentHash it 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 inner strong fell back to the browser's bolder.
  • 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, and packages/extractor/test/redos.test.ts keeps
    the old regexes as executable oracles to prove it, which is load-bearing
    because cleanPartName and stateBaseName feed 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's Icon left) is now an anatomy part.
    The YAML brief and the Component Context v5 artifact always include such
    parts, each carrying shown_by, the property that reveals it. Component
    Context schema 5.2.0 adds shown_by as an optional string on anatomy
    nodes; the schema URL is unchanged. The envelope now requires
    schema_version 5.2.0, so once the site redeploys, an artifact still
    stamped 5.1.0 no 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, and EXTRACTOR_VERSION is 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 a census of what each emitted file holds and a config_hash of the
    projection options that produced it, and resolver.json carries that
    extension instead of only the clipboard document doing so. The sidecar
    records the transform behind 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, and EXTRACTOR_VERSION is 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.json keeps 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), and screenshots/ (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 by npm run check:site-live, which
    compares the live spec-layer.com/schemas/** bytes against
    packages/extractor/src/v5/schema/*.json directly. 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.
    defaultIncludeHidden is 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; verify is npm run check:ci alone. The
    job id is unchanged, so the required status check on main still applies.
  • npm run check:nul scans packages/ and scripts/. The prose trees it also
    covered, where the NUL trap bit three times, are scanned in the private
    repository instead.