fix(skills): stop a near-neutral color from being promoted to a brand accent

semanticColors() ranked every leftover preset color by chroma to pick
accent/accent2, with no floor. A preset with only one real accent (e.g.
broadside's fire-orange) still handed back an accents[1]: its next-highest
leftover, often a near-neutral grey text tone (broadside's cream-muted,
chroma 8). build-frame.mjs's remix then treated that as an exact accent2
match and repainted it with the brand's literal accent2 hex, turning a
muted grey text color into a saturated amber.

Add a chroma>=16 floor before a color counts as a real accent candidate,
matching the neutral cutoff the remix itself already uses to keep a grey
ladder grey. Below the floor, accent2 falls back to the primary accent.
This commit is contained in:
Miguel Angel Simon Sierra
2026-09-23 00:37:15 -04:00
parent 387f46915c
commit 08744926b0
4 changed files with 30 additions and 2 deletions
+1 -1
View File
@@ -70,7 +70,7 @@
"files": 30
},
"product-launch-video": {
"hash": "2c3d5d90b2b181a0",
"hash": "3f16091b195233bb",
"files": 31
},
"remotion-to-hyperframes": {
@@ -74,3 +74,11 @@ test("a light-ground preset (capsule) onto a light-mode brand is not inverted",
const { stdout } = runBuildFrame("capsule", LIGHT_BRAND_TOKENS);
assert.doesNotMatch(stdout, /INVERTED/);
});
test("broadside's cream-muted stays a neutral grey, not the brand's saturated accent", () => {
const { frameMd } = runBuildFrame("broadside", DARK_BRAND_TOKENS);
const m = /cream-muted:\s*"(#[0-9A-Fa-f]{6})"/.exec(frameMd);
assert.ok(m, "cream-muted key missing from frame.md");
// #FFB800 is the brand's literal accent2 — forced in before the fix.
assert.notEqual(m[1].toUpperCase(), "#FFB800");
});
@@ -181,12 +181,17 @@ export function semanticColors(colors, md) {
byLum[byLum.length - 1] ?? colors[colors.length - 1],
);
const accents = colors
// A candidate must clear a neutral chroma floor to count as a real accent — otherwise a
// preset with only one genuine accent hands back a near-neutral leftover (e.g. a grey text
// tone at chroma ~8) as accent2, which then gets an exact-match repaint downstream. 16
// matches the neutral cutoff the remix itself already uses to keep a grey ladder grey.
.filter(
([k, v]) =>
v !== ink &&
v !== canvas &&
!UA_DEFAULT_COLORS.has(String(v).toUpperCase()) &&
!STATUS_ROLE_KEY.test(k), // a status red/green carries meaning by hue — never an accent
!STATUS_ROLE_KEY.test(k) && // a status red/green carries meaning by hue — never an accent
chroma(v) >= 16,
)
.sort((a, b) => chroma(b[1]) - chroma(a[1]))
.map(([, v]) => v);
@@ -122,3 +122,18 @@ test("every shipped preset's detected ground polarity matches its declared regis
);
}
});
test("semanticColors keeps a near-neutral 'second accent' neutral instead of promoting it", () => {
// broadside's cream-muted (#888880, chroma 8) is a muted grey text tone, not a real second
// brand accent — it must not win accent2 over the neutral chroma floor.
const colors = [
["ink-black", "#111111"],
["fire-orange", "#E85D26"],
["cream", "#F0ECE5"],
["cream-muted", "#888880"],
];
const roles = semanticColors(colors);
assert.equal(roles.accent, "#E85D26");
assert.notEqual(roles.accent2, "#888880");
assert.equal(roles.accent2, "#E85D26"); // no 2nd real accent — falls back to the primary
});