From 12470f64f72521cd93ebbc4efa20f46988833c35 Mon Sep 17 00:00:00 2001 From: DavidBabinec Date: Tue, 27 Oct 2026 11:07:07 +0100 Subject: [PATCH] fix(figma): resolve optional dark colors independently RAW references without dark values crashed variable preparation before the host could save them. Resolve an enabled dark value on its own and use the referenced dark color when available. --- CHANGELOG.md | 1 + .../figma/src/colors/functions/getColor.ts | 2 +- .../figma/src/colors/getColorVariables.ts | 7 ++- packages/figma/tests/colorVariables.test.ts | 58 +++++++++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 packages/figma/tests/colorVariables.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index e06dff9..e80cb94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed +- Fixed Figma synchronization stopping when a RAW color referenced another variable without a dark-mode value. Light and dark colors now resolve independently, so mixed literal and referenced colors keep their correct values. - Fixed Bricks sites hanging when removed Core Framework classes were cleaned up. The initial lookup joined post metadata five times, multiplying rows on sites with many metadata entries. It now checks all five Bricks metadata keys through one join while retaining the existing batched cleanup. - Fixed transparent variants and shade/tint counts for RAW colors that reference another variable, such as `var(--bg-body)`. Generated variants now follow the current source color, while manually edited swatches are preserved. Older saved swatches without edit history are left unchanged on load; use Reset on a stale swatch to return it to automatic generation, then save to update the site's CSS. - Fixed the block editor going blank with the Core Framework Gutenberg integration enabled. The editor script was loaded with an outdated, hard-coded dependency list that left out WordPress's `react-jsx-runtime`, so rendering the Core Framework class panel threw on an undefined `ReactJSXRuntime` and took the whole editor down. The script now loads the dependencies its build declares. diff --git a/packages/figma/src/colors/functions/getColor.ts b/packages/figma/src/colors/functions/getColor.ts index 4cdf498..b8f8cc0 100644 --- a/packages/figma/src/colors/functions/getColor.ts +++ b/packages/figma/src/colors/functions/getColor.ts @@ -24,7 +24,7 @@ export const getColor = (value: string, colorVariables?: ColorVariable[], isDark if (variable) { visited.add(varName); - return variable.colorValue; + return isDarkColor ? variable.colorDarkValue || variable.colorValue : variable.colorValue; } } diff --git a/packages/figma/src/colors/getColorVariables.ts b/packages/figma/src/colors/getColorVariables.ts index b135b68..9f36e80 100644 --- a/packages/figma/src/colors/getColorVariables.ts +++ b/packages/figma/src/colors/getColorVariables.ts @@ -32,7 +32,12 @@ export function getColorVariables({ preset, colorVariables }: GetColorVariablesP for (const color of group.colors) { const isValidColor = isValidColorValue(color.value); const colorValue = isValidColor ? color.value : getColor(color.value, colorVariables); - const darkColorValue = isValidColor ? color.darkValue : getColor(color.darkValue, colorVariables); + const darkColorValue = + color.isDarkMode && color.darkValue + ? isValidColorValue(color.darkValue) + ? color.darkValue + : getColor(color.darkValue, colorVariables, true) + : undefined; variables.push({ variable: `Colors/${group.name}/${variablePrefix}${color.name}`, diff --git a/packages/figma/tests/colorVariables.test.ts b/packages/figma/tests/colorVariables.test.ts new file mode 100644 index 0000000..8633403 --- /dev/null +++ b/packages/figma/tests/colorVariables.test.ts @@ -0,0 +1,58 @@ +import { expect, test } from "bun:test"; +import { getColorVariables } from "../src/colors/getColorVariables"; +import type { Preset } from "../src/types"; + +const colorVariables = [ + { name: "brand-navy", colorValue: "#0a2545", colorDarkValue: "#ffffff" }, +]; + +function variables(color: Record) { + const preset = { + modulesData: { + COLOR_SYSTEM: { groups: [{ name: "Brand", colors: [{ name: "ink", ...color }] }] }, + }, + } as Preset; + return getColorVariables({ preset, colorVariables }); +} + +test("exports a RAW reference without a dark value, including transparent variants", () => { + expect( + variables({ value: "var(--brand-navy)", transparent: true, transparentVariables: [50] }), + ).toEqual([ + { + variable: "Colors/Brand/ink", + type: "COLOR", + value: { r: 10 / 255, g: 37 / 255, b: 69 / 255, a: 1 }, + webSyntax: "var(--ink)", + }, + { + variable: "Colors/Brand/ink-50", + type: "COLOR", + value: { r: 10 / 255, g: 37 / 255, b: 69 / 255, a: 0.5 }, + webSyntax: "var(--ink-50)", + }, + ]); +}); + +for (const darkValue of [undefined, null, ""]) { + test(`does not resolve an absent dark value (${darkValue})`, () => { + expect(variables({ value: "var(--brand-navy)", isDarkMode: true, darkValue })).toHaveLength(1); + }); +} + +for (const value of ["#0a2545", "var(--brand-navy)"]) { + for (const darkValue of ["#ffffff", "var(--brand-navy)"]) { + test(`resolves light ${value} and dark ${darkValue} independently`, () => { + const result = variables({ value, darkValue, isDarkMode: true }); + expect(result[0].value).toEqual({ r: 10 / 255, g: 37 / 255, b: 69 / 255, a: 1 }); + expect(result[1].variable).toBe("Dark Colors/Brand/ink"); + expect(result[1].value).toEqual({ r: 1, g: 1, b: 1, a: 1 }); + }); + } +} + +test("does not export dark variables when dark mode is disabled", () => { + expect( + variables({ value: "var(--brand-navy)", darkValue: "var(--brand-navy)", isDarkMode: false }), + ).toHaveLength(1); +});