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>
This commit is contained in:
Vance Ingalls
2026-07-08 00:47:13 -07:00
co-authored by Claude Fable 5
parent 80271863d3
commit def276524b
14 changed files with 623 additions and 173 deletions
@@ -203,3 +203,96 @@ describe("readDeclaredDefaults", () => {
expect(readDeclaredDefaults(b)).toEqual({});
});
});
describe("css variable injection (figma brand-token chain)", () => {
afterEach(() => {
document.documentElement.removeAttribute(VARIABLES_ATTR);
document.documentElement.removeAttribute("data-hf-css-vars");
document.documentElement.style.cssText = "";
document.body.innerHTML = "";
delete (window as Window & { __hfVariables?: unknown }).__hfVariables;
});
it("slug stays byte-compatible with the figma importer", async () => {
const { slugify } = await import("../figma/nodeToHtml");
const { cssVariableName } = await import("../tokenSlug");
for (const id of [
"figma:sds-color-background-brand-default",
"figma:Brand/Primary 500",
"figma:Acme/Semantic/Blue-500",
"!!!",
]) {
expect(cssVariableName(id)).toBe(`--${slugify(id)}`);
}
});
it("defines declared variables on the DECLARING element, not globally", async () => {
const { injectCompositionCssVariables } = await import("./getVariables");
document.body.innerHTML = `<div id="root" ${VARIABLES_ATTR}='[{"id":"figma:brand/primary","type":"color","label":"p","default":"#112233"}]'></div>`;
injectCompositionCssVariables(document);
const root = document.getElementById("root") as HTMLElement;
expect(root.style.getPropertyValue("--figma-brand-primary")).toBe("#112233");
expect(document.documentElement.style.getPropertyValue("--figma-brand-primary")).toBe("");
});
it("two compositions on one page keep their own same-id values", async () => {
const { injectCompositionCssVariables } = await import("./getVariables");
document.body.innerHTML =
`<div id="a" ${VARIABLES_ATTR}='[{"id":"figma:brand","type":"color","label":"b","default":"#aa0000"}]'></div>` +
`<div id="b" ${VARIABLES_ATTR}='[{"id":"figma:brand","type":"color","label":"b","default":"#0000bb"}]'></div>`;
injectCompositionCssVariables(document);
expect(
(document.getElementById("a") as HTMLElement).style.getPropertyValue("--figma-brand"),
).toBe("#aa0000");
expect(
(document.getElementById("b") as HTMLElement).style.getPropertyValue("--figma-brand"),
).toBe("#0000bb");
});
it("declared defaults do NOT clobber an authored inline definition (define-if-absent)", async () => {
const { injectCompositionCssVariables } = await import("./getVariables");
document.body.innerHTML = `<div id="root" style="--accent: #3b82f6" ${VARIABLES_ATTR}='[{"id":"accent","type":"color","label":"a","default":"#ff5722"}]'></div>`;
injectCompositionCssVariables(document);
expect(
(document.getElementById("root") as HTMLElement).style.getPropertyValue("--accent"),
).toBe("#3b82f6");
});
it("render-time overrides win over declared defaults AND authored values", async () => {
const { injectCompositionCssVariables } = await import("./getVariables");
document.body.innerHTML = `<div id="root" style="--figma-brand: #000000" ${VARIABLES_ATTR}='[{"id":"figma:brand","type":"color","label":"b","default":"#2c2c2c"}]'></div>`;
(window as Window & { __hfVariables?: Record<string, unknown> }).__hfVariables = {
"figma:brand": "#00ff99",
};
injectCompositionCssVariables(document);
expect(
(document.getElementById("root") as HTMLElement).style.getPropertyValue("--figma-brand"),
).toBe("#00ff99");
});
it("skips empty-string values (setProperty would remove, not define)", async () => {
const { applyCssVariables } = await import("./getVariables");
const el = document.createElement("div");
applyCssVariables(el, { blank: "", real: "#123456" });
expect(el.style.getPropertyValue("--blank")).toBe("");
expect(el.style.getPropertyValue("--real")).toBe("#123456");
expect(el.getAttribute("data-hf-css-vars")).toBe("--real");
});
it("clearAppliedCssVariables removes exactly what was applied", async () => {
const { applyCssVariables, clearAppliedCssVariables } = await import("./getVariables");
const el = document.createElement("div");
el.style.setProperty("--authored", "keep");
applyCssVariables(el, { "figma:brand": "#111111" });
clearAppliedCssVariables(el);
expect(el.style.getPropertyValue("--figma-brand")).toBe("");
expect(el.style.getPropertyValue("--authored")).toBe("keep");
expect(el.hasAttribute("data-hf-css-vars")).toBe(false);
});
it("getVariables() honors element-declared variables like the injection does", async () => {
const { getVariables } = await import("./getVariables");
document.body.innerHTML = `<div ${VARIABLES_ATTR}='[{"id":"figma:brand","type":"color","label":"b","default":"#445566"}]'></div>`;
expect(getVariables()["figma:brand"]).toBe("#445566");
});
});