okchroma 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist-lib/index.cjs
CHANGED
|
@@ -597,14 +597,11 @@ var RED_SOLVE = {
|
|
|
597
597
|
brickCoolGold: -3,
|
|
598
598
|
brickDesat: 0.85,
|
|
599
599
|
brickExtraDeep: 0.02,
|
|
600
|
-
// red's
|
|
601
|
-
//
|
|
602
|
-
//
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
redHuesWarmBrand: [28, 24, 33.3, 38],
|
|
606
|
-
redHuesMagentaBrand: [33.3, 38, 28],
|
|
607
|
-
redHueMagentaDh: -15
|
|
600
|
+
// the red complement's hue sets (resolve.ts redComplementVariant): the canonical hue and
|
|
601
|
+
// the two ends of the error band the field ships, read through this engine (the cool end
|
|
602
|
+
// near Primer's danger, the gold side near USWDS's error). The set furthest from the brand
|
|
603
|
+
// stamp by the side-by-side measure ships.
|
|
604
|
+
setHues: { true: 33.3, cool: 24, "gold-side": 38 }
|
|
608
605
|
};
|
|
609
606
|
function redSolveDist(c, red) {
|
|
610
607
|
const dh = (c.H - red.H + 540) % 360 - 180;
|
|
@@ -3112,63 +3109,61 @@ function hueCollisionPending(scale, sigScales) {
|
|
|
3112
3109
|
}
|
|
3113
3110
|
var blackLcAt = (L, C2, H2) => Math.abs(apcaLc(apcaYAt(0, 0, 0), apcaYAt(L, clampChromaToGamut(L, C2, H2), H2)));
|
|
3114
3111
|
var redCleanBeside = (brandCta, c) => p2Diff(brandCta, c) >= (c.L < brandCta.L ? P2_D : P2_D_UP) && redSolveDist(brandCta, c) >= RED_GATE.G + RED_SOLVE.ring;
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3112
|
+
var redSetsCache = /* @__PURE__ */ new Map();
|
|
3113
|
+
var redSetKey = (set, band) => `${set}:${band}`;
|
|
3114
|
+
var bandBelow = (band) => {
|
|
3115
|
+
const i = ARCHETYPES.findIndex((a) => a.name === band);
|
|
3116
|
+
return ARCHETYPES[Math.max(0, i - 1)].name;
|
|
3117
|
+
};
|
|
3118
|
+
function redSetsFor(red, contrastProfile) {
|
|
3119
|
+
const cacheKey = contrastProfile ?? "wcag";
|
|
3120
|
+
const hit = redSetsCache.get(cacheKey);
|
|
3121
|
+
if (hit) return hit;
|
|
3122
|
+
const own = classifyArchetype(red.scale.cta.L);
|
|
3123
|
+
const sets = /* @__PURE__ */ new Map();
|
|
3124
|
+
for (const [set, hue] of Object.entries(RED_SOLVE.setHues)) for (const band of [own, bandBelow(own)]) {
|
|
3125
|
+
sets.set(redSetKey(set, band), generateScale(red.def.hex, red.def.name, band === own ? void 0 : band, {
|
|
3126
|
+
hueShiftDeg: hue - red.def.H,
|
|
3127
|
+
darkChromaCurve,
|
|
3128
|
+
darkCtaC: "signal",
|
|
3129
|
+
darkFillMinL: red.def.darkFillMinL,
|
|
3130
|
+
enforceOnFillContrast: true,
|
|
3131
|
+
suppressRedCool: true,
|
|
3132
|
+
goldBoost: true,
|
|
3133
|
+
signalWarmDrift: true,
|
|
3134
|
+
contrastProfile,
|
|
3135
|
+
apcaClearance: true,
|
|
3136
|
+
apcaClearanceLc: CRITICAL_CLEARANCE_LC
|
|
3137
|
+
}));
|
|
3138
|
+
}
|
|
3139
|
+
redSetsCache.set(cacheKey, sets);
|
|
3140
|
+
return sets;
|
|
3141
|
+
}
|
|
3142
|
+
function redComplementVariant(brandCta, brandWentUp, red, contrastProfile) {
|
|
3126
3143
|
const redCta = red.scale.cta;
|
|
3127
|
-
const at = (L, H2) => ({ L, C: clampChromaToGamut(L, rctx.cAt("light", L, rctx.brandC), H2), H: H2 });
|
|
3128
3144
|
const fireLc = CRITICAL_CLEARANCE_LC + APCA_ENFORCE_MARGIN_LC;
|
|
3129
|
-
const shipsWhite = (c) => onFillIsWhiteDarkAt(c.L, c.C, c.H, true, contrastProfile === "apca" ? void 0 : 4.5);
|
|
3130
3145
|
const poleOk = (c) => {
|
|
3131
|
-
const white =
|
|
3146
|
+
const white = onFillIsWhiteDarkAt(c.L, c.C, c.H, true, contrastProfile === "apca" ? void 0 : 4.5);
|
|
3132
3147
|
const lc = white ? whiteTextLcAt(c.L, c.C, c.H) : blackLcAt(c.L, c.C, c.H);
|
|
3133
3148
|
const legal = contrastProfile === "apca" || legalRatio(c.L, c.C, c.H, white ? 1 : 0) >= 4.5;
|
|
3134
3149
|
return legal && lc >= fireLc;
|
|
3135
3150
|
};
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
return poleOk(c2) ? c2 : null;
|
|
3141
|
-
};
|
|
3142
|
-
const clean = (c) => redCleanBeside(brandCta, c) && poleOk(c);
|
|
3143
|
-
if (!brandWentUp && clean(redCta)) return null;
|
|
3144
|
-
const hues = hueDelta(seed.H, redCta.H) <= RED_SOLVE.redHueMagentaDh ? RED_SOLVE.redHuesMagentaBrand : RED_SOLVE.redHuesWarmBrand;
|
|
3145
|
-
const wantLighter = brandCta.L <= redCta.L;
|
|
3146
|
-
const tiers = wantLighter ? [[...RED_SOLVE.edgeL], RED_SOLVE.coreL.filter((l) => l > brandCta.L)] : [[...RED_SOLVE.coreL], [...RED_SOLVE.edgeL]];
|
|
3151
|
+
if (!brandWentUp && redCleanBeside(brandCta, redCta) && poleOk(redCta)) return null;
|
|
3152
|
+
const own = classifyArchetype(redCta.L);
|
|
3153
|
+
const band = classifyArchetype(brandCta.L) === own ? bandBelow(own) : own;
|
|
3154
|
+
const sets = redSetsFor(red, contrastProfile);
|
|
3147
3155
|
let pick = null;
|
|
3148
|
-
|
|
3149
|
-
const
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
if (!onSide || !clean(c)) continue;
|
|
3155
|
-
pick = c;
|
|
3156
|
-
break outer;
|
|
3157
|
-
}
|
|
3158
|
-
}
|
|
3159
|
-
if (!pick) return null;
|
|
3160
|
-
const cta = makeStop(redCta.stop, pick.L, rctx.cAt("light", pick.L, rctx.brandC), pick.H);
|
|
3161
|
-
const vCFor = (L) => rctx.cAt("light", L, rctx.brandC);
|
|
3162
|
-
const hL = stateFillL(pick.L, "light", 1);
|
|
3163
|
-
const ctaHover = makeStop(red.scale.ctaHover.stop, hL, vCFor(hL), pick.H);
|
|
3164
|
-
const pL = stateFillL(pick.L, "light", 2);
|
|
3165
|
-
const ctaPressed = makeStop(red.scale.ctaPressed.stop, pL, vCFor(pL), pick.H);
|
|
3166
|
-
const onFillTextIsWhite = onFillIsWhiteDarkAt(cta.L, cta.C, cta.H, true, contrastProfile === "apca" ? void 0 : 4.5);
|
|
3156
|
+
for (const set of Object.keys(RED_SOLVE.setHues)) {
|
|
3157
|
+
const scale = sets.get(redSetKey(set, band));
|
|
3158
|
+
const d = p2Diff(brandCta, scale.cta);
|
|
3159
|
+
if (!pick || d > pick.d) pick = { set, scale, d };
|
|
3160
|
+
}
|
|
3161
|
+
const { cta, ctaHover, ctaPressed, onFillTextIsWhite } = pick.scale;
|
|
3167
3162
|
return {
|
|
3168
3163
|
scale: { ...red.scale, cta, ctaHover, ctaPressed, onFillTextIsWhite },
|
|
3169
|
-
//
|
|
3170
|
-
// the
|
|
3171
|
-
note: `red \u2192 ${pick.
|
|
3164
|
+
// the community plugin shows the part after the arrow as a display label; the hex is
|
|
3165
|
+
// the variant's identity there, so the note carries no value
|
|
3166
|
+
note: `red \u2192 ${pick.set} at ${band}`
|
|
3172
3167
|
};
|
|
3173
3168
|
}
|
|
3174
3169
|
function resolveBrand(hex, name, opts) {
|
|
@@ -3214,7 +3209,7 @@ function resolveBrand(hex, name, opts) {
|
|
|
3214
3209
|
const signalOverrides = [];
|
|
3215
3210
|
if (solving || opts?.exact && signalMoves && !opts?.archetypeOverride) {
|
|
3216
3211
|
const brandWentUp = !!scale.ctaRepelled?.light && scale.cta.L > seedO.L + 1e-6;
|
|
3217
|
-
const v2 = redComplementVariant(scale.cta,
|
|
3212
|
+
const v2 = redComplementVariant(scale.cta, brandWentUp, sigScales.get("red"), opts?.contrastProfile);
|
|
3218
3213
|
if (v2) signalOverrides.push({ name: "red", scale: v2.scale, note: v2.note });
|
|
3219
3214
|
}
|
|
3220
3215
|
let warnVariant = null;
|
|
@@ -3376,7 +3371,7 @@ function resolveTheme(input) {
|
|
|
3376
3371
|
const red = sigScales.get("red");
|
|
3377
3372
|
const primaryRed = mergedOverrides.find((o) => o.name === "red");
|
|
3378
3373
|
if (!primaryRed) {
|
|
3379
|
-
const v2 = redComplementVariant(secScale.cta,
|
|
3374
|
+
const v2 = redComplementVariant(secScale.cta, false, red, cp2);
|
|
3380
3375
|
if (v2 && redCleanBeside(primary.scale.cta, v2.scale.cta)) adopt("red", v2.scale, `${v2.note} (for the secondary)`);
|
|
3381
3376
|
}
|
|
3382
3377
|
for (const sigName of ["yellow", "green", "blue"]) {
|
package/dist-lib/index.mjs
CHANGED
|
@@ -504,14 +504,11 @@ var RED_SOLVE = {
|
|
|
504
504
|
brickCoolGold: -3,
|
|
505
505
|
brickDesat: 0.85,
|
|
506
506
|
brickExtraDeep: 0.02,
|
|
507
|
-
// red's
|
|
508
|
-
//
|
|
509
|
-
//
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
redHuesWarmBrand: [28, 24, 33.3, 38],
|
|
513
|
-
redHuesMagentaBrand: [33.3, 38, 28],
|
|
514
|
-
redHueMagentaDh: -15
|
|
507
|
+
// the red complement's hue sets (resolve.ts redComplementVariant): the canonical hue and
|
|
508
|
+
// the two ends of the error band the field ships, read through this engine (the cool end
|
|
509
|
+
// near Primer's danger, the gold side near USWDS's error). The set furthest from the brand
|
|
510
|
+
// stamp by the side-by-side measure ships.
|
|
511
|
+
setHues: { true: 33.3, cool: 24, "gold-side": 38 }
|
|
515
512
|
};
|
|
516
513
|
function redSolveDist(c, red) {
|
|
517
514
|
const dh = (c.H - red.H + 540) % 360 - 180;
|
|
@@ -3019,63 +3016,61 @@ function hueCollisionPending(scale, sigScales) {
|
|
|
3019
3016
|
}
|
|
3020
3017
|
var blackLcAt = (L, C2, H2) => Math.abs(apcaLc(apcaYAt(0, 0, 0), apcaYAt(L, clampChromaToGamut(L, C2, H2), H2)));
|
|
3021
3018
|
var redCleanBeside = (brandCta, c) => p2Diff(brandCta, c) >= (c.L < brandCta.L ? P2_D : P2_D_UP) && redSolveDist(brandCta, c) >= RED_GATE.G + RED_SOLVE.ring;
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3019
|
+
var redSetsCache = /* @__PURE__ */ new Map();
|
|
3020
|
+
var redSetKey = (set, band) => `${set}:${band}`;
|
|
3021
|
+
var bandBelow = (band) => {
|
|
3022
|
+
const i = ARCHETYPES.findIndex((a) => a.name === band);
|
|
3023
|
+
return ARCHETYPES[Math.max(0, i - 1)].name;
|
|
3024
|
+
};
|
|
3025
|
+
function redSetsFor(red, contrastProfile) {
|
|
3026
|
+
const cacheKey = contrastProfile ?? "wcag";
|
|
3027
|
+
const hit = redSetsCache.get(cacheKey);
|
|
3028
|
+
if (hit) return hit;
|
|
3029
|
+
const own = classifyArchetype(red.scale.cta.L);
|
|
3030
|
+
const sets = /* @__PURE__ */ new Map();
|
|
3031
|
+
for (const [set, hue] of Object.entries(RED_SOLVE.setHues)) for (const band of [own, bandBelow(own)]) {
|
|
3032
|
+
sets.set(redSetKey(set, band), generateScale(red.def.hex, red.def.name, band === own ? void 0 : band, {
|
|
3033
|
+
hueShiftDeg: hue - red.def.H,
|
|
3034
|
+
darkChromaCurve,
|
|
3035
|
+
darkCtaC: "signal",
|
|
3036
|
+
darkFillMinL: red.def.darkFillMinL,
|
|
3037
|
+
enforceOnFillContrast: true,
|
|
3038
|
+
suppressRedCool: true,
|
|
3039
|
+
goldBoost: true,
|
|
3040
|
+
signalWarmDrift: true,
|
|
3041
|
+
contrastProfile,
|
|
3042
|
+
apcaClearance: true,
|
|
3043
|
+
apcaClearanceLc: CRITICAL_CLEARANCE_LC
|
|
3044
|
+
}));
|
|
3045
|
+
}
|
|
3046
|
+
redSetsCache.set(cacheKey, sets);
|
|
3047
|
+
return sets;
|
|
3048
|
+
}
|
|
3049
|
+
function redComplementVariant(brandCta, brandWentUp, red, contrastProfile) {
|
|
3033
3050
|
const redCta = red.scale.cta;
|
|
3034
|
-
const at = (L, H2) => ({ L, C: clampChromaToGamut(L, rctx.cAt("light", L, rctx.brandC), H2), H: H2 });
|
|
3035
3051
|
const fireLc = CRITICAL_CLEARANCE_LC + APCA_ENFORCE_MARGIN_LC;
|
|
3036
|
-
const shipsWhite = (c) => onFillIsWhiteDarkAt(c.L, c.C, c.H, true, contrastProfile === "apca" ? void 0 : 4.5);
|
|
3037
3052
|
const poleOk = (c) => {
|
|
3038
|
-
const white =
|
|
3053
|
+
const white = onFillIsWhiteDarkAt(c.L, c.C, c.H, true, contrastProfile === "apca" ? void 0 : 4.5);
|
|
3039
3054
|
const lc = white ? whiteTextLcAt(c.L, c.C, c.H) : blackLcAt(c.L, c.C, c.H);
|
|
3040
3055
|
const legal = contrastProfile === "apca" || legalRatio(c.L, c.C, c.H, white ? 1 : 0) >= 4.5;
|
|
3041
3056
|
return legal && lc >= fireLc;
|
|
3042
3057
|
};
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
return poleOk(c2) ? c2 : null;
|
|
3048
|
-
};
|
|
3049
|
-
const clean = (c) => redCleanBeside(brandCta, c) && poleOk(c);
|
|
3050
|
-
if (!brandWentUp && clean(redCta)) return null;
|
|
3051
|
-
const hues = hueDelta(seed.H, redCta.H) <= RED_SOLVE.redHueMagentaDh ? RED_SOLVE.redHuesMagentaBrand : RED_SOLVE.redHuesWarmBrand;
|
|
3052
|
-
const wantLighter = brandCta.L <= redCta.L;
|
|
3053
|
-
const tiers = wantLighter ? [[...RED_SOLVE.edgeL], RED_SOLVE.coreL.filter((l) => l > brandCta.L)] : [[...RED_SOLVE.coreL], [...RED_SOLVE.edgeL]];
|
|
3058
|
+
if (!brandWentUp && redCleanBeside(brandCta, redCta) && poleOk(redCta)) return null;
|
|
3059
|
+
const own = classifyArchetype(redCta.L);
|
|
3060
|
+
const band = classifyArchetype(brandCta.L) === own ? bandBelow(own) : own;
|
|
3061
|
+
const sets = redSetsFor(red, contrastProfile);
|
|
3054
3062
|
let pick = null;
|
|
3055
|
-
|
|
3056
|
-
const
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
if (!onSide || !clean(c)) continue;
|
|
3062
|
-
pick = c;
|
|
3063
|
-
break outer;
|
|
3064
|
-
}
|
|
3065
|
-
}
|
|
3066
|
-
if (!pick) return null;
|
|
3067
|
-
const cta = makeStop(redCta.stop, pick.L, rctx.cAt("light", pick.L, rctx.brandC), pick.H);
|
|
3068
|
-
const vCFor = (L) => rctx.cAt("light", L, rctx.brandC);
|
|
3069
|
-
const hL = stateFillL(pick.L, "light", 1);
|
|
3070
|
-
const ctaHover = makeStop(red.scale.ctaHover.stop, hL, vCFor(hL), pick.H);
|
|
3071
|
-
const pL = stateFillL(pick.L, "light", 2);
|
|
3072
|
-
const ctaPressed = makeStop(red.scale.ctaPressed.stop, pL, vCFor(pL), pick.H);
|
|
3073
|
-
const onFillTextIsWhite = onFillIsWhiteDarkAt(cta.L, cta.C, cta.H, true, contrastProfile === "apca" ? void 0 : 4.5);
|
|
3063
|
+
for (const set of Object.keys(RED_SOLVE.setHues)) {
|
|
3064
|
+
const scale = sets.get(redSetKey(set, band));
|
|
3065
|
+
const d = p2Diff(brandCta, scale.cta);
|
|
3066
|
+
if (!pick || d > pick.d) pick = { set, scale, d };
|
|
3067
|
+
}
|
|
3068
|
+
const { cta, ctaHover, ctaPressed, onFillTextIsWhite } = pick.scale;
|
|
3074
3069
|
return {
|
|
3075
3070
|
scale: { ...red.scale, cta, ctaHover, ctaPressed, onFillTextIsWhite },
|
|
3076
|
-
//
|
|
3077
|
-
// the
|
|
3078
|
-
note: `red \u2192 ${pick.
|
|
3071
|
+
// the community plugin shows the part after the arrow as a display label; the hex is
|
|
3072
|
+
// the variant's identity there, so the note carries no value
|
|
3073
|
+
note: `red \u2192 ${pick.set} at ${band}`
|
|
3079
3074
|
};
|
|
3080
3075
|
}
|
|
3081
3076
|
function resolveBrand(hex, name, opts) {
|
|
@@ -3121,7 +3116,7 @@ function resolveBrand(hex, name, opts) {
|
|
|
3121
3116
|
const signalOverrides = [];
|
|
3122
3117
|
if (solving || opts?.exact && signalMoves && !opts?.archetypeOverride) {
|
|
3123
3118
|
const brandWentUp = !!scale.ctaRepelled?.light && scale.cta.L > seedO.L + 1e-6;
|
|
3124
|
-
const v2 = redComplementVariant(scale.cta,
|
|
3119
|
+
const v2 = redComplementVariant(scale.cta, brandWentUp, sigScales.get("red"), opts?.contrastProfile);
|
|
3125
3120
|
if (v2) signalOverrides.push({ name: "red", scale: v2.scale, note: v2.note });
|
|
3126
3121
|
}
|
|
3127
3122
|
let warnVariant = null;
|
|
@@ -3283,7 +3278,7 @@ function resolveTheme(input) {
|
|
|
3283
3278
|
const red = sigScales.get("red");
|
|
3284
3279
|
const primaryRed = mergedOverrides.find((o) => o.name === "red");
|
|
3285
3280
|
if (!primaryRed) {
|
|
3286
|
-
const v2 = redComplementVariant(secScale.cta,
|
|
3281
|
+
const v2 = redComplementVariant(secScale.cta, false, red, cp2);
|
|
3287
3282
|
if (v2 && redCleanBeside(primary.scale.cta, v2.scale.cta)) adopt("red", v2.scale, `${v2.note} (for the secondary)`);
|
|
3288
3283
|
}
|
|
3289
3284
|
for (const sigName of ["yellow", "green", "blue"]) {
|
|
@@ -66,11 +66,11 @@ export declare const RED_SOLVE: {
|
|
|
66
66
|
readonly brickCoolGold: -3;
|
|
67
67
|
readonly brickDesat: 0.85;
|
|
68
68
|
readonly brickExtraDeep: 0.02;
|
|
69
|
-
readonly
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
readonly setHues: {
|
|
70
|
+
readonly true: 33.3;
|
|
71
|
+
readonly cool: 24;
|
|
72
|
+
readonly 'gold-side': 38;
|
|
73
|
+
};
|
|
74
74
|
};
|
|
75
75
|
export declare function redSolveDist(c: {
|
|
76
76
|
L: number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type GeneratedScale, type ContrastProfile, type ColorStop } from './colorEngine';
|
|
2
|
-
import type
|
|
2
|
+
import { type Archetype } from './archetypes';
|
|
3
3
|
import { type SignalDef } from './signals';
|
|
4
4
|
type SignalScales = Map<SignalDef['name'], {
|
|
5
5
|
def: SignalDef;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "okchroma",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Color-system engine: one brand hex in, a light and dark set of color primitives out, with the contrast requirements solved during generation; emitted as CSS custom properties, DTCG documents, and Figma variables",
|
|
6
6
|
"repository": {
|