@odla-ai/brand 0.1.0 → 0.2.1
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/README.md +11 -2
- package/dist/chunk-APLECQBR.js +1010 -0
- package/dist/chunk-APLECQBR.js.map +1 -0
- package/dist/index-bj73h3qo.d.cts +409 -0
- package/dist/index-bj73h3qo.d.ts +409 -0
- package/dist/index.cjs +11 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -409
- package/dist/index.d.ts +5 -409
- package/dist/index.js +65 -958
- package/dist/index.js.map +1 -1
- package/dist/tokens/index.cjs +912 -0
- package/dist/tokens/index.cjs.map +1 -0
- package/dist/tokens/index.d.cts +1 -0
- package/dist/tokens/index.d.ts +1 -0
- package/dist/tokens/index.js +29 -0
- package/dist/tokens/index.js.map +1 -0
- package/package.json +6 -1
|
@@ -0,0 +1,1010 @@
|
|
|
1
|
+
// src/tokens/roles.ts
|
|
2
|
+
var BRAND_REQUIRED_TOKENS = [
|
|
3
|
+
"--ui-bg",
|
|
4
|
+
"--ui-surface",
|
|
5
|
+
"--ui-surface-2",
|
|
6
|
+
"--ui-text",
|
|
7
|
+
"--ui-text-muted",
|
|
8
|
+
"--ui-text-faint",
|
|
9
|
+
"--ui-border",
|
|
10
|
+
"--ui-border-strong",
|
|
11
|
+
"--ui-accent",
|
|
12
|
+
"--ui-accent-strong",
|
|
13
|
+
"--ui-accent-soft",
|
|
14
|
+
"--ui-on-accent",
|
|
15
|
+
"--ui-good",
|
|
16
|
+
"--ui-good-soft",
|
|
17
|
+
"--ui-warn",
|
|
18
|
+
"--ui-warn-soft",
|
|
19
|
+
"--ui-danger",
|
|
20
|
+
"--ui-danger-soft",
|
|
21
|
+
"--ui-code-bg",
|
|
22
|
+
"--ui-code-text",
|
|
23
|
+
"--ui-shadow",
|
|
24
|
+
"--ui-font-sans",
|
|
25
|
+
"--ui-font-serif",
|
|
26
|
+
"--ui-font-mono",
|
|
27
|
+
"--ui-font-display"
|
|
28
|
+
];
|
|
29
|
+
var BRAND_DERIVED_TOKENS = [
|
|
30
|
+
"--ui-accent-glow",
|
|
31
|
+
"--ui-accent-2",
|
|
32
|
+
"--ui-accent-2-soft",
|
|
33
|
+
"--ui-highlight",
|
|
34
|
+
"--ui-focus",
|
|
35
|
+
"--ui-shadow-strong"
|
|
36
|
+
];
|
|
37
|
+
var BRAND_CHART_TOKENS = [
|
|
38
|
+
"--ui-chart-1",
|
|
39
|
+
"--ui-chart-2",
|
|
40
|
+
"--ui-chart-3",
|
|
41
|
+
"--ui-chart-4",
|
|
42
|
+
"--ui-chart-5",
|
|
43
|
+
"--ui-chart-6",
|
|
44
|
+
"--ui-chart-band",
|
|
45
|
+
"--ui-chart-band-strong",
|
|
46
|
+
"--ui-chart-flow",
|
|
47
|
+
"--ui-chart-glow"
|
|
48
|
+
];
|
|
49
|
+
var BRAND_CHAT_TOKENS = [
|
|
50
|
+
"--ui-chat-user-bg",
|
|
51
|
+
"--ui-chat-user-text",
|
|
52
|
+
"--ui-chat-assistant-bg",
|
|
53
|
+
"--ui-chat-thinking-bg",
|
|
54
|
+
"--ui-chat-thinking-text",
|
|
55
|
+
"--ui-chat-tool-accent"
|
|
56
|
+
];
|
|
57
|
+
var BRAND_EMITTED_TOKENS = [
|
|
58
|
+
...BRAND_REQUIRED_TOKENS,
|
|
59
|
+
...BRAND_DERIVED_TOKENS,
|
|
60
|
+
...BRAND_CHART_TOKENS,
|
|
61
|
+
...BRAND_CHAT_TOKENS
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
// src/color/hex.ts
|
|
65
|
+
function clamp01(x) {
|
|
66
|
+
return Number.isNaN(x) ? 0 : x < 0 ? 0 : x > 1 ? 1 : x;
|
|
67
|
+
}
|
|
68
|
+
var HEX3 = /^#[0-9a-f]{3}$/;
|
|
69
|
+
var HEX6 = /^#[0-9a-f]{6}$/;
|
|
70
|
+
var HEX_ALPHA = /^#([0-9a-f]{4}|[0-9a-f]{8})$/;
|
|
71
|
+
function parseHex(hex) {
|
|
72
|
+
const s = hex.trim().toLowerCase();
|
|
73
|
+
if (HEX_ALPHA.test(s)) {
|
|
74
|
+
throw new RangeError(
|
|
75
|
+
`parseHex: alpha hex "${hex}" is not supported \u2014 use an opaque "#rrggbb" value`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (HEX3.test(s)) {
|
|
79
|
+
return {
|
|
80
|
+
r: parseInt(s[1] + s[1], 16) / 255,
|
|
81
|
+
g: parseInt(s[2] + s[2], 16) / 255,
|
|
82
|
+
b: parseInt(s[3] + s[3], 16) / 255
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (HEX6.test(s)) {
|
|
86
|
+
return {
|
|
87
|
+
r: parseInt(s.slice(1, 3), 16) / 255,
|
|
88
|
+
g: parseInt(s.slice(3, 5), 16) / 255,
|
|
89
|
+
b: parseInt(s.slice(5, 7), 16) / 255
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
throw new RangeError(`parseHex: expected "#rgb" or "#rrggbb", got "${hex}"`);
|
|
93
|
+
}
|
|
94
|
+
function toHex(rgb) {
|
|
95
|
+
const ch = (c) => Math.round(clamp01(c) * 255).toString(16).padStart(2, "0");
|
|
96
|
+
return `#${ch(rgb.r)}${ch(rgb.g)}${ch(rgb.b)}`;
|
|
97
|
+
}
|
|
98
|
+
function normalizeHex(hex) {
|
|
99
|
+
return toHex(parseHex(hex));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/color/convert.ts
|
|
103
|
+
function srgbToLinear(c) {
|
|
104
|
+
return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
105
|
+
}
|
|
106
|
+
function linearToSrgb(c) {
|
|
107
|
+
return c <= 31308e-7 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055;
|
|
108
|
+
}
|
|
109
|
+
function rgbToHsl({ r, g, b }) {
|
|
110
|
+
const max = Math.max(r, g, b);
|
|
111
|
+
const min = Math.min(r, g, b);
|
|
112
|
+
const l = (max + min) / 2;
|
|
113
|
+
const d = max - min;
|
|
114
|
+
if (d === 0) return { h: 0, s: 0, l };
|
|
115
|
+
const s = d / (1 - Math.abs(2 * l - 1));
|
|
116
|
+
let h;
|
|
117
|
+
if (max === r) h = 60 * ((g - b) / d % 6);
|
|
118
|
+
else if (max === g) h = 60 * ((b - r) / d + 2);
|
|
119
|
+
else h = 60 * ((r - g) / d + 4);
|
|
120
|
+
return { h: (h + 360) % 360, s, l };
|
|
121
|
+
}
|
|
122
|
+
function hslToRgb({ h, s, l }) {
|
|
123
|
+
const hue = (h % 360 + 360) % 360;
|
|
124
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
125
|
+
const x = c * (1 - Math.abs(hue / 60 % 2 - 1));
|
|
126
|
+
const m = l - c / 2;
|
|
127
|
+
const sextants = [
|
|
128
|
+
[c, x, 0],
|
|
129
|
+
[x, c, 0],
|
|
130
|
+
[0, c, x],
|
|
131
|
+
[0, x, c],
|
|
132
|
+
[x, 0, c],
|
|
133
|
+
[c, 0, x]
|
|
134
|
+
];
|
|
135
|
+
const [r, g, b] = sextants[Math.floor(hue / 60) % 6];
|
|
136
|
+
return { r: r + m, g: g + m, b: b + m };
|
|
137
|
+
}
|
|
138
|
+
function rgbToOklab({ r, g, b }) {
|
|
139
|
+
const R = srgbToLinear(r);
|
|
140
|
+
const G = srgbToLinear(g);
|
|
141
|
+
const B = srgbToLinear(b);
|
|
142
|
+
const l = Math.cbrt(0.4122214708 * R + 0.5363325363 * G + 0.0514459929 * B);
|
|
143
|
+
const m = Math.cbrt(0.2119034982 * R + 0.6806995451 * G + 0.1073969566 * B);
|
|
144
|
+
const s = Math.cbrt(0.0883024619 * R + 0.2817188376 * G + 0.6299787005 * B);
|
|
145
|
+
return {
|
|
146
|
+
L: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
|
|
147
|
+
a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
|
|
148
|
+
b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function oklabToRgb({ L, a, b }) {
|
|
152
|
+
const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;
|
|
153
|
+
const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;
|
|
154
|
+
const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;
|
|
155
|
+
return {
|
|
156
|
+
r: linearToSrgb(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),
|
|
157
|
+
g: linearToSrgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),
|
|
158
|
+
b: linearToSrgb(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s)
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function oklabToOklch({ L, a, b }) {
|
|
162
|
+
const C = Math.hypot(a, b);
|
|
163
|
+
const h = C < 1e-9 ? 0 : (Math.atan2(b, a) * 180 / Math.PI + 360) % 360;
|
|
164
|
+
return { L, C, h };
|
|
165
|
+
}
|
|
166
|
+
function oklchToOklab({ L, C, h }) {
|
|
167
|
+
const rad = h * Math.PI / 180;
|
|
168
|
+
return { L, a: C * Math.cos(rad), b: C * Math.sin(rad) };
|
|
169
|
+
}
|
|
170
|
+
function hexToOklch(hex) {
|
|
171
|
+
return oklabToOklch(rgbToOklab(parseHex(hex)));
|
|
172
|
+
}
|
|
173
|
+
function oklchToHex(lch) {
|
|
174
|
+
return toHex(oklabToRgb(oklchToOklab(lch)));
|
|
175
|
+
}
|
|
176
|
+
var GAMUT_EPS = 1e-6;
|
|
177
|
+
function inSrgbGamut({ r, g, b }) {
|
|
178
|
+
const ok = (c) => c >= -GAMUT_EPS && c <= 1 + GAMUT_EPS;
|
|
179
|
+
return ok(r) && ok(g) && ok(b);
|
|
180
|
+
}
|
|
181
|
+
function clampToGamut(lch) {
|
|
182
|
+
const L = clamp01(lch.L);
|
|
183
|
+
const h = lch.h;
|
|
184
|
+
if (inSrgbGamut(oklabToRgb(oklchToOklab({ L, C: lch.C, h })))) return { L, C: lch.C, h };
|
|
185
|
+
let lo = 0;
|
|
186
|
+
let hi = lch.C;
|
|
187
|
+
for (let i = 0; i < 24; i++) {
|
|
188
|
+
const mid = (lo + hi) / 2;
|
|
189
|
+
if (inSrgbGamut(oklabToRgb(oklchToOklab({ L, C: mid, h })))) lo = mid;
|
|
190
|
+
else hi = mid;
|
|
191
|
+
}
|
|
192
|
+
return { L, C: lo, h };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/color/contrast.ts
|
|
196
|
+
function relativeLuminance(hex) {
|
|
197
|
+
const { r, g, b } = parseHex(hex);
|
|
198
|
+
return 0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);
|
|
199
|
+
}
|
|
200
|
+
function contrastRatio(hexA, hexB) {
|
|
201
|
+
const la = relativeLuminance(hexA);
|
|
202
|
+
const lb = relativeLuminance(hexB);
|
|
203
|
+
return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
|
|
204
|
+
}
|
|
205
|
+
var AA = { text: 4.5, "large-text": 3, ui: 3 };
|
|
206
|
+
var AAA = { text: 7, "large-text": 4.5 };
|
|
207
|
+
function meetsAA(ratio, kind = "text") {
|
|
208
|
+
return ratio >= AA[kind];
|
|
209
|
+
}
|
|
210
|
+
function meetsAAA(ratio, kind = "text") {
|
|
211
|
+
return ratio >= AAA[kind];
|
|
212
|
+
}
|
|
213
|
+
var PICK_TEXT_DEFAULT_CANDIDATES = ["#ffffff", "#050505"];
|
|
214
|
+
function pickTextOn(bgHex, candidates = PICK_TEXT_DEFAULT_CANDIDATES) {
|
|
215
|
+
if (candidates.length === 0) throw new RangeError("pickTextOn: candidates must be non-empty");
|
|
216
|
+
let best = candidates[0];
|
|
217
|
+
let bestRatio = -1;
|
|
218
|
+
for (const candidate of candidates) {
|
|
219
|
+
const ratio = contrastRatio(bgHex, candidate);
|
|
220
|
+
if (ratio > bestRatio) {
|
|
221
|
+
best = candidate;
|
|
222
|
+
bestRatio = ratio;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return best;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/color/harmony.ts
|
|
229
|
+
function rotateHue(hex, degrees) {
|
|
230
|
+
const { L, C, h } = hexToOklch(hex);
|
|
231
|
+
return oklchToHex(clampToGamut({ L, C, h: ((h + degrees) % 360 + 360) % 360 }));
|
|
232
|
+
}
|
|
233
|
+
function complementary(hex) {
|
|
234
|
+
return rotateHue(hex, 180);
|
|
235
|
+
}
|
|
236
|
+
function analogous(hex, angle = 30) {
|
|
237
|
+
return [rotateHue(hex, -angle), rotateHue(hex, angle)];
|
|
238
|
+
}
|
|
239
|
+
function triadic(hex) {
|
|
240
|
+
return [rotateHue(hex, -120), rotateHue(hex, 120)];
|
|
241
|
+
}
|
|
242
|
+
function splitComplementary(hex) {
|
|
243
|
+
return [rotateHue(hex, -150), rotateHue(hex, 150)];
|
|
244
|
+
}
|
|
245
|
+
function tetradic(hex) {
|
|
246
|
+
return [rotateHue(hex, 90), rotateHue(hex, 180), rotateHue(hex, 270)];
|
|
247
|
+
}
|
|
248
|
+
function monochrome(hex, steps = 5) {
|
|
249
|
+
if (!Number.isInteger(steps) || steps < 2) {
|
|
250
|
+
throw new RangeError(`monochrome: steps must be an integer >= 2, got ${steps}`);
|
|
251
|
+
}
|
|
252
|
+
const { L, C, h } = hexToOklch(hex);
|
|
253
|
+
const out = [];
|
|
254
|
+
for (let i = 0; i < steps; i++) {
|
|
255
|
+
out.push(oklchToHex(clampToGamut({ L, C: C * (1 - i / (steps - 1)), h })));
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/color/ramp.ts
|
|
261
|
+
var RAMP_L_MAX = 0.96;
|
|
262
|
+
var RAMP_L_MIN = 0.27;
|
|
263
|
+
var CHROMA_FLOOR = 0.25;
|
|
264
|
+
function tintShadeRamp(hex, steps = 9) {
|
|
265
|
+
if (!Number.isInteger(steps) || steps < 2) {
|
|
266
|
+
throw new RangeError(`tintShadeRamp: steps must be an integer >= 2, got ${steps}`);
|
|
267
|
+
}
|
|
268
|
+
const { C, h } = hexToOklch(hex);
|
|
269
|
+
const out = [];
|
|
270
|
+
for (let i = 0; i < steps; i++) {
|
|
271
|
+
const t = i / (steps - 1);
|
|
272
|
+
const L = RAMP_L_MAX + (RAMP_L_MIN - RAMP_L_MAX) * t;
|
|
273
|
+
const taper = CHROMA_FLOOR + (1 - CHROMA_FLOOR) * (1 - Math.abs(2 * t - 1));
|
|
274
|
+
out.push(oklchToHex(clampToGamut({ L, C: C * taper, h })));
|
|
275
|
+
}
|
|
276
|
+
return out;
|
|
277
|
+
}
|
|
278
|
+
var SCAN_STEPS = 16;
|
|
279
|
+
var BISECT_STEPS = 22;
|
|
280
|
+
function adjustLightnessUntil(hex, predicate, direction) {
|
|
281
|
+
const start = normalizeHex(hex);
|
|
282
|
+
if (predicate(start)) return start;
|
|
283
|
+
const { L, C, h } = hexToOklch(start);
|
|
284
|
+
const bound = direction === "lighten" ? 1 : 0;
|
|
285
|
+
const at = (l) => oklchToHex(clampToGamut({ L: l, C, h }));
|
|
286
|
+
let lastFail = L;
|
|
287
|
+
let firstPass = Number.NaN;
|
|
288
|
+
for (let i = 1; i <= SCAN_STEPS; i++) {
|
|
289
|
+
const l = L + (bound - L) * i / SCAN_STEPS;
|
|
290
|
+
if (predicate(at(l))) {
|
|
291
|
+
firstPass = l;
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
lastFail = l;
|
|
295
|
+
}
|
|
296
|
+
if (Number.isNaN(firstPass)) return null;
|
|
297
|
+
for (let i = 0; i < BISECT_STEPS; i++) {
|
|
298
|
+
const mid = (lastFail + firstPass) / 2;
|
|
299
|
+
if (predicate(at(mid))) firstPass = mid;
|
|
300
|
+
else lastFail = mid;
|
|
301
|
+
}
|
|
302
|
+
return at(firstPass);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/color/delta.ts
|
|
306
|
+
function deltaEOKLab(x, y) {
|
|
307
|
+
return Math.hypot(x.L - y.L, x.a - y.a, x.b - y.b);
|
|
308
|
+
}
|
|
309
|
+
function deltaEOK(hexA, hexB) {
|
|
310
|
+
return deltaEOKLab(rgbToOklab(parseHex(hexA)), rgbToOklab(parseHex(hexB)));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/color/names.ts
|
|
314
|
+
var TABLE = [
|
|
315
|
+
["aliceblue", "#f0f8ff"],
|
|
316
|
+
["antiquewhite", "#faebd7"],
|
|
317
|
+
["aqua", "#00ffff"],
|
|
318
|
+
["aquamarine", "#7fffd4"],
|
|
319
|
+
["azure", "#f0ffff"],
|
|
320
|
+
["beige", "#f5f5dc"],
|
|
321
|
+
["bisque", "#ffe4c4"],
|
|
322
|
+
["black", "#000000"],
|
|
323
|
+
["blanchedalmond", "#ffebcd"],
|
|
324
|
+
["blue", "#0000ff"],
|
|
325
|
+
["blueviolet", "#8a2be2"],
|
|
326
|
+
["brown", "#a52a2a"],
|
|
327
|
+
["burlywood", "#deb887"],
|
|
328
|
+
["cadetblue", "#5f9ea0"],
|
|
329
|
+
["chartreuse", "#7fff00"],
|
|
330
|
+
["chocolate", "#d2691e"],
|
|
331
|
+
["coral", "#ff7f50"],
|
|
332
|
+
["cornflowerblue", "#6495ed"],
|
|
333
|
+
["cornsilk", "#fff8dc"],
|
|
334
|
+
["crimson", "#dc143c"],
|
|
335
|
+
["cyan", "#00ffff"],
|
|
336
|
+
["darkblue", "#00008b"],
|
|
337
|
+
["darkcyan", "#008b8b"],
|
|
338
|
+
["darkgoldenrod", "#b8860b"],
|
|
339
|
+
["darkgray", "#a9a9a9"],
|
|
340
|
+
["darkgreen", "#006400"],
|
|
341
|
+
["darkgrey", "#a9a9a9"],
|
|
342
|
+
["darkkhaki", "#bdb76b"],
|
|
343
|
+
["darkmagenta", "#8b008b"],
|
|
344
|
+
["darkolivegreen", "#556b2f"],
|
|
345
|
+
["darkorange", "#ff8c00"],
|
|
346
|
+
["darkorchid", "#9932cc"],
|
|
347
|
+
["darkred", "#8b0000"],
|
|
348
|
+
["darksalmon", "#e9967a"],
|
|
349
|
+
["darkseagreen", "#8fbc8f"],
|
|
350
|
+
["darkslateblue", "#483d8b"],
|
|
351
|
+
["darkslategray", "#2f4f4f"],
|
|
352
|
+
["darkslategrey", "#2f4f4f"],
|
|
353
|
+
["darkturquoise", "#00ced1"],
|
|
354
|
+
["darkviolet", "#9400d3"],
|
|
355
|
+
["deeppink", "#ff1493"],
|
|
356
|
+
["deepskyblue", "#00bfff"],
|
|
357
|
+
["dimgray", "#696969"],
|
|
358
|
+
["dimgrey", "#696969"],
|
|
359
|
+
["dodgerblue", "#1e90ff"],
|
|
360
|
+
["firebrick", "#b22222"],
|
|
361
|
+
["floralwhite", "#fffaf0"],
|
|
362
|
+
["forestgreen", "#228b22"],
|
|
363
|
+
["fuchsia", "#ff00ff"],
|
|
364
|
+
["gainsboro", "#dcdcdc"],
|
|
365
|
+
["ghostwhite", "#f8f8ff"],
|
|
366
|
+
["gold", "#ffd700"],
|
|
367
|
+
["goldenrod", "#daa520"],
|
|
368
|
+
["gray", "#808080"],
|
|
369
|
+
["green", "#008000"],
|
|
370
|
+
["greenyellow", "#adff2f"],
|
|
371
|
+
["grey", "#808080"],
|
|
372
|
+
["honeydew", "#f0fff0"],
|
|
373
|
+
["hotpink", "#ff69b4"],
|
|
374
|
+
["indianred", "#cd5c5c"],
|
|
375
|
+
["indigo", "#4b0082"],
|
|
376
|
+
["ivory", "#fffff0"],
|
|
377
|
+
["khaki", "#f0e68c"],
|
|
378
|
+
["lavender", "#e6e6fa"],
|
|
379
|
+
["lavenderblush", "#fff0f5"],
|
|
380
|
+
["lawngreen", "#7cfc00"],
|
|
381
|
+
["lemonchiffon", "#fffacd"],
|
|
382
|
+
["lightblue", "#add8e6"],
|
|
383
|
+
["lightcoral", "#f08080"],
|
|
384
|
+
["lightcyan", "#e0ffff"],
|
|
385
|
+
["lightgoldenrodyellow", "#fafad2"],
|
|
386
|
+
["lightgray", "#d3d3d3"],
|
|
387
|
+
["lightgreen", "#90ee90"],
|
|
388
|
+
["lightgrey", "#d3d3d3"],
|
|
389
|
+
["lightpink", "#ffb6c1"],
|
|
390
|
+
["lightsalmon", "#ffa07a"],
|
|
391
|
+
["lightseagreen", "#20b2aa"],
|
|
392
|
+
["lightskyblue", "#87cefa"],
|
|
393
|
+
["lightslategray", "#778899"],
|
|
394
|
+
["lightslategrey", "#778899"],
|
|
395
|
+
["lightsteelblue", "#b0c4de"],
|
|
396
|
+
["lightyellow", "#ffffe0"],
|
|
397
|
+
["lime", "#00ff00"],
|
|
398
|
+
["limegreen", "#32cd32"],
|
|
399
|
+
["linen", "#faf0e6"],
|
|
400
|
+
["magenta", "#ff00ff"],
|
|
401
|
+
["maroon", "#800000"],
|
|
402
|
+
["mediumaquamarine", "#66cdaa"],
|
|
403
|
+
["mediumblue", "#0000cd"],
|
|
404
|
+
["mediumorchid", "#ba55d3"],
|
|
405
|
+
["mediumpurple", "#9370db"],
|
|
406
|
+
["mediumseagreen", "#3cb371"],
|
|
407
|
+
["mediumslateblue", "#7b68ee"],
|
|
408
|
+
["mediumspringgreen", "#00fa9a"],
|
|
409
|
+
["mediumturquoise", "#48d1cc"],
|
|
410
|
+
["mediumvioletred", "#c71585"],
|
|
411
|
+
["midnightblue", "#191970"],
|
|
412
|
+
["mintcream", "#f5fffa"],
|
|
413
|
+
["mistyrose", "#ffe4e1"],
|
|
414
|
+
["moccasin", "#ffe4b5"],
|
|
415
|
+
["navajowhite", "#ffdead"],
|
|
416
|
+
["navy", "#000080"],
|
|
417
|
+
["oldlace", "#fdf5e6"],
|
|
418
|
+
["olive", "#808000"],
|
|
419
|
+
["olivedrab", "#6b8e23"],
|
|
420
|
+
["orange", "#ffa500"],
|
|
421
|
+
["orangered", "#ff4500"],
|
|
422
|
+
["orchid", "#da70d6"],
|
|
423
|
+
["palegoldenrod", "#eee8aa"],
|
|
424
|
+
["palegreen", "#98fb98"],
|
|
425
|
+
["paleturquoise", "#afeeee"],
|
|
426
|
+
["palevioletred", "#db7093"],
|
|
427
|
+
["papayawhip", "#ffefd5"],
|
|
428
|
+
["peachpuff", "#ffdab9"],
|
|
429
|
+
["peru", "#cd853f"],
|
|
430
|
+
["pink", "#ffc0cb"],
|
|
431
|
+
["plum", "#dda0dd"],
|
|
432
|
+
["powderblue", "#b0e0e6"],
|
|
433
|
+
["purple", "#800080"],
|
|
434
|
+
["rebeccapurple", "#663399"],
|
|
435
|
+
["red", "#ff0000"],
|
|
436
|
+
["rosybrown", "#bc8f8f"],
|
|
437
|
+
["royalblue", "#4169e1"],
|
|
438
|
+
["saddlebrown", "#8b4513"],
|
|
439
|
+
["salmon", "#fa8072"],
|
|
440
|
+
["sandybrown", "#f4a460"],
|
|
441
|
+
["seagreen", "#2e8b57"],
|
|
442
|
+
["seashell", "#fff5ee"],
|
|
443
|
+
["sienna", "#a0522d"],
|
|
444
|
+
["silver", "#c0c0c0"],
|
|
445
|
+
["skyblue", "#87ceeb"],
|
|
446
|
+
["slateblue", "#6a5acd"],
|
|
447
|
+
["slategray", "#708090"],
|
|
448
|
+
["slategrey", "#708090"],
|
|
449
|
+
["snow", "#fffafa"],
|
|
450
|
+
["springgreen", "#00ff7f"],
|
|
451
|
+
["steelblue", "#4682b4"],
|
|
452
|
+
["tan", "#d2b48c"],
|
|
453
|
+
["teal", "#008080"],
|
|
454
|
+
["thistle", "#d8bfd8"],
|
|
455
|
+
["tomato", "#ff6347"],
|
|
456
|
+
["turquoise", "#40e0d0"],
|
|
457
|
+
["violet", "#ee82ee"],
|
|
458
|
+
["wheat", "#f5deb3"],
|
|
459
|
+
["white", "#ffffff"],
|
|
460
|
+
["whitesmoke", "#f5f5f5"],
|
|
461
|
+
["yellow", "#ffff00"],
|
|
462
|
+
["yellowgreen", "#9acd32"]
|
|
463
|
+
];
|
|
464
|
+
var CSS_NAMED_COLORS = TABLE.map(([name, hex]) => ({
|
|
465
|
+
name,
|
|
466
|
+
hex
|
|
467
|
+
}));
|
|
468
|
+
var labCache = null;
|
|
469
|
+
function nearestNamedColor(hex) {
|
|
470
|
+
const target = rgbToOklab(parseHex(hex));
|
|
471
|
+
labCache ??= TABLE.map(([, value2]) => rgbToOklab(parseHex(value2)));
|
|
472
|
+
let bestIdx = 0;
|
|
473
|
+
let bestD = Infinity;
|
|
474
|
+
for (let i = 0; i < labCache.length; i++) {
|
|
475
|
+
const d = deltaEOKLab(target, labCache[i]);
|
|
476
|
+
if (d < bestD) {
|
|
477
|
+
bestD = d;
|
|
478
|
+
bestIdx = i;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const [name, value] = TABLE[bestIdx];
|
|
482
|
+
return { name, hex: value, deltaEOK: bestD };
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// src/color/palette.ts
|
|
486
|
+
var CHART_DELTA_MIN = 0.1;
|
|
487
|
+
var CHART_L = 0.7;
|
|
488
|
+
var CHART_C = 0.115;
|
|
489
|
+
var ANCHOR_HUE = 250;
|
|
490
|
+
var ACHROMATIC_C = 0.02;
|
|
491
|
+
var STATUS_HUES = { good: 145, warn: 85, danger: 25 };
|
|
492
|
+
var STATUS_L = 0.7;
|
|
493
|
+
var STATUS_C = 0.14;
|
|
494
|
+
var ROTATIONS = [180, 120, -120, 150, -150];
|
|
495
|
+
function deriveChartColors(seedHex, opts = {}) {
|
|
496
|
+
const count = opts.count ?? 6;
|
|
497
|
+
const deltaMin = opts.deltaMin ?? CHART_DELTA_MIN;
|
|
498
|
+
if (!Number.isInteger(count) || count < 1 || count > 12) {
|
|
499
|
+
throw new RangeError(`deriveChartColors: count must be an integer in 1..12, got ${count}`);
|
|
500
|
+
}
|
|
501
|
+
const lch = hexToOklch(normalizeHex(seedHex));
|
|
502
|
+
const base = lch.C < ACHROMATIC_C ? ANCHOR_HUE : lch.h;
|
|
503
|
+
const offsets = [0, 60, 120, 180, 240, 300, 30, 90, 150, 210, 270, 330];
|
|
504
|
+
const candidates = offsets.map(
|
|
505
|
+
(deg) => oklchToHex(clampToGamut({ L: CHART_L, C: CHART_C, h: (base + deg) % 360 }))
|
|
506
|
+
);
|
|
507
|
+
const picked = [];
|
|
508
|
+
for (const hex of candidates) {
|
|
509
|
+
if (picked.length >= count) break;
|
|
510
|
+
if (picked.includes(hex)) continue;
|
|
511
|
+
if (picked.every((p) => deltaEOK(p, hex) >= deltaMin)) picked.push(hex);
|
|
512
|
+
}
|
|
513
|
+
for (const hex of candidates) {
|
|
514
|
+
if (picked.length >= count) break;
|
|
515
|
+
if (!picked.includes(hex)) picked.push(hex);
|
|
516
|
+
}
|
|
517
|
+
return picked;
|
|
518
|
+
}
|
|
519
|
+
function derivePalette(seedHex, opts = {}) {
|
|
520
|
+
const withNames = opts.names !== false;
|
|
521
|
+
const seed = normalizeHex(seedHex);
|
|
522
|
+
const seedLch = hexToOklch(seed);
|
|
523
|
+
const achromatic = seedLch.C < ACHROMATIC_C;
|
|
524
|
+
const hue = achromatic ? ANCHOR_HUE : seedLch.h;
|
|
525
|
+
const neutralAt = (L, C) => oklchToHex(clampToGamut({ L, C, h: hue }));
|
|
526
|
+
const bg = neutralAt(0.985, 5e-3);
|
|
527
|
+
const surface = neutralAt(0.955, 8e-3);
|
|
528
|
+
const text = neutralAt(0.24, 0.012);
|
|
529
|
+
const neutral = neutralAt(0.62, 0.012);
|
|
530
|
+
const accentBase = achromatic ? oklchToHex(
|
|
531
|
+
clampToGamut({ L: Math.min(Math.max(seedLch.L, 0.45), 0.75), C: CHART_C, h: ANCHOR_HUE })
|
|
532
|
+
) : seed;
|
|
533
|
+
const cands = ROTATIONS.map((deg) => {
|
|
534
|
+
const hex = rotateHue(accentBase, deg);
|
|
535
|
+
return { deg, hex, d: deltaEOK(seed, hex) };
|
|
536
|
+
});
|
|
537
|
+
let secondary = cands[0];
|
|
538
|
+
for (const c of cands) if (c.d > secondary.d) secondary = c;
|
|
539
|
+
let highlight = cands[0] === secondary ? cands[1] : cands[0];
|
|
540
|
+
let hiScore = Math.min(highlight.d, deltaEOK(highlight.hex, secondary.hex));
|
|
541
|
+
for (const c of cands) {
|
|
542
|
+
if (c === secondary || c === highlight) continue;
|
|
543
|
+
const score = Math.min(c.d, deltaEOK(c.hex, secondary.hex));
|
|
544
|
+
if (score > hiScore) {
|
|
545
|
+
highlight = c;
|
|
546
|
+
hiScore = score;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
const status = (anchor) => {
|
|
550
|
+
const start = oklchToHex(clampToGamut({ L: STATUS_L, C: STATUS_C, h: anchor }));
|
|
551
|
+
return adjustLightnessUntil(start, (c) => contrastRatio(c, bg) >= 3, "darken") ?? text;
|
|
552
|
+
};
|
|
553
|
+
const sw = (role, hex, rationale) => ({
|
|
554
|
+
role,
|
|
555
|
+
hex,
|
|
556
|
+
rationale
|
|
557
|
+
});
|
|
558
|
+
const swatches = [
|
|
559
|
+
sw("primary", seed, "seed color"),
|
|
560
|
+
sw("secondary", secondary.hex, `harmony rotation ${secondary.deg}\xB0, \u0394EOK ${secondary.d.toFixed(3)} from primary`),
|
|
561
|
+
sw("highlight", highlight.hex, `harmony rotation ${highlight.deg}\xB0, spread from primary and secondary`),
|
|
562
|
+
sw("good", status(STATUS_HUES.good), "green anchor 145\xB0, \u22653:1 on bg"),
|
|
563
|
+
sw("warn", status(STATUS_HUES.warn), "amber anchor 85\xB0, \u22653:1 on bg"),
|
|
564
|
+
sw("danger", status(STATUS_HUES.danger), "red anchor 25\xB0, \u22653:1 on bg"),
|
|
565
|
+
sw("bg", bg, "near-white tinted with the seed hue"),
|
|
566
|
+
sw("surface", surface, "raised surface, one step below bg"),
|
|
567
|
+
sw("text", text, "near-black tinted with the seed hue"),
|
|
568
|
+
sw("neutral", neutral, "mid neutral for borders and muted marks"),
|
|
569
|
+
...deriveChartColors(seed).map((hex, i) => sw("chart", hex, `chart series ${i + 1}`))
|
|
570
|
+
];
|
|
571
|
+
return withNames ? swatches.map((s) => ({ ...s, name: nearestNamedColor(s.hex).name })) : swatches;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/tokens/map.ts
|
|
575
|
+
var DEFAULT_ACCENT_SEED = "#3b5e8c";
|
|
576
|
+
var CHROMATIC_C = 0.02;
|
|
577
|
+
var SANS_STACK = `ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif`;
|
|
578
|
+
var SERIF_STACK = `"Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif`;
|
|
579
|
+
var MONO_STACK = `ui-monospace, "SF Mono", Menlo, Consolas, monospace`;
|
|
580
|
+
var DISPLAY_STACK = `ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif`;
|
|
581
|
+
var UI_CHART_DEFAULTS = [
|
|
582
|
+
"var(--ui-accent)",
|
|
583
|
+
"var(--ui-good)",
|
|
584
|
+
"var(--ui-warn)",
|
|
585
|
+
"var(--ui-danger)",
|
|
586
|
+
"color-mix(in srgb, var(--ui-accent) 45%, var(--ui-text))",
|
|
587
|
+
"var(--ui-text-faint)"
|
|
588
|
+
];
|
|
589
|
+
var ROLE_TOKEN = {
|
|
590
|
+
primary: "--ui-accent",
|
|
591
|
+
secondary: "--ui-accent-2",
|
|
592
|
+
highlight: "--ui-highlight",
|
|
593
|
+
bg: "--ui-bg",
|
|
594
|
+
surface: "--ui-surface",
|
|
595
|
+
text: "--ui-text",
|
|
596
|
+
neutral: "--ui-border-strong",
|
|
597
|
+
good: "--ui-good",
|
|
598
|
+
warn: "--ui-warn",
|
|
599
|
+
danger: "--ui-danger",
|
|
600
|
+
chart: "--ui-chart-1"
|
|
601
|
+
};
|
|
602
|
+
var mixVar = (name, pct) => `color-mix(in srgb, var(${name}) ${pct}%, transparent)`;
|
|
603
|
+
function shiftL(hex, dL) {
|
|
604
|
+
const { L, C, h } = hexToOklch(hex);
|
|
605
|
+
return oklchToHex(clampToGamut({ L: clamp01(L + dL), C, h }));
|
|
606
|
+
}
|
|
607
|
+
function lerpL(fromHex, toHex2, t) {
|
|
608
|
+
const a = hexToOklch(fromHex);
|
|
609
|
+
const b = hexToOklch(toHex2);
|
|
610
|
+
return oklchToHex(clampToGamut({ L: a.L + (b.L - a.L) * t, C: a.C, h: a.h }));
|
|
611
|
+
}
|
|
612
|
+
function ensureOnBg(hex, bg, min) {
|
|
613
|
+
const pred = (c) => contrastRatio(c, bg) >= min;
|
|
614
|
+
const away = relativeLuminance(bg) >= relativeLuminance(hex) ? "darken" : "lighten";
|
|
615
|
+
return adjustLightnessUntil(hex, pred, away) ?? adjustLightnessUntil(hex, pred, away === "darken" ? "lighten" : "darken") ?? pickTextOn(bg);
|
|
616
|
+
}
|
|
617
|
+
var rgbaOf = (hex, alpha) => {
|
|
618
|
+
const { r, g, b } = parseHex(hex);
|
|
619
|
+
const c = (v) => Math.round(v * 255);
|
|
620
|
+
return `rgba(${c(r)}, ${c(g)}, ${c(b)}, ${alpha})`;
|
|
621
|
+
};
|
|
622
|
+
function fontValue(family, stack) {
|
|
623
|
+
const f = family?.trim() ?? "";
|
|
624
|
+
if (f === "") return stack;
|
|
625
|
+
if (f.includes(",") || /^["']/.test(f) || /^[a-zA-Z][a-zA-Z0-9-]*$/.test(f))
|
|
626
|
+
return `${f}, ${stack}`;
|
|
627
|
+
return `"${f.replaceAll('"', "")}", ${stack}`;
|
|
628
|
+
}
|
|
629
|
+
function collect(swatches, warnings) {
|
|
630
|
+
const byRole = {};
|
|
631
|
+
const charts = [];
|
|
632
|
+
if (!Array.isArray(swatches)) {
|
|
633
|
+
warnings.push({ token: "--ui-accent", message: "swatches is not an array; compiling from defaults" });
|
|
634
|
+
return { byRole, charts };
|
|
635
|
+
}
|
|
636
|
+
swatches.forEach((s, i) => {
|
|
637
|
+
let hex;
|
|
638
|
+
try {
|
|
639
|
+
hex = normalizeHex(String(s?.hex));
|
|
640
|
+
} catch {
|
|
641
|
+
warnings.push({
|
|
642
|
+
token: s?.role !== void 0 && ROLE_TOKEN[s.role] || "--ui-accent",
|
|
643
|
+
message: `swatches[${i}] dropped: invalid hex ${String(s?.hex)}`
|
|
644
|
+
});
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
const role = s?.role;
|
|
648
|
+
if (role === "chart") charts.push(hex);
|
|
649
|
+
else if (role !== void 0 && role !== "custom" && byRole[role] === void 0) byRole[role] = hex;
|
|
650
|
+
});
|
|
651
|
+
return { byRole, charts };
|
|
652
|
+
}
|
|
653
|
+
function mapPaletteToTokens(input) {
|
|
654
|
+
const warnings = [];
|
|
655
|
+
const { byRole, charts } = collect(input?.swatches, warnings);
|
|
656
|
+
let accent = byRole.primary;
|
|
657
|
+
if (accent === void 0) {
|
|
658
|
+
const chromatic = [...Object.values(byRole), ...charts].find(
|
|
659
|
+
(h) => h !== void 0 && hexToOklch(h).C >= CHROMATIC_C
|
|
660
|
+
);
|
|
661
|
+
accent = chromatic ?? DEFAULT_ACCENT_SEED;
|
|
662
|
+
warnings.push({
|
|
663
|
+
token: "--ui-accent",
|
|
664
|
+
message: `no primary swatch; using ${chromatic !== void 0 ? "the first chromatic swatch" : "the neutral default seed"} ${accent}`
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
const fb = /* @__PURE__ */ new Map();
|
|
668
|
+
for (const s of derivePalette(accent, { names: false })) if (!fb.has(s.role)) fb.set(s.role, s.hex);
|
|
669
|
+
const pick = (role) => {
|
|
670
|
+
const own = byRole[role];
|
|
671
|
+
if (own !== void 0) return own;
|
|
672
|
+
const derived = fb.get(role);
|
|
673
|
+
warnings.push({ token: ROLE_TOKEN[role], message: `no ${role} swatch; derived ${derived} from ${accent}` });
|
|
674
|
+
return derived;
|
|
675
|
+
};
|
|
676
|
+
const bg = pick("bg");
|
|
677
|
+
const surface = pick("surface");
|
|
678
|
+
const neutral = pick("neutral");
|
|
679
|
+
const surface2 = shiftL(surface, -0.035);
|
|
680
|
+
const rawText = pick("text");
|
|
681
|
+
const text = ensureOnBg(rawText, bg, 4.5);
|
|
682
|
+
if (text !== rawText)
|
|
683
|
+
warnings.push({ token: "--ui-text", message: "adjusted to reach 4.5:1 on --ui-bg", adjustedFrom: rawText });
|
|
684
|
+
const accentStrong = ensureOnBg(accent, bg, 4.5);
|
|
685
|
+
if (accentStrong !== accent)
|
|
686
|
+
warnings.push({
|
|
687
|
+
token: "--ui-accent-strong",
|
|
688
|
+
message: "moved --ui-accent in lightness to reach 4.5:1 on --ui-bg",
|
|
689
|
+
adjustedFrom: accent
|
|
690
|
+
});
|
|
691
|
+
const status = (role) => {
|
|
692
|
+
const own = pick(role);
|
|
693
|
+
const fixed = ensureOnBg(own, bg, 3);
|
|
694
|
+
if (fixed !== own)
|
|
695
|
+
warnings.push({ token: ROLE_TOKEN[role], message: "adjusted to reach 3:1 on --ui-bg", adjustedFrom: own });
|
|
696
|
+
return fixed;
|
|
697
|
+
};
|
|
698
|
+
const chromaticCharts = charts.filter((h) => hexToOklch(h).C >= CHROMATIC_C);
|
|
699
|
+
let chartValues;
|
|
700
|
+
if (chromaticCharts.length >= 3) {
|
|
701
|
+
const picked = chromaticCharts.slice(0, 6);
|
|
702
|
+
for (const c of deriveChartColors(accent, { count: 12 })) {
|
|
703
|
+
if (picked.length >= 6) break;
|
|
704
|
+
if (!picked.includes(c)) picked.push(c);
|
|
705
|
+
}
|
|
706
|
+
chartValues = picked;
|
|
707
|
+
} else {
|
|
708
|
+
warnings.push({
|
|
709
|
+
token: "--ui-chart-1",
|
|
710
|
+
message: `fewer than 3 chromatic chart swatches (${chromaticCharts.length}); using the ui derived chart defaults`
|
|
711
|
+
});
|
|
712
|
+
chartValues = UI_CHART_DEFAULTS;
|
|
713
|
+
}
|
|
714
|
+
const secondary = byRole.secondary;
|
|
715
|
+
if (secondary === void 0)
|
|
716
|
+
warnings.push({ token: "--ui-accent-2", message: "no secondary swatch; --ui-accent-2 collapses onto var(--ui-accent) (ui default)" });
|
|
717
|
+
const highlight = byRole.highlight;
|
|
718
|
+
if (highlight === void 0)
|
|
719
|
+
warnings.push({ token: "--ui-highlight", message: "no highlight swatch; --ui-highlight aliases var(--ui-accent-strong) (ui default)" });
|
|
720
|
+
const t = input?.typography ?? {};
|
|
721
|
+
const tokens = {
|
|
722
|
+
"--ui-bg": bg,
|
|
723
|
+
"--ui-surface": surface,
|
|
724
|
+
"--ui-surface-2": surface2,
|
|
725
|
+
"--ui-text": text,
|
|
726
|
+
"--ui-text-muted": ensureOnBg(lerpL(text, bg, 0.35), bg, 4.5),
|
|
727
|
+
"--ui-text-faint": lerpL(text, bg, 0.58),
|
|
728
|
+
"--ui-border": lerpL(neutral, bg, 0.55),
|
|
729
|
+
"--ui-border-strong": neutral,
|
|
730
|
+
"--ui-accent": accent,
|
|
731
|
+
"--ui-accent-strong": accentStrong,
|
|
732
|
+
"--ui-accent-soft": mixVar("--ui-accent", 10),
|
|
733
|
+
"--ui-on-accent": pickTextOn(accent),
|
|
734
|
+
"--ui-good": status("good"),
|
|
735
|
+
"--ui-good-soft": mixVar("--ui-good", 12),
|
|
736
|
+
"--ui-warn": status("warn"),
|
|
737
|
+
"--ui-warn-soft": mixVar("--ui-warn", 12),
|
|
738
|
+
"--ui-danger": status("danger"),
|
|
739
|
+
"--ui-danger-soft": mixVar("--ui-danger", 10),
|
|
740
|
+
"--ui-code-bg": surface2,
|
|
741
|
+
"--ui-code-text": text,
|
|
742
|
+
"--ui-shadow": `0 1px 2px ${rgbaOf(text, 0.04)}, 0 8px 24px ${rgbaOf(text, 0.06)}`,
|
|
743
|
+
"--ui-font-sans": fontValue(t.fontBody, SANS_STACK),
|
|
744
|
+
"--ui-font-serif": SERIF_STACK,
|
|
745
|
+
"--ui-font-mono": fontValue(t.fontMono, MONO_STACK),
|
|
746
|
+
"--ui-font-display": fontValue(t.fontDisplay ?? t.fontBody, DISPLAY_STACK),
|
|
747
|
+
"--ui-accent-glow": mixVar("--ui-accent", 16),
|
|
748
|
+
"--ui-accent-2": secondary ?? "var(--ui-accent)",
|
|
749
|
+
"--ui-accent-2-soft": mixVar("--ui-accent-2", 10),
|
|
750
|
+
"--ui-highlight": highlight ?? "var(--ui-accent-strong)",
|
|
751
|
+
"--ui-focus": "0 0 0 3px var(--ui-accent-soft)",
|
|
752
|
+
"--ui-shadow-strong": "var(--ui-shadow)",
|
|
753
|
+
"--ui-chart-1": chartValues[0],
|
|
754
|
+
"--ui-chart-2": chartValues[1],
|
|
755
|
+
"--ui-chart-3": chartValues[2],
|
|
756
|
+
"--ui-chart-4": chartValues[3],
|
|
757
|
+
"--ui-chart-5": chartValues[4],
|
|
758
|
+
"--ui-chart-6": chartValues[5],
|
|
759
|
+
"--ui-chart-band": mixVar("--ui-accent", 10),
|
|
760
|
+
"--ui-chart-band-strong": mixVar("--ui-accent", 22),
|
|
761
|
+
"--ui-chart-flow": "var(--ui-accent)",
|
|
762
|
+
"--ui-chart-glow": "var(--ui-accent-strong)",
|
|
763
|
+
"--ui-chat-user-bg": "var(--ui-accent-soft)",
|
|
764
|
+
"--ui-chat-user-text": "var(--ui-text)",
|
|
765
|
+
"--ui-chat-assistant-bg": "var(--ui-surface)",
|
|
766
|
+
"--ui-chat-thinking-bg": "var(--ui-surface-2)",
|
|
767
|
+
"--ui-chat-thinking-text": "var(--ui-text-muted)",
|
|
768
|
+
"--ui-chat-tool-accent": "var(--ui-accent)"
|
|
769
|
+
};
|
|
770
|
+
return { tokens, warnings };
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// src/tokens/dark.ts
|
|
774
|
+
var DARK_FLIP_L_MIN = 0.2;
|
|
775
|
+
var DARK_FLIP_L_MAX = 0.95;
|
|
776
|
+
var DARK_SHADOW = "0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.35)";
|
|
777
|
+
var HEX62 = /^#[0-9a-f]{6}$/;
|
|
778
|
+
var NEUTRAL_FLIP = /* @__PURE__ */ new Set([
|
|
779
|
+
"--ui-bg",
|
|
780
|
+
"--ui-surface",
|
|
781
|
+
"--ui-surface-2",
|
|
782
|
+
"--ui-text",
|
|
783
|
+
"--ui-text-muted",
|
|
784
|
+
"--ui-text-faint",
|
|
785
|
+
"--ui-border",
|
|
786
|
+
"--ui-border-strong",
|
|
787
|
+
"--ui-code-bg",
|
|
788
|
+
"--ui-code-text"
|
|
789
|
+
]);
|
|
790
|
+
var MIN_RUNG = 0.02;
|
|
791
|
+
var ACCENT_AA = [
|
|
792
|
+
"--ui-accent",
|
|
793
|
+
"--ui-accent-strong",
|
|
794
|
+
"--ui-accent-2",
|
|
795
|
+
"--ui-highlight",
|
|
796
|
+
"--ui-good",
|
|
797
|
+
"--ui-warn",
|
|
798
|
+
"--ui-danger"
|
|
799
|
+
];
|
|
800
|
+
var CHART_UI = [
|
|
801
|
+
"--ui-chart-1",
|
|
802
|
+
"--ui-chart-2",
|
|
803
|
+
"--ui-chart-3",
|
|
804
|
+
"--ui-chart-4",
|
|
805
|
+
"--ui-chart-5",
|
|
806
|
+
"--ui-chart-6"
|
|
807
|
+
];
|
|
808
|
+
function flipNeutral(hex) {
|
|
809
|
+
const { L, C, h } = hexToOklch(hex);
|
|
810
|
+
const flipped = DARK_FLIP_L_MIN + (1 - L) * (DARK_FLIP_L_MAX - DARK_FLIP_L_MIN);
|
|
811
|
+
return oklchToHex(clampToGamut({ L: flipped, C, h }));
|
|
812
|
+
}
|
|
813
|
+
function rebuildSurfaces(out, light, lightBgL, darkBgL) {
|
|
814
|
+
const lightSurface = light["--ui-surface"];
|
|
815
|
+
const lightSurface2 = light["--ui-surface-2"];
|
|
816
|
+
let surfaceDarkL;
|
|
817
|
+
let surfaceLightL = lightBgL;
|
|
818
|
+
if (lightSurface !== void 0 && HEX62.test(lightSurface)) {
|
|
819
|
+
const { L, C, h } = hexToOklch(lightSurface);
|
|
820
|
+
surfaceLightL = L;
|
|
821
|
+
surfaceDarkL = darkBgL + Math.max(Math.abs(L - lightBgL), MIN_RUNG);
|
|
822
|
+
out["--ui-surface"] = oklchToHex(clampToGamut({ L: surfaceDarkL, C, h }));
|
|
823
|
+
}
|
|
824
|
+
if (lightSurface2 !== void 0 && HEX62.test(lightSurface2)) {
|
|
825
|
+
const { L, C, h } = hexToOklch(lightSurface2);
|
|
826
|
+
const base = surfaceDarkL ?? darkBgL + MIN_RUNG;
|
|
827
|
+
const L2 = base + Math.max(Math.abs(L - surfaceLightL), MIN_RUNG);
|
|
828
|
+
out["--ui-surface-2"] = oklchToHex(clampToGamut({ L: L2, C, h }));
|
|
829
|
+
}
|
|
830
|
+
const lightCodeBg = light["--ui-code-bg"];
|
|
831
|
+
if (lightCodeBg !== void 0 && HEX62.test(lightCodeBg)) {
|
|
832
|
+
if (lightCodeBg === lightSurface2 && out["--ui-surface-2"] !== void 0) {
|
|
833
|
+
out["--ui-code-bg"] = out["--ui-surface-2"];
|
|
834
|
+
} else {
|
|
835
|
+
const { L, C, h } = hexToOklch(lightCodeBg);
|
|
836
|
+
const codeL = darkBgL + Math.max(Math.abs(L - lightBgL), MIN_RUNG);
|
|
837
|
+
out["--ui-code-bg"] = oklchToHex(clampToGamut({ L: codeL, C, h }));
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
function relight(hex, bg, min) {
|
|
842
|
+
const pred = (c) => contrastRatio(c, bg) >= min;
|
|
843
|
+
return adjustLightnessUntil(hex, pred, "lighten") ?? adjustLightnessUntil(hex, pred, "darken") ?? pickTextOn(bg);
|
|
844
|
+
}
|
|
845
|
+
function deriveDarkTokens(light) {
|
|
846
|
+
const out = {};
|
|
847
|
+
for (const [name, value] of Object.entries(light)) {
|
|
848
|
+
if (name === "--ui-shadow") out[name] = DARK_SHADOW;
|
|
849
|
+
else if (NEUTRAL_FLIP.has(name) && HEX62.test(value)) out[name] = flipNeutral(value);
|
|
850
|
+
else out[name] = value;
|
|
851
|
+
}
|
|
852
|
+
const bg = out["--ui-bg"];
|
|
853
|
+
const lightBg = light["--ui-bg"];
|
|
854
|
+
if (bg === void 0 || !HEX62.test(bg) || lightBg === void 0 || !HEX62.test(lightBg)) return out;
|
|
855
|
+
rebuildSurfaces(out, light, hexToOklch(lightBg).L, hexToOklch(bg).L);
|
|
856
|
+
for (const name of ["--ui-text", "--ui-text-muted"]) {
|
|
857
|
+
const v = out[name];
|
|
858
|
+
if (v !== void 0 && HEX62.test(v)) out[name] = relight(v, bg, 4.5);
|
|
859
|
+
}
|
|
860
|
+
const codeBg = out["--ui-code-bg"];
|
|
861
|
+
const codeText = out["--ui-code-text"];
|
|
862
|
+
if (codeText !== void 0 && HEX62.test(codeText))
|
|
863
|
+
out["--ui-code-text"] = relight(codeText, codeBg !== void 0 && HEX62.test(codeBg) ? codeBg : bg, 4.5);
|
|
864
|
+
for (const name of ACCENT_AA) {
|
|
865
|
+
const v = out[name];
|
|
866
|
+
if (v !== void 0 && HEX62.test(v)) out[name] = relight(v, bg, 4.5);
|
|
867
|
+
}
|
|
868
|
+
for (const name of CHART_UI) {
|
|
869
|
+
const v = out[name];
|
|
870
|
+
if (v !== void 0 && HEX62.test(v)) out[name] = relight(v, bg, 3);
|
|
871
|
+
}
|
|
872
|
+
const accent = out["--ui-accent"];
|
|
873
|
+
const onAccent = out["--ui-on-accent"];
|
|
874
|
+
if (accent !== void 0 && HEX62.test(accent) && onAccent !== void 0 && HEX62.test(onAccent))
|
|
875
|
+
out["--ui-on-accent"] = pickTextOn(accent);
|
|
876
|
+
return out;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// src/tokens/css.ts
|
|
880
|
+
var HEADER = "/* Generated by @odla-ai/brand \u2014 @odla-ai/ui token overrides; pair with @odla-ai/ui css/tokens.css. */";
|
|
881
|
+
var ORDER = new Map(
|
|
882
|
+
BRAND_EMITTED_TOKENS.map((name, i) => [name, i])
|
|
883
|
+
);
|
|
884
|
+
function orderedNames(tokens) {
|
|
885
|
+
return Object.keys(tokens).sort((a, b) => {
|
|
886
|
+
const ia = ORDER.get(a);
|
|
887
|
+
const ib = ORDER.get(b);
|
|
888
|
+
if (ia !== void 0 && ib !== void 0) return ia - ib;
|
|
889
|
+
if (ia !== void 0) return -1;
|
|
890
|
+
if (ib !== void 0) return 1;
|
|
891
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
function renderBlock(selector, tokens, names, indent, lead) {
|
|
895
|
+
const lines = [];
|
|
896
|
+
if (lead !== void 0) lines.push(`${indent} ${lead}`);
|
|
897
|
+
for (const name of names) lines.push(`${indent} ${name}: ${tokens[name]};`);
|
|
898
|
+
if (lines.length === 0) return `${indent}${selector} {
|
|
899
|
+
${indent}}`;
|
|
900
|
+
return `${indent}${selector} {
|
|
901
|
+
${lines.join("\n")}
|
|
902
|
+
${indent}}`;
|
|
903
|
+
}
|
|
904
|
+
function renderTokensCss(light, opts = {}) {
|
|
905
|
+
const selector = opts.selector ?? ":root";
|
|
906
|
+
const scoped = selector !== ":root";
|
|
907
|
+
const parts = [HEADER, renderBlock(selector, light, orderedNames(light), "")];
|
|
908
|
+
const dark = opts.dark;
|
|
909
|
+
if (dark !== void 0) {
|
|
910
|
+
const diffNames = orderedNames(dark).filter((name) => light[name] !== dark[name]);
|
|
911
|
+
if (diffNames.length > 0) {
|
|
912
|
+
const attrSelector = scoped ? `[data-theme="dark"] ${selector}, ${selector}[data-theme="dark"]` : `[data-theme="dark"]`;
|
|
913
|
+
const mediaSelector = scoped ? `:root:not([data-theme="light"]) ${selector}` : `:root:not([data-theme="light"])`;
|
|
914
|
+
parts.push(renderBlock(attrSelector, dark, diffNames, ""));
|
|
915
|
+
parts.push(
|
|
916
|
+
`@media (prefers-color-scheme: dark) {
|
|
917
|
+
${renderBlock(mediaSelector, dark, diffNames, " ")}
|
|
918
|
+
}`
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
if (opts.includeInvert === true) {
|
|
922
|
+
parts.push(renderBlock(".ui-invert", dark, orderedNames(dark), "", "color-scheme: dark;"));
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
return `${parts.join("\n\n")}
|
|
926
|
+
`;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// src/tokens/compile.ts
|
|
930
|
+
var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
931
|
+
function applyOverrides(light, overrides, warnings) {
|
|
932
|
+
if (overrides === void 0) return;
|
|
933
|
+
if (!isRecord(overrides)) {
|
|
934
|
+
warnings.push({ token: "--ui-accent", message: "overrides ignored: not an object" });
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
for (const [name, value] of Object.entries(overrides)) {
|
|
938
|
+
if (!name.startsWith("--")) {
|
|
939
|
+
warnings.push({ token: name, message: "override ignored: token names must start with --" });
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
943
|
+
warnings.push({ token: name, message: "override ignored: value must be a non-empty string" });
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
light[name] = value.trim();
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
function compileBrandTokens(input) {
|
|
950
|
+
const { tokens: light, warnings } = mapPaletteToTokens(input);
|
|
951
|
+
applyOverrides(light, input?.overrides, warnings);
|
|
952
|
+
const dark = deriveDarkTokens(light);
|
|
953
|
+
return { light, dark, warnings };
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
export {
|
|
957
|
+
clamp01,
|
|
958
|
+
parseHex,
|
|
959
|
+
toHex,
|
|
960
|
+
normalizeHex,
|
|
961
|
+
srgbToLinear,
|
|
962
|
+
linearToSrgb,
|
|
963
|
+
rgbToHsl,
|
|
964
|
+
hslToRgb,
|
|
965
|
+
rgbToOklab,
|
|
966
|
+
oklabToRgb,
|
|
967
|
+
oklabToOklch,
|
|
968
|
+
oklchToOklab,
|
|
969
|
+
hexToOklch,
|
|
970
|
+
oklchToHex,
|
|
971
|
+
inSrgbGamut,
|
|
972
|
+
clampToGamut,
|
|
973
|
+
relativeLuminance,
|
|
974
|
+
contrastRatio,
|
|
975
|
+
meetsAA,
|
|
976
|
+
meetsAAA,
|
|
977
|
+
PICK_TEXT_DEFAULT_CANDIDATES,
|
|
978
|
+
pickTextOn,
|
|
979
|
+
rotateHue,
|
|
980
|
+
complementary,
|
|
981
|
+
analogous,
|
|
982
|
+
triadic,
|
|
983
|
+
splitComplementary,
|
|
984
|
+
tetradic,
|
|
985
|
+
monochrome,
|
|
986
|
+
RAMP_L_MAX,
|
|
987
|
+
RAMP_L_MIN,
|
|
988
|
+
tintShadeRamp,
|
|
989
|
+
adjustLightnessUntil,
|
|
990
|
+
deltaEOKLab,
|
|
991
|
+
deltaEOK,
|
|
992
|
+
CSS_NAMED_COLORS,
|
|
993
|
+
nearestNamedColor,
|
|
994
|
+
CHART_DELTA_MIN,
|
|
995
|
+
deriveChartColors,
|
|
996
|
+
derivePalette,
|
|
997
|
+
BRAND_REQUIRED_TOKENS,
|
|
998
|
+
BRAND_DERIVED_TOKENS,
|
|
999
|
+
BRAND_CHART_TOKENS,
|
|
1000
|
+
BRAND_CHAT_TOKENS,
|
|
1001
|
+
BRAND_EMITTED_TOKENS,
|
|
1002
|
+
DEFAULT_ACCENT_SEED,
|
|
1003
|
+
mapPaletteToTokens,
|
|
1004
|
+
DARK_FLIP_L_MIN,
|
|
1005
|
+
DARK_FLIP_L_MAX,
|
|
1006
|
+
deriveDarkTokens,
|
|
1007
|
+
renderTokensCss,
|
|
1008
|
+
compileBrandTokens
|
|
1009
|
+
};
|
|
1010
|
+
//# sourceMappingURL=chunk-APLECQBR.js.map
|