Commit Graph
6 Commits
Author SHA1 Message Date
Vance IngallsandClaude Fable 5 d6d0fccbf2 fix(core): reproduce figma's vertical text trim via text-box-trim
A figma text node whose box is shorter than its line-height carries
vertically-trimmed (cap-to-baseline) bounds. The mapper positioned the box
at those bounds but let the browser lay glyphs with half-leading, pushing
them ~6px low on a 70px font (glyph-centroid measurement against figma's
own render: +9.1px vs figma's +3.4px inside the same pill). Emitting
text-box-trim: trim-both / text-box-edge: cap alphabetic reproduces the
trim in the render engine; post-fix centroid agrees within 0.4px and the
motion verifier's min window score improved 20.3 -> 25.3dB. Trim applies
only to single-line trimmed text; boxes matching their line-height are
untouched.

Skill: component imports now include a static fidelity self-check step
against figma's PNG export of the same node.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 10:47:01 -07:00
Vance IngallsandClaude Fable 5 63ff046c30 fix(core): figma mapper prefixes digit-leading element ids
slugify("3D Object - Headphones") produced id="3d-object-headphones" —
valid HTML, but querySelector("#3d-…") throws (CSS idents cannot start
with a digit), which kills GSAP targeting and figma-motion translation
against imported components. uniqueSlug now prefixes digit-leading slugs
("n3d-object-headphones"). Found translating a real Figma Motion timeline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 00:47:04 -07:00
Vance IngallsandClaude Fable 5 dfe63af3ae fix(core,cli): parent-relative figma child geometry; groups stop rejecting subcommand flags
Both found running the brand-loop guide end-to-end against the Simple
Design System:

- nodeToHtml subtracted the ROOT origin from every node's absolute bounds,
  but CSS absolute positioning resolves against the nearest positioned
  ancestor — every nesting level re-added its ancestors' offsets, drifting
  nested content down-right and pushing deep children off-frame (hero
  buttons invisible, pricing grid collapsed to one card). Children now
  subtract their PARENT's box; regression test with a two-level tree.

- trackCommandFailures asserted unknown flags against the command group's
  own (flagless) arg table even when the group was delegating to a
  subcommand, so `figma component <ref> --name x` imported and THEN threw
  "Unknown flag: --name". The assertion is now skipped when the first
  positional names a subcommand; leaf and non-delegating behavior is
  unchanged and covered by tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 00:47:03 -07:00
Vance IngallsandClaude Fable 5 e2c88ef689 fix(core,producer): composition CSS variables reach the render path at eval time
Live testing of the compile-time variable emission surfaced four gaps:

- The producer render path never emitted the compile-time stylesheet (only
  the preview bundler did), so eval-time reads — GSAP .from immediateRender,
  top-level getComputedStyle — saw undefined vars in rendered output. The
  producer's inlineSubCompositions now calls the shared
  emitRootCompositionVariableStyles and passes the variable hooks.
- --variables overrides weren't visible at eval time. They now thread from
  the orchestrator / distributed plan through compileStage into the emitted
  rules (window.__hfVariables still covers script reads).
- Per-declarer rules anchored on data-composition-id, which two inlined
  instances of one sub-composition share — instance A's rule restyled
  instance B, and a rule directly on the declarer defeated the host's
  inherited data-variable-values. Rules now anchor on per-instance
  data-hf-var-scope markers and layer nearest-host values over declared
  defaults, mirroring the runtime loader.
- Emission ignored authored CSS; a declared default now yields to a var
  already defined in an authored <style> block (define-if-absent, matching
  the runtime injection).

Also: the figma importer emits background-color (longhand) for solid fills.
GSAP backgroundColor tweens cannot read a var() through the background
shorthand — its pending-substitution longhands serialize empty, so .from
captured nothing and settled on transparent (pre-existing GSAP interaction,
reproduced with no composition variables involved).

Validated live: eval-time default + override, .from + override, two-instance
host branding, authored :root precedence, SDS brand-loop pixel parity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 02:28:16 -07:00
Vance IngallsandClaude Fable 5 def276524b fix(core,cli,lint): close the figma brand-token loop — runtime CSS variables, --name, snippet lint
Brand-loop live test (SDS duplicate, plans/figma/brand-loop-test-plan.md)
proved the recolor chain end-to-end and surfaced three gaps:

- runtime now defines every declared composition variable as a CSS
  custom property (document root at init + scoped sub-comp hosts in the
  loader), so imported var(--slug, literal) fills resolve live — without
  this the frozen literal always won and variable-driven rebranding
  could not propagate. Slug kept byte-compatible with the figma
  importer (parity test). render --variables overrides win.
- figma component --name: variant frames are often all named
  'Platform=Desktop' and slug-collided across imports.
- imported fragments carry data-hf-snippet and the project linter skips
  composition-root rules for them.
- /figma skill documents the field-tested non-Enterprise tokens path
  (MCP get_variable_defs joined with REST boundVariables ids).

Shared-helper extractions (injectScopedStyles, flattenedRoot module,
parseHostVariableValues, rasterizeFallback, shapeCss) satisfy the
dedup/complexity audit the runtime changes tripped.

Validated live: brand-loop renders purple from the attribute alone (no
manual :root); 118 figma + 662 runtime/compiler + 331 lint tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 00:47:13 -07:00
Vance IngallsandClaude Fable 5 ee7a96147a feat(core,cli): figma component import with binding-aware node-to-html mapper (M3) (#1872)
resolveBindings: scan the full tree (boundVariables + style ids, alias
chains, children) and partition exact-ID-only against the binding index
before any CSS is emitted per spec 7.1 - never value matching.

nodeToHtml: absolute geometry at figma bounds inside a fixed-size root,
solid/linear-gradient fills, corner radius, opacity, drop shadow, blur,
text styles; resolved bindings emit var(--slug, literal), unresolved
bake literals with data-figma-unresolved; visible:false respected;
vectors/boolean ops route to a rasterize list.

hyperframes figma component: tree -> bindings -> html, rasterize
fallback via Phase-1 asset export with src backfill, registry-item
packaging, unresolved-binding guidance in output.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 18:19:01 -07:00