@epilot/spark-ui 1.0.0-alpha.7 → 1.0.0-alpha.9
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/components/badge.cjs +2 -2
- package/dist/components/badge.cjs.map +1 -1
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/battery-progress-row.d.cts +1 -1
- package/dist/components/battery-progress-row.d.ts +1 -1
- package/dist/components/bubble.d.cts +1 -1
- package/dist/components/bubble.d.ts +1 -1
- package/dist/components/button.cjs +5 -4
- package/dist/components/button.cjs.map +1 -1
- package/dist/components/button.d.cts +4 -17
- package/dist/components/button.d.ts +5 -18
- package/dist/components/button.js +5 -4
- package/dist/components/button.js.map +1 -1
- package/dist/components/callout.cjs +4 -4
- package/dist/components/callout.cjs.map +1 -1
- package/dist/components/callout.d.cts +6 -1
- package/dist/components/callout.d.ts +6 -1
- package/dist/components/callout.js +4 -4
- package/dist/components/callout.js.map +1 -1
- package/dist/components/card.cjs +25 -11
- package/dist/components/card.cjs.map +1 -1
- package/dist/components/card.d.cts +6 -0
- package/dist/components/card.d.ts +7 -1
- package/dist/components/card.js +25 -11
- package/dist/components/card.js.map +1 -1
- package/dist/components/checkbox.cjs +2 -2
- package/dist/components/checkbox.cjs.map +1 -1
- package/dist/components/checkbox.d.ts +1 -1
- package/dist/components/checkbox.js +1 -1
- package/dist/components/circular-gauge.d.cts +1 -1
- package/dist/components/circular-gauge.d.ts +1 -1
- package/dist/components/device-card.d.cts +1 -1
- package/dist/components/device-card.d.ts +1 -1
- package/dist/components/empty-state.cjs +9 -2
- package/dist/components/empty-state.cjs.map +1 -1
- package/dist/components/empty-state.d.cts +8 -2
- package/dist/components/empty-state.d.ts +8 -2
- package/dist/components/empty-state.js +10 -2
- package/dist/components/empty-state.js.map +1 -1
- package/dist/components/energy-bar-chart.d.cts +1 -1
- package/dist/components/energy-bar-chart.d.ts +1 -1
- package/dist/components/feed-in-hero.d.cts +1 -1
- package/dist/components/feed-in-hero.d.ts +1 -1
- package/dist/components/field.cjs +3 -3
- package/dist/components/field.cjs.map +1 -1
- package/dist/components/field.d.ts +1 -1
- package/dist/components/field.js +1 -1
- package/dist/components/info-block.d.cts +1 -1
- package/dist/components/info-block.d.ts +1 -1
- package/dist/components/input-group.cjs +2 -2
- package/dist/components/input-group.cjs.map +1 -1
- package/dist/components/input-group.d.cts +1 -1
- package/dist/components/input-group.d.ts +1 -1
- package/dist/components/input-group.js +1 -1
- package/dist/components/input-otp.cjs +10 -10
- package/dist/components/input-otp.cjs.map +1 -1
- package/dist/components/input-otp.js +1 -1
- package/dist/components/input.cjs +10 -10
- package/dist/components/input.cjs.map +1 -1
- package/dist/components/input.d.ts +1 -1
- package/dist/components/input.js +1 -1
- package/dist/components/marker.d.cts +1 -1
- package/dist/components/marker.d.ts +1 -1
- package/dist/components/message-scroller.cjs +1 -1
- package/dist/components/message-scroller.cjs.map +1 -1
- package/dist/components/message-scroller.js +1 -1
- package/dist/components/message-scroller.js.map +1 -1
- package/dist/components/metering-row.d.cts +1 -1
- package/dist/components/metering-row.d.ts +1 -1
- package/dist/components/number-flow.d.cts +1 -1
- package/dist/components/number-flow.d.ts +1 -1
- package/dist/components/page-header.d.cts +1 -1
- package/dist/components/page-header.d.ts +1 -1
- package/dist/components/radio.cjs +3 -3
- package/dist/components/radio.cjs.map +1 -1
- package/dist/components/radio.d.ts +1 -1
- package/dist/components/radio.js +1 -1
- package/dist/components/sankey-chart.d.cts +1 -1
- package/dist/components/sankey-chart.d.ts +1 -1
- package/dist/components/segmented-control.d.ts +1 -1
- package/dist/components/select.cjs +2 -2
- package/dist/components/select.cjs.map +1 -1
- package/dist/components/select.d.ts +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/switch.cjs +2 -2
- package/dist/components/switch.cjs.map +1 -1
- package/dist/components/switch.d.ts +1 -1
- package/dist/components/switch.js +1 -1
- package/dist/components/theme.cjs +119 -3
- package/dist/components/theme.cjs.map +1 -0
- package/dist/components/theme.d.cts +1 -3
- package/dist/components/theme.d.ts +2 -4
- package/dist/components/theme.js +115 -1
- package/dist/components/theme.js.map +1 -0
- package/dist/components/toast.cjs +158 -65
- package/dist/components/toast.cjs.map +1 -1
- package/dist/components/toast.d.cts +51 -9
- package/dist/components/toast.d.ts +51 -9
- package/dist/components/toast.js +158 -65
- package/dist/components/toast.js.map +1 -1
- package/dist/components/tooltip.cjs +1 -1
- package/dist/components/tooltip.cjs.map +1 -1
- package/dist/components/tooltip.js +1 -1
- package/dist/components/tooltip.js.map +1 -1
- package/dist/generate-radix-colors-BW09KT9m.d.ts +33 -0
- package/dist/generate-radix-colors-C5mWRWnQ.d.cts +33 -0
- package/dist/{accent-colors-CjHCf8fH.cjs → lib/accent-colors.cjs} +3 -7
- package/dist/lib/accent-colors.cjs.map +1 -0
- package/dist/lib/accent-colors.d.cts +8 -0
- package/dist/lib/accent-colors.d.ts +8 -0
- package/dist/{accent-colors-Ch2J0EEY.js → lib/accent-colors.js} +2 -2
- package/dist/lib/accent-colors.js.map +1 -0
- package/dist/lib/accent-theme.cjs +130 -0
- package/dist/lib/accent-theme.cjs.map +1 -0
- package/dist/lib/accent-theme.d.cts +35 -0
- package/dist/lib/accent-theme.d.ts +35 -0
- package/dist/lib/accent-theme.js +122 -0
- package/dist/lib/accent-theme.js.map +1 -0
- package/dist/{theme-ClRJhexF.cjs → lib/generate-radix-colors.cjs} +12 -240
- package/dist/lib/generate-radix-colors.cjs.map +1 -0
- package/dist/lib/generate-radix-colors.d.cts +4 -0
- package/dist/lib/generate-radix-colors.d.ts +4 -0
- package/dist/{theme-8PDO9E0S.js → lib/generate-radix-colors.js} +8 -225
- package/dist/lib/generate-radix-colors.js.map +1 -0
- package/dist/{input-box-variants-ZCtgs-1S.cjs → lib/input-box-variants.cjs} +5 -14
- package/dist/lib/input-box-variants.cjs.map +1 -0
- package/dist/lib/input-box-variants.d.cts +10 -0
- package/dist/lib/input-box-variants.d.ts +10 -0
- package/dist/{input-box-variants-DcajYXvo.js → lib/input-box-variants.js} +3 -3
- package/dist/lib/input-box-variants.js.map +1 -0
- package/dist/lib/theme-types.cjs +1 -0
- package/dist/lib/theme-types.d.cts +4 -0
- package/dist/lib/theme-types.d.ts +4 -0
- package/dist/lib/theme-types.js +2 -0
- package/dist/lib/tones.cjs +14 -0
- package/dist/lib/tones.cjs.map +1 -0
- package/dist/{tones-PXJGlSpe.d.ts → lib/tones.d.cts} +2 -2
- package/dist/{tones-DjgKOX3s.d.cts → lib/tones.d.ts} +2 -2
- package/dist/lib/tones.js +13 -0
- package/dist/lib/tones.js.map +1 -0
- package/dist/{number-flow-DMJouddg.d.cts → number-flow-CVIvQhZn.d.cts} +1 -1
- package/dist/{number-flow-D9zsxH0-.d.ts → number-flow-DVUHTv9D.d.ts} +1 -1
- package/dist/styles/index.css +158 -0
- package/dist/styles/spark-default.css +1 -0
- package/dist/{theme-types-BDdoRxXa.d.ts → theme-types-BKlzRiyw.d.ts} +1 -1
- package/package.json +18 -3
- package/dist/accent-colors-Ch2J0EEY.js.map +0 -1
- package/dist/accent-colors-CjHCf8fH.cjs.map +0 -1
- package/dist/input-box-variants-DcajYXvo.js.map +0 -1
- package/dist/input-box-variants-ZCtgs-1S.cjs.map +0 -1
- package/dist/theme-8PDO9E0S.js.map +0 -1
- package/dist/theme-ClRJhexF.cjs.map +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/generate-radix-colors.d.ts
|
|
4
|
+
type ArrayOf12<T> = [T, T, T, T, T, T, T, T, T, T, T, T];
|
|
5
|
+
declare const generateRadixColors: ({
|
|
6
|
+
appearance,
|
|
7
|
+
...args
|
|
8
|
+
}: {
|
|
9
|
+
appearance: "light" | "dark";
|
|
10
|
+
accent: string;
|
|
11
|
+
gray: string;
|
|
12
|
+
background: string;
|
|
13
|
+
}) => {
|
|
14
|
+
accentScale: ArrayOf12<string>;
|
|
15
|
+
accentScaleAlpha: ArrayOf12<string>;
|
|
16
|
+
accentScaleWideGamut: ArrayOf12<string>;
|
|
17
|
+
accentScaleAlphaWideGamut: ArrayOf12<string>;
|
|
18
|
+
accentContrast: string;
|
|
19
|
+
grayScale: ArrayOf12<string>;
|
|
20
|
+
grayScaleAlpha: ArrayOf12<string>;
|
|
21
|
+
grayScaleWideGamut: ArrayOf12<string>;
|
|
22
|
+
grayScaleAlphaWideGamut: ArrayOf12<string>;
|
|
23
|
+
graySurface: string;
|
|
24
|
+
graySurfaceWideGamut: string;
|
|
25
|
+
accentSurface: string;
|
|
26
|
+
accentSurfaceWideGamut: string;
|
|
27
|
+
background: string;
|
|
28
|
+
};
|
|
29
|
+
declare function transposeProgressionStart(to: number, arr: number[], curve: [number, number, number, number]): number[];
|
|
30
|
+
declare function transposeProgressionEnd(to: number, arr: number[], curve: [number, number, number, number]): number[];
|
|
31
|
+
//#endregion
|
|
32
|
+
export { transposeProgressionEnd as n, transposeProgressionStart as r, generateRadixColors as t };
|
|
33
|
+
//# sourceMappingURL=generate-radix-colors-C5mWRWnQ.d.cts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
3
|
//#region src/lib/accent-colors.ts
|
|
3
4
|
const accentByColor = {
|
|
4
5
|
accent: void 0,
|
|
@@ -8,11 +9,6 @@ const accentByColor = {
|
|
|
8
9
|
danger: "red"
|
|
9
10
|
};
|
|
10
11
|
//#endregion
|
|
11
|
-
|
|
12
|
-
enumerable: true,
|
|
13
|
-
get: function() {
|
|
14
|
-
return accentByColor;
|
|
15
|
-
}
|
|
16
|
-
});
|
|
12
|
+
exports.accentByColor = accentByColor;
|
|
17
13
|
|
|
18
|
-
//# sourceMappingURL=accent-colors
|
|
14
|
+
//# sourceMappingURL=accent-colors.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accent-colors.cjs","names":[],"sources":["../../src/lib/accent-colors.ts"],"sourcesContent":["export type SparkAccentColor =\n | 'accent'\n | 'neutral'\n | 'success'\n | 'warning'\n | 'danger'\n\nexport const accentByColor: Record<\n SparkAccentColor,\n 'gray' | 'red' | 'green' | 'orange' | undefined\n> = {\n accent: undefined,\n neutral: 'gray',\n success: 'green',\n warning: 'orange',\n danger: 'red',\n}\n"],"mappings":";;;AAOA,MAAa,gBAGT;CACF,QAAQ,KAAA;CACR,SAAS;CACT,SAAS;CACT,SAAS;CACT,QAAQ;CACT"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/accent-colors.d.ts
|
|
4
|
+
type SparkAccentColor = 'accent' | 'neutral' | 'success' | 'warning' | 'danger';
|
|
5
|
+
declare const accentByColor: Record<SparkAccentColor, 'gray' | 'red' | 'green' | 'orange' | undefined>;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { SparkAccentColor, accentByColor };
|
|
8
|
+
//# sourceMappingURL=accent-colors.d.cts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/accent-colors.d.ts
|
|
4
|
+
type SparkAccentColor = 'accent' | 'neutral' | 'success' | 'warning' | 'danger';
|
|
5
|
+
declare const accentByColor: Record<SparkAccentColor, 'gray' | 'red' | 'green' | 'orange' | undefined>;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { SparkAccentColor, accentByColor };
|
|
8
|
+
//# sourceMappingURL=accent-colors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accent-colors.js","names":[],"sources":["../../src/lib/accent-colors.ts"],"sourcesContent":["export type SparkAccentColor =\n | 'accent'\n | 'neutral'\n | 'success'\n | 'warning'\n | 'danger'\n\nexport const accentByColor: Record<\n SparkAccentColor,\n 'gray' | 'red' | 'green' | 'orange' | undefined\n> = {\n accent: undefined,\n neutral: 'gray',\n success: 'green',\n warning: 'orange',\n danger: 'red',\n}\n"],"mappings":";;AAOA,MAAa,gBAGT;CACF,QAAQ,KAAA;CACR,SAAS;CACT,SAAS;CACT,SAAS;CACT,QAAQ;CACT"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const require_chunk = require("../chunk-aJGBMT86.cjs");
|
|
4
|
+
const require_lib_generate_radix_colors = require("./generate-radix-colors.cjs");
|
|
5
|
+
let _radix_ui_colors = require("@radix-ui/colors");
|
|
6
|
+
_radix_ui_colors = require_chunk.__toESM(_radix_ui_colors, 1);
|
|
7
|
+
let colorjs_io = require("colorjs.io");
|
|
8
|
+
colorjs_io = require_chunk.__toESM(colorjs_io, 1);
|
|
9
|
+
//#region src/lib/accent-theme.ts
|
|
10
|
+
/** Curated name → source color. `blue` = Radix blue; the no-attribute default `--spark-*` stays the epilot brand. */
|
|
11
|
+
const CURATED_ACCENTS = {
|
|
12
|
+
blue: _radix_ui_colors.blue.blue9,
|
|
13
|
+
indigo: _radix_ui_colors.indigo.indigo9,
|
|
14
|
+
violet: _radix_ui_colors.violet.violet9,
|
|
15
|
+
purple: _radix_ui_colors.purple.purple9,
|
|
16
|
+
cyan: _radix_ui_colors.cyan.cyan9,
|
|
17
|
+
teal: _radix_ui_colors.teal.teal9,
|
|
18
|
+
green: _radix_ui_colors.green.green9,
|
|
19
|
+
grass: _radix_ui_colors.grass.grass9,
|
|
20
|
+
orange: _radix_ui_colors.orange.orange9,
|
|
21
|
+
red: _radix_ui_colors.red.red9,
|
|
22
|
+
pink: _radix_ui_colors.pink.pink9,
|
|
23
|
+
gray: _radix_ui_colors.gray.gray9
|
|
24
|
+
};
|
|
25
|
+
/** ECP `DesignUtils` generator inputs, so a generated scale matches live portals. */
|
|
26
|
+
const DEFAULTS = {
|
|
27
|
+
light: {
|
|
28
|
+
gray: "#333333",
|
|
29
|
+
background: "#ffffff"
|
|
30
|
+
},
|
|
31
|
+
dark: {
|
|
32
|
+
gray: "#8b8d98",
|
|
33
|
+
background: "#000000"
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
/** Generate the Radix-style accent scale for one appearance from a single accent color. */
|
|
37
|
+
function generateAccentScale(accent, appearance) {
|
|
38
|
+
const scale = require_lib_generate_radix_colors.generateRadixColors({
|
|
39
|
+
appearance,
|
|
40
|
+
accent,
|
|
41
|
+
gray: DEFAULTS[appearance].gray,
|
|
42
|
+
background: DEFAULTS[appearance].background
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
...scale,
|
|
46
|
+
accentContrast: readableContrast(scale, MIN_TEXT_CONTRAST),
|
|
47
|
+
accentOnSolid: readableContrast(scale, MIN_GRAPHIC_CONTRAST)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const MIN_TEXT_CONTRAST = 4.5;
|
|
51
|
+
const MIN_GRAPHIC_CONTRAST = 3;
|
|
52
|
+
function readableContrast(scale, min) {
|
|
53
|
+
const step9 = new colorjs_io.default(scale.accentScale[8]);
|
|
54
|
+
const [, chroma, hue] = step9.to("oklch").coords;
|
|
55
|
+
const tinted = (lightness) => new colorjs_io.default("oklch", [
|
|
56
|
+
lightness,
|
|
57
|
+
Math.max(.08 * (chroma ?? 0), .04),
|
|
58
|
+
hue ?? 0
|
|
59
|
+
]).to("srgb").toString({ format: "hex" });
|
|
60
|
+
const candidates = [
|
|
61
|
+
scale.accentContrast,
|
|
62
|
+
tinted(.25),
|
|
63
|
+
tinted(.15)
|
|
64
|
+
];
|
|
65
|
+
const ratios = candidates.map((c) => new colorjs_io.default(c).contrast(step9, "WCAG21"));
|
|
66
|
+
const passing = ratios.findIndex((r) => r >= min);
|
|
67
|
+
if (passing !== -1) return candidates[passing];
|
|
68
|
+
return candidates[ratios.indexOf(Math.max(...ratios))];
|
|
69
|
+
}
|
|
70
|
+
/** sRGB `--spark-*` declarations for a generated accent scale. */
|
|
71
|
+
function accentSparkVars(scale) {
|
|
72
|
+
return sparkVars(scale.accentScale, scale.accentScaleAlpha, scale.accentContrast, scale.accentOnSolid, scale.accentSurface, scale.background);
|
|
73
|
+
}
|
|
74
|
+
/** Wide-gamut (P3) `--spark-*` declarations — for a `color-gamut: p3` `@supports` block. */
|
|
75
|
+
function accentSparkVarsP3(scale) {
|
|
76
|
+
return sparkVars(scale.accentScaleWideGamut, scale.accentScaleAlphaWideGamut, scale.accentContrast, scale.accentOnSolid, scale.accentSurfaceWideGamut, scale.background);
|
|
77
|
+
}
|
|
78
|
+
/** Radix's `grayColor="auto"` pairing (accent family → gray), mapped to our 6 neutrals. */
|
|
79
|
+
const NEUTRAL_BY_ACCENT = {
|
|
80
|
+
blue: "slate",
|
|
81
|
+
indigo: "slate",
|
|
82
|
+
cyan: "slate",
|
|
83
|
+
violet: "mauve",
|
|
84
|
+
purple: "mauve",
|
|
85
|
+
pink: "mauve",
|
|
86
|
+
red: "mauve",
|
|
87
|
+
teal: "sage",
|
|
88
|
+
green: "sage",
|
|
89
|
+
grass: "olive",
|
|
90
|
+
orange: "sand",
|
|
91
|
+
gray: "gray"
|
|
92
|
+
};
|
|
93
|
+
/** Radix's "auto" gray: curated names use Radix's table; a custom hex is classified by OKLCH hue. */
|
|
94
|
+
function getMatchingNeutral(accent) {
|
|
95
|
+
const byName = NEUTRAL_BY_ACCENT[accent];
|
|
96
|
+
if (byName) return byName;
|
|
97
|
+
try {
|
|
98
|
+
const [, chroma, hueRaw] = new colorjs_io.default(accent).to("oklch").coords;
|
|
99
|
+
if (!chroma || chroma < .04) return "gray";
|
|
100
|
+
const hue = ((hueRaw ?? 0) % 360 + 360) % 360;
|
|
101
|
+
if (hue >= 300 || hue < 35) return "mauve";
|
|
102
|
+
if (hue < 115) return "sand";
|
|
103
|
+
if (hue < 145) return "olive";
|
|
104
|
+
if (hue < 200) return "sage";
|
|
105
|
+
return "slate";
|
|
106
|
+
} catch {
|
|
107
|
+
return "slate";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function sparkVars(steps, alpha, contrast, onSolid, surface, background) {
|
|
111
|
+
const indicator = steps[8];
|
|
112
|
+
return [
|
|
113
|
+
...steps.map((c, i) => `--spark-${i + 1}: ${c};`),
|
|
114
|
+
...alpha.map((c, i) => `--spark-a${i + 1}: ${c};`),
|
|
115
|
+
`--spark-contrast: ${contrast};`,
|
|
116
|
+
`--spark-on-solid: ${onSolid};`,
|
|
117
|
+
`--spark-surface: ${surface};`,
|
|
118
|
+
`--spark-background: ${background};`,
|
|
119
|
+
`--spark-indicator: ${indicator};`,
|
|
120
|
+
`--spark-track: ${indicator};`
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
exports.CURATED_ACCENTS = CURATED_ACCENTS;
|
|
125
|
+
exports.accentSparkVars = accentSparkVars;
|
|
126
|
+
exports.accentSparkVarsP3 = accentSparkVarsP3;
|
|
127
|
+
exports.generateAccentScale = generateAccentScale;
|
|
128
|
+
exports.getMatchingNeutral = getMatchingNeutral;
|
|
129
|
+
|
|
130
|
+
//# sourceMappingURL=accent-theme.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accent-theme.cjs","names":["RadixColors","generateRadixColors","Color"],"sources":["../../src/lib/accent-theme.ts"],"sourcesContent":["// Accent is the one runtime knob (colour math, not a CSS-var swap): generate the\n// `--spark-*` scale with the same Radix algorithm live portals use, apply as inline vars.\nimport * as RadixColors from '@radix-ui/colors'\nimport Color from 'colorjs.io'\nimport { generateRadixColors } from './generate-radix-colors'\nimport type { SparkNeutral } from './theme-types'\n\n/** Shape returned by the vendored Radix generator (it doesn't export a named type). */\nexport type GeneratedColors = ReturnType<typeof generateRadixColors>\n\n/**\n * generateAccentScale output: the raw scale, but with two readable foregrounds on the\n * solid (step-9) fill, each meeting a different WCAG bar:\n * - `accentContrast` — 4.5:1 (WCAG 1.4.3), for button/label **text**.\n * - `accentOnSolid` — 3:1 (WCAG 1.4.11), for a **graphical glyph** (the checkbox check).\n * A checkmark is graphical, not text, so it uses the looser-but-correct 3:1 bar: white\n * where it clears 3:1 (most accents, incl. neutral & red — the convention), and a dark\n * accent-tint where white is too weak (e.g. orange). Using the 4.5:1 text contrast for\n * the glyph over-darkened it (a black tick on a red/neutral checkbox).\n */\nexport type SparkAccentScale = GeneratedColors & { accentOnSolid: string }\n\nexport type Appearance = 'light' | 'dark'\n\n/** Curated name → source color. `blue` = Radix blue; the no-attribute default `--spark-*` stays the epilot brand. */\nexport const CURATED_ACCENTS: Record<string, string> = {\n blue: RadixColors.blue.blue9,\n indigo: RadixColors.indigo.indigo9,\n violet: RadixColors.violet.violet9,\n purple: RadixColors.purple.purple9,\n cyan: RadixColors.cyan.cyan9,\n teal: RadixColors.teal.teal9,\n green: RadixColors.green.green9,\n grass: RadixColors.grass.grass9,\n orange: RadixColors.orange.orange9,\n red: RadixColors.red.red9,\n pink: RadixColors.pink.pink9,\n gray: RadixColors.gray.gray9,\n}\n\n/** ECP `DesignUtils` generator inputs, so a generated scale matches live portals. */\nconst DEFAULTS: Record<Appearance, { gray: string; background: string }> = {\n light: { gray: '#333333', background: '#ffffff' },\n // Matches ECP DesignUtils' DEFAULT_DARK_BACKGROUND_COLOR -- the alpha ramp\n // is composited against this, so it must match what's actually painted.\n dark: { gray: '#8b8d98', background: '#000000' },\n}\n\n/** Generate the Radix-style accent scale for one appearance from a single accent color. */\nexport function generateAccentScale(\n accent: string,\n appearance: Appearance,\n): SparkAccentScale {\n const scale = generateRadixColors({\n appearance,\n accent,\n gray: DEFAULTS[appearance].gray,\n background: DEFAULTS[appearance].background,\n })\n // accentContrast → 4.5:1 (button text); accentOnSolid → 3:1 (graphical glyph,\n // see SparkAccentScale). Same picker, different WCAG threshold.\n return {\n ...scale,\n accentContrast: readableContrast(scale, MIN_TEXT_CONTRAST),\n accentOnSolid: readableContrast(scale, MIN_GRAPHIC_CONTRAST),\n }\n}\n\nconst MIN_TEXT_CONTRAST = 4.5 // WCAG 1.4.3 — button / label text\nconst MIN_GRAPHIC_CONTRAST = 3 // WCAG 1.4.11 — a check / indicator glyph on a solid fill\n\n// Pick the readable foreground on step 9: prefer the generator's native contrast\n// (usually white), else fall to progressively darker accent-tinted inks until one\n// clears `min`. Text uses 4.5:1; a graphical glyph (the checkmark) uses 3:1 — which\n// keeps white on most accents (incl. neutral 3.32:1, red 3.91:1) but flips to a dark\n// tint where white is too weak (e.g. orange, 2.97:1). The vendored generator keeps\n// white at APCA Lc >= 40, which can sit below both WCAG thresholds — hence this guard.\nfunction readableContrast(scale: GeneratedColors, min: number): string {\n const step9 = new Color(scale.accentScale[8]!)\n const [, chroma, hue] = step9.to('oklch').coords\n const tinted = (lightness: number) =>\n new Color('oklch', [\n lightness,\n Math.max(0.08 * (chroma ?? 0), 0.04),\n hue ?? 0,\n ])\n .to('srgb')\n .toString({ format: 'hex' })\n const candidates = [scale.accentContrast, tinted(0.25), tinted(0.15)]\n const ratios = candidates.map((c) => new Color(c).contrast(step9, 'WCAG21'))\n const passing = ratios.findIndex((r) => r >= min)\n if (passing !== -1) return candidates[passing]!\n return candidates[ratios.indexOf(Math.max(...ratios))]!\n}\n\n/** sRGB `--spark-*` declarations for a generated accent scale. */\nexport function accentSparkVars(scale: SparkAccentScale): string[] {\n return sparkVars(\n scale.accentScale,\n scale.accentScaleAlpha,\n scale.accentContrast,\n scale.accentOnSolid,\n scale.accentSurface,\n scale.background,\n )\n}\n\n/** Wide-gamut (P3) `--spark-*` declarations — for a `color-gamut: p3` `@supports` block. */\nexport function accentSparkVarsP3(scale: SparkAccentScale): string[] {\n return sparkVars(\n scale.accentScaleWideGamut,\n scale.accentScaleAlphaWideGamut,\n scale.accentContrast,\n scale.accentOnSolid,\n scale.accentSurfaceWideGamut,\n scale.background,\n )\n}\n\n/** Radix's `grayColor=\"auto\"` pairing (accent family → gray), mapped to our 6 neutrals. */\nconst NEUTRAL_BY_ACCENT: Record<string, SparkNeutral> = {\n blue: 'slate',\n indigo: 'slate',\n cyan: 'slate',\n violet: 'mauve',\n purple: 'mauve',\n pink: 'mauve',\n red: 'mauve',\n teal: 'sage',\n green: 'sage',\n grass: 'olive',\n orange: 'sand',\n gray: 'gray',\n}\n\n/** Radix's \"auto\" gray: curated names use Radix's table; a custom hex is classified by OKLCH hue. */\nexport function getMatchingNeutral(accent: string): SparkNeutral {\n const byName = NEUTRAL_BY_ACCENT[accent]\n if (byName) return byName\n try {\n const [, chroma, hueRaw] = new Color(accent).to('oklch').coords\n if (!chroma || chroma < 0.04) return 'gray'\n const hue = (((hueRaw ?? 0) % 360) + 360) % 360\n if (hue >= 300 || hue < 35) return 'mauve'\n if (hue < 115) return 'sand'\n if (hue < 145) return 'olive'\n if (hue < 200) return 'sage'\n return 'slate'\n } catch {\n return 'slate'\n }\n}\n\nfunction sparkVars(\n steps: string[],\n alpha: string[],\n contrast: string,\n onSolid: string,\n surface: string,\n background: string,\n): string[] {\n const indicator = steps[8]! // step 9 — the brand step (matches --spark-indicator/-track)\n return [\n ...steps.map((c, i) => `--spark-${i + 1}: ${c};`),\n ...alpha.map((c, i) => `--spark-a${i + 1}: ${c};`),\n `--spark-contrast: ${contrast};`,\n `--spark-on-solid: ${onSolid};`,\n `--spark-surface: ${surface};`,\n `--spark-background: ${background};`,\n `--spark-indicator: ${indicator};`,\n `--spark-track: ${indicator};`,\n ]\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { o as SparkNeutral } from "../theme-types-BwSJKdsB.cjs";
|
|
4
|
+
import { t as generateRadixColors } from "../generate-radix-colors-C5mWRWnQ.cjs";
|
|
5
|
+
|
|
6
|
+
//#region src/lib/accent-theme.d.ts
|
|
7
|
+
/** Shape returned by the vendored Radix generator (it doesn't export a named type). */
|
|
8
|
+
type GeneratedColors = ReturnType<typeof generateRadixColors>;
|
|
9
|
+
/**
|
|
10
|
+
* generateAccentScale output: the raw scale, but with two readable foregrounds on the
|
|
11
|
+
* solid (step-9) fill, each meeting a different WCAG bar:
|
|
12
|
+
* - `accentContrast` — 4.5:1 (WCAG 1.4.3), for button/label **text**.
|
|
13
|
+
* - `accentOnSolid` — 3:1 (WCAG 1.4.11), for a **graphical glyph** (the checkbox check).
|
|
14
|
+
* A checkmark is graphical, not text, so it uses the looser-but-correct 3:1 bar: white
|
|
15
|
+
* where it clears 3:1 (most accents, incl. neutral & red — the convention), and a dark
|
|
16
|
+
* accent-tint where white is too weak (e.g. orange). Using the 4.5:1 text contrast for
|
|
17
|
+
* the glyph over-darkened it (a black tick on a red/neutral checkbox).
|
|
18
|
+
*/
|
|
19
|
+
type SparkAccentScale = GeneratedColors & {
|
|
20
|
+
accentOnSolid: string;
|
|
21
|
+
};
|
|
22
|
+
type Appearance = 'light' | 'dark';
|
|
23
|
+
/** Curated name → source color. `blue` = Radix blue; the no-attribute default `--spark-*` stays the epilot brand. */
|
|
24
|
+
declare const CURATED_ACCENTS: Record<string, string>;
|
|
25
|
+
/** Generate the Radix-style accent scale for one appearance from a single accent color. */
|
|
26
|
+
declare function generateAccentScale(accent: string, appearance: Appearance): SparkAccentScale;
|
|
27
|
+
/** sRGB `--spark-*` declarations for a generated accent scale. */
|
|
28
|
+
declare function accentSparkVars(scale: SparkAccentScale): string[];
|
|
29
|
+
/** Wide-gamut (P3) `--spark-*` declarations — for a `color-gamut: p3` `@supports` block. */
|
|
30
|
+
declare function accentSparkVarsP3(scale: SparkAccentScale): string[];
|
|
31
|
+
/** Radix's "auto" gray: curated names use Radix's table; a custom hex is classified by OKLCH hue. */
|
|
32
|
+
declare function getMatchingNeutral(accent: string): SparkNeutral;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { Appearance, CURATED_ACCENTS, GeneratedColors, SparkAccentScale, accentSparkVars, accentSparkVarsP3, generateAccentScale, getMatchingNeutral };
|
|
35
|
+
//# sourceMappingURL=accent-theme.d.cts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { o as SparkNeutral } from "../theme-types-BKlzRiyw.js";
|
|
4
|
+
import { t as generateRadixColors } from "../generate-radix-colors-BW09KT9m.js";
|
|
5
|
+
|
|
6
|
+
//#region src/lib/accent-theme.d.ts
|
|
7
|
+
/** Shape returned by the vendored Radix generator (it doesn't export a named type). */
|
|
8
|
+
type GeneratedColors = ReturnType<typeof generateRadixColors>;
|
|
9
|
+
/**
|
|
10
|
+
* generateAccentScale output: the raw scale, but with two readable foregrounds on the
|
|
11
|
+
* solid (step-9) fill, each meeting a different WCAG bar:
|
|
12
|
+
* - `accentContrast` — 4.5:1 (WCAG 1.4.3), for button/label **text**.
|
|
13
|
+
* - `accentOnSolid` — 3:1 (WCAG 1.4.11), for a **graphical glyph** (the checkbox check).
|
|
14
|
+
* A checkmark is graphical, not text, so it uses the looser-but-correct 3:1 bar: white
|
|
15
|
+
* where it clears 3:1 (most accents, incl. neutral & red — the convention), and a dark
|
|
16
|
+
* accent-tint where white is too weak (e.g. orange). Using the 4.5:1 text contrast for
|
|
17
|
+
* the glyph over-darkened it (a black tick on a red/neutral checkbox).
|
|
18
|
+
*/
|
|
19
|
+
type SparkAccentScale = GeneratedColors & {
|
|
20
|
+
accentOnSolid: string;
|
|
21
|
+
};
|
|
22
|
+
type Appearance = 'light' | 'dark';
|
|
23
|
+
/** Curated name → source color. `blue` = Radix blue; the no-attribute default `--spark-*` stays the epilot brand. */
|
|
24
|
+
declare const CURATED_ACCENTS: Record<string, string>;
|
|
25
|
+
/** Generate the Radix-style accent scale for one appearance from a single accent color. */
|
|
26
|
+
declare function generateAccentScale(accent: string, appearance: Appearance): SparkAccentScale;
|
|
27
|
+
/** sRGB `--spark-*` declarations for a generated accent scale. */
|
|
28
|
+
declare function accentSparkVars(scale: SparkAccentScale): string[];
|
|
29
|
+
/** Wide-gamut (P3) `--spark-*` declarations — for a `color-gamut: p3` `@supports` block. */
|
|
30
|
+
declare function accentSparkVarsP3(scale: SparkAccentScale): string[];
|
|
31
|
+
/** Radix's "auto" gray: curated names use Radix's table; a custom hex is classified by OKLCH hue. */
|
|
32
|
+
declare function getMatchingNeutral(accent: string): SparkNeutral;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { Appearance, CURATED_ACCENTS, GeneratedColors, SparkAccentScale, accentSparkVars, accentSparkVarsP3, generateAccentScale, getMatchingNeutral };
|
|
35
|
+
//# sourceMappingURL=accent-theme.d.ts.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { generateRadixColors } from "./generate-radix-colors.js";
|
|
3
|
+
import * as RadixColors from "@radix-ui/colors";
|
|
4
|
+
import Color from "colorjs.io";
|
|
5
|
+
//#region src/lib/accent-theme.ts
|
|
6
|
+
/** Curated name → source color. `blue` = Radix blue; the no-attribute default `--spark-*` stays the epilot brand. */
|
|
7
|
+
const CURATED_ACCENTS = {
|
|
8
|
+
blue: RadixColors.blue.blue9,
|
|
9
|
+
indigo: RadixColors.indigo.indigo9,
|
|
10
|
+
violet: RadixColors.violet.violet9,
|
|
11
|
+
purple: RadixColors.purple.purple9,
|
|
12
|
+
cyan: RadixColors.cyan.cyan9,
|
|
13
|
+
teal: RadixColors.teal.teal9,
|
|
14
|
+
green: RadixColors.green.green9,
|
|
15
|
+
grass: RadixColors.grass.grass9,
|
|
16
|
+
orange: RadixColors.orange.orange9,
|
|
17
|
+
red: RadixColors.red.red9,
|
|
18
|
+
pink: RadixColors.pink.pink9,
|
|
19
|
+
gray: RadixColors.gray.gray9
|
|
20
|
+
};
|
|
21
|
+
/** ECP `DesignUtils` generator inputs, so a generated scale matches live portals. */
|
|
22
|
+
const DEFAULTS = {
|
|
23
|
+
light: {
|
|
24
|
+
gray: "#333333",
|
|
25
|
+
background: "#ffffff"
|
|
26
|
+
},
|
|
27
|
+
dark: {
|
|
28
|
+
gray: "#8b8d98",
|
|
29
|
+
background: "#000000"
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
/** Generate the Radix-style accent scale for one appearance from a single accent color. */
|
|
33
|
+
function generateAccentScale(accent, appearance) {
|
|
34
|
+
const scale = generateRadixColors({
|
|
35
|
+
appearance,
|
|
36
|
+
accent,
|
|
37
|
+
gray: DEFAULTS[appearance].gray,
|
|
38
|
+
background: DEFAULTS[appearance].background
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
...scale,
|
|
42
|
+
accentContrast: readableContrast(scale, MIN_TEXT_CONTRAST),
|
|
43
|
+
accentOnSolid: readableContrast(scale, MIN_GRAPHIC_CONTRAST)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const MIN_TEXT_CONTRAST = 4.5;
|
|
47
|
+
const MIN_GRAPHIC_CONTRAST = 3;
|
|
48
|
+
function readableContrast(scale, min) {
|
|
49
|
+
const step9 = new Color(scale.accentScale[8]);
|
|
50
|
+
const [, chroma, hue] = step9.to("oklch").coords;
|
|
51
|
+
const tinted = (lightness) => new Color("oklch", [
|
|
52
|
+
lightness,
|
|
53
|
+
Math.max(.08 * (chroma ?? 0), .04),
|
|
54
|
+
hue ?? 0
|
|
55
|
+
]).to("srgb").toString({ format: "hex" });
|
|
56
|
+
const candidates = [
|
|
57
|
+
scale.accentContrast,
|
|
58
|
+
tinted(.25),
|
|
59
|
+
tinted(.15)
|
|
60
|
+
];
|
|
61
|
+
const ratios = candidates.map((c) => new Color(c).contrast(step9, "WCAG21"));
|
|
62
|
+
const passing = ratios.findIndex((r) => r >= min);
|
|
63
|
+
if (passing !== -1) return candidates[passing];
|
|
64
|
+
return candidates[ratios.indexOf(Math.max(...ratios))];
|
|
65
|
+
}
|
|
66
|
+
/** sRGB `--spark-*` declarations for a generated accent scale. */
|
|
67
|
+
function accentSparkVars(scale) {
|
|
68
|
+
return sparkVars(scale.accentScale, scale.accentScaleAlpha, scale.accentContrast, scale.accentOnSolid, scale.accentSurface, scale.background);
|
|
69
|
+
}
|
|
70
|
+
/** Wide-gamut (P3) `--spark-*` declarations — for a `color-gamut: p3` `@supports` block. */
|
|
71
|
+
function accentSparkVarsP3(scale) {
|
|
72
|
+
return sparkVars(scale.accentScaleWideGamut, scale.accentScaleAlphaWideGamut, scale.accentContrast, scale.accentOnSolid, scale.accentSurfaceWideGamut, scale.background);
|
|
73
|
+
}
|
|
74
|
+
/** Radix's `grayColor="auto"` pairing (accent family → gray), mapped to our 6 neutrals. */
|
|
75
|
+
const NEUTRAL_BY_ACCENT = {
|
|
76
|
+
blue: "slate",
|
|
77
|
+
indigo: "slate",
|
|
78
|
+
cyan: "slate",
|
|
79
|
+
violet: "mauve",
|
|
80
|
+
purple: "mauve",
|
|
81
|
+
pink: "mauve",
|
|
82
|
+
red: "mauve",
|
|
83
|
+
teal: "sage",
|
|
84
|
+
green: "sage",
|
|
85
|
+
grass: "olive",
|
|
86
|
+
orange: "sand",
|
|
87
|
+
gray: "gray"
|
|
88
|
+
};
|
|
89
|
+
/** Radix's "auto" gray: curated names use Radix's table; a custom hex is classified by OKLCH hue. */
|
|
90
|
+
function getMatchingNeutral(accent) {
|
|
91
|
+
const byName = NEUTRAL_BY_ACCENT[accent];
|
|
92
|
+
if (byName) return byName;
|
|
93
|
+
try {
|
|
94
|
+
const [, chroma, hueRaw] = new Color(accent).to("oklch").coords;
|
|
95
|
+
if (!chroma || chroma < .04) return "gray";
|
|
96
|
+
const hue = ((hueRaw ?? 0) % 360 + 360) % 360;
|
|
97
|
+
if (hue >= 300 || hue < 35) return "mauve";
|
|
98
|
+
if (hue < 115) return "sand";
|
|
99
|
+
if (hue < 145) return "olive";
|
|
100
|
+
if (hue < 200) return "sage";
|
|
101
|
+
return "slate";
|
|
102
|
+
} catch {
|
|
103
|
+
return "slate";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function sparkVars(steps, alpha, contrast, onSolid, surface, background) {
|
|
107
|
+
const indicator = steps[8];
|
|
108
|
+
return [
|
|
109
|
+
...steps.map((c, i) => `--spark-${i + 1}: ${c};`),
|
|
110
|
+
...alpha.map((c, i) => `--spark-a${i + 1}: ${c};`),
|
|
111
|
+
`--spark-contrast: ${contrast};`,
|
|
112
|
+
`--spark-on-solid: ${onSolid};`,
|
|
113
|
+
`--spark-surface: ${surface};`,
|
|
114
|
+
`--spark-background: ${background};`,
|
|
115
|
+
`--spark-indicator: ${indicator};`,
|
|
116
|
+
`--spark-track: ${indicator};`
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { CURATED_ACCENTS, accentSparkVars, accentSparkVarsP3, generateAccentScale, getMatchingNeutral };
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=accent-theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accent-theme.js","names":[],"sources":["../../src/lib/accent-theme.ts"],"sourcesContent":["// Accent is the one runtime knob (colour math, not a CSS-var swap): generate the\n// `--spark-*` scale with the same Radix algorithm live portals use, apply as inline vars.\nimport * as RadixColors from '@radix-ui/colors'\nimport Color from 'colorjs.io'\nimport { generateRadixColors } from './generate-radix-colors'\nimport type { SparkNeutral } from './theme-types'\n\n/** Shape returned by the vendored Radix generator (it doesn't export a named type). */\nexport type GeneratedColors = ReturnType<typeof generateRadixColors>\n\n/**\n * generateAccentScale output: the raw scale, but with two readable foregrounds on the\n * solid (step-9) fill, each meeting a different WCAG bar:\n * - `accentContrast` — 4.5:1 (WCAG 1.4.3), for button/label **text**.\n * - `accentOnSolid` — 3:1 (WCAG 1.4.11), for a **graphical glyph** (the checkbox check).\n * A checkmark is graphical, not text, so it uses the looser-but-correct 3:1 bar: white\n * where it clears 3:1 (most accents, incl. neutral & red — the convention), and a dark\n * accent-tint where white is too weak (e.g. orange). Using the 4.5:1 text contrast for\n * the glyph over-darkened it (a black tick on a red/neutral checkbox).\n */\nexport type SparkAccentScale = GeneratedColors & { accentOnSolid: string }\n\nexport type Appearance = 'light' | 'dark'\n\n/** Curated name → source color. `blue` = Radix blue; the no-attribute default `--spark-*` stays the epilot brand. */\nexport const CURATED_ACCENTS: Record<string, string> = {\n blue: RadixColors.blue.blue9,\n indigo: RadixColors.indigo.indigo9,\n violet: RadixColors.violet.violet9,\n purple: RadixColors.purple.purple9,\n cyan: RadixColors.cyan.cyan9,\n teal: RadixColors.teal.teal9,\n green: RadixColors.green.green9,\n grass: RadixColors.grass.grass9,\n orange: RadixColors.orange.orange9,\n red: RadixColors.red.red9,\n pink: RadixColors.pink.pink9,\n gray: RadixColors.gray.gray9,\n}\n\n/** ECP `DesignUtils` generator inputs, so a generated scale matches live portals. */\nconst DEFAULTS: Record<Appearance, { gray: string; background: string }> = {\n light: { gray: '#333333', background: '#ffffff' },\n // Matches ECP DesignUtils' DEFAULT_DARK_BACKGROUND_COLOR -- the alpha ramp\n // is composited against this, so it must match what's actually painted.\n dark: { gray: '#8b8d98', background: '#000000' },\n}\n\n/** Generate the Radix-style accent scale for one appearance from a single accent color. */\nexport function generateAccentScale(\n accent: string,\n appearance: Appearance,\n): SparkAccentScale {\n const scale = generateRadixColors({\n appearance,\n accent,\n gray: DEFAULTS[appearance].gray,\n background: DEFAULTS[appearance].background,\n })\n // accentContrast → 4.5:1 (button text); accentOnSolid → 3:1 (graphical glyph,\n // see SparkAccentScale). Same picker, different WCAG threshold.\n return {\n ...scale,\n accentContrast: readableContrast(scale, MIN_TEXT_CONTRAST),\n accentOnSolid: readableContrast(scale, MIN_GRAPHIC_CONTRAST),\n }\n}\n\nconst MIN_TEXT_CONTRAST = 4.5 // WCAG 1.4.3 — button / label text\nconst MIN_GRAPHIC_CONTRAST = 3 // WCAG 1.4.11 — a check / indicator glyph on a solid fill\n\n// Pick the readable foreground on step 9: prefer the generator's native contrast\n// (usually white), else fall to progressively darker accent-tinted inks until one\n// clears `min`. Text uses 4.5:1; a graphical glyph (the checkmark) uses 3:1 — which\n// keeps white on most accents (incl. neutral 3.32:1, red 3.91:1) but flips to a dark\n// tint where white is too weak (e.g. orange, 2.97:1). The vendored generator keeps\n// white at APCA Lc >= 40, which can sit below both WCAG thresholds — hence this guard.\nfunction readableContrast(scale: GeneratedColors, min: number): string {\n const step9 = new Color(scale.accentScale[8]!)\n const [, chroma, hue] = step9.to('oklch').coords\n const tinted = (lightness: number) =>\n new Color('oklch', [\n lightness,\n Math.max(0.08 * (chroma ?? 0), 0.04),\n hue ?? 0,\n ])\n .to('srgb')\n .toString({ format: 'hex' })\n const candidates = [scale.accentContrast, tinted(0.25), tinted(0.15)]\n const ratios = candidates.map((c) => new Color(c).contrast(step9, 'WCAG21'))\n const passing = ratios.findIndex((r) => r >= min)\n if (passing !== -1) return candidates[passing]!\n return candidates[ratios.indexOf(Math.max(...ratios))]!\n}\n\n/** sRGB `--spark-*` declarations for a generated accent scale. */\nexport function accentSparkVars(scale: SparkAccentScale): string[] {\n return sparkVars(\n scale.accentScale,\n scale.accentScaleAlpha,\n scale.accentContrast,\n scale.accentOnSolid,\n scale.accentSurface,\n scale.background,\n )\n}\n\n/** Wide-gamut (P3) `--spark-*` declarations — for a `color-gamut: p3` `@supports` block. */\nexport function accentSparkVarsP3(scale: SparkAccentScale): string[] {\n return sparkVars(\n scale.accentScaleWideGamut,\n scale.accentScaleAlphaWideGamut,\n scale.accentContrast,\n scale.accentOnSolid,\n scale.accentSurfaceWideGamut,\n scale.background,\n )\n}\n\n/** Radix's `grayColor=\"auto\"` pairing (accent family → gray), mapped to our 6 neutrals. */\nconst NEUTRAL_BY_ACCENT: Record<string, SparkNeutral> = {\n blue: 'slate',\n indigo: 'slate',\n cyan: 'slate',\n violet: 'mauve',\n purple: 'mauve',\n pink: 'mauve',\n red: 'mauve',\n teal: 'sage',\n green: 'sage',\n grass: 'olive',\n orange: 'sand',\n gray: 'gray',\n}\n\n/** Radix's \"auto\" gray: curated names use Radix's table; a custom hex is classified by OKLCH hue. */\nexport function getMatchingNeutral(accent: string): SparkNeutral {\n const byName = NEUTRAL_BY_ACCENT[accent]\n if (byName) return byName\n try {\n const [, chroma, hueRaw] = new Color(accent).to('oklch').coords\n if (!chroma || chroma < 0.04) return 'gray'\n const hue = (((hueRaw ?? 0) % 360) + 360) % 360\n if (hue >= 300 || hue < 35) return 'mauve'\n if (hue < 115) return 'sand'\n if (hue < 145) return 'olive'\n if (hue < 200) return 'sage'\n return 'slate'\n } catch {\n return 'slate'\n }\n}\n\nfunction sparkVars(\n steps: string[],\n alpha: string[],\n contrast: string,\n onSolid: string,\n surface: string,\n background: string,\n): string[] {\n const indicator = steps[8]! // step 9 — the brand step (matches --spark-indicator/-track)\n return [\n ...steps.map((c, i) => `--spark-${i + 1}: ${c};`),\n ...alpha.map((c, i) => `--spark-a${i + 1}: ${c};`),\n `--spark-contrast: ${contrast};`,\n `--spark-on-solid: ${onSolid};`,\n `--spark-surface: ${surface};`,\n `--spark-background: ${background};`,\n `--spark-indicator: ${indicator};`,\n `--spark-track: ${indicator};`,\n ]\n}\n"],"mappings":";;;;;;AAyBA,MAAa,kBAA0C;CACrD,MAAM,YAAY,KAAK;CACvB,QAAQ,YAAY,OAAO;CAC3B,QAAQ,YAAY,OAAO;CAC3B,QAAQ,YAAY,OAAO;CAC3B,MAAM,YAAY,KAAK;CACvB,MAAM,YAAY,KAAK;CACvB,OAAO,YAAY,MAAM;CACzB,OAAO,YAAY,MAAM;CACzB,QAAQ,YAAY,OAAO;CAC3B,KAAK,YAAY,IAAI;CACrB,MAAM,YAAY,KAAK;CACvB,MAAM,YAAY,KAAK;CACxB;;AAGD,MAAM,WAAqE;CACzE,OAAO;EAAE,MAAM;EAAW,YAAY;EAAW;CAGjD,MAAM;EAAE,MAAM;EAAW,YAAY;EAAW;CACjD;;AAGD,SAAgB,oBACd,QACA,YACkB;CAClB,MAAM,QAAQ,oBAAoB;EAChC;EACA;EACA,MAAM,SAAS,YAAY;EAC3B,YAAY,SAAS,YAAY;EAClC,CAAC;AAGF,QAAO;EACL,GAAG;EACH,gBAAgB,iBAAiB,OAAO,kBAAkB;EAC1D,eAAe,iBAAiB,OAAO,qBAAqB;EAC7D;;AAGH,MAAM,oBAAoB;AAC1B,MAAM,uBAAuB;AAQ7B,SAAS,iBAAiB,OAAwB,KAAqB;CACrE,MAAM,QAAQ,IAAI,MAAM,MAAM,YAAY,GAAI;CAC9C,MAAM,GAAG,QAAQ,OAAO,MAAM,GAAG,QAAQ,CAAC;CAC1C,MAAM,UAAU,cACd,IAAI,MAAM,SAAS;EACjB;EACA,KAAK,IAAI,OAAQ,UAAU,IAAI,IAAK;EACpC,OAAO;EACR,CAAC,CACC,GAAG,OAAO,CACV,SAAS,EAAE,QAAQ,OAAO,CAAC;CAChC,MAAM,aAAa;EAAC,MAAM;EAAgB,OAAO,IAAK;EAAE,OAAO,IAAK;EAAC;CACrE,MAAM,SAAS,WAAW,KAAK,MAAM,IAAI,MAAM,EAAE,CAAC,SAAS,OAAO,SAAS,CAAC;CAC5E,MAAM,UAAU,OAAO,WAAW,MAAM,KAAK,IAAI;AACjD,KAAI,YAAY,GAAI,QAAO,WAAW;AACtC,QAAO,WAAW,OAAO,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC;;;AAIvD,SAAgB,gBAAgB,OAAmC;AACjE,QAAO,UACL,MAAM,aACN,MAAM,kBACN,MAAM,gBACN,MAAM,eACN,MAAM,eACN,MAAM,WACP;;;AAIH,SAAgB,kBAAkB,OAAmC;AACnE,QAAO,UACL,MAAM,sBACN,MAAM,2BACN,MAAM,gBACN,MAAM,eACN,MAAM,wBACN,MAAM,WACP;;;AAIH,MAAM,oBAAkD;CACtD,MAAM;CACN,QAAQ;CACR,MAAM;CACN,QAAQ;CACR,QAAQ;CACR,MAAM;CACN,KAAK;CACL,MAAM;CACN,OAAO;CACP,OAAO;CACP,QAAQ;CACR,MAAM;CACP;;AAGD,SAAgB,mBAAmB,QAA8B;CAC/D,MAAM,SAAS,kBAAkB;AACjC,KAAI,OAAQ,QAAO;AACnB,KAAI;EACF,MAAM,GAAG,QAAQ,UAAU,IAAI,MAAM,OAAO,CAAC,GAAG,QAAQ,CAAC;AACzD,MAAI,CAAC,UAAU,SAAS,IAAM,QAAO;EACrC,MAAM,QAAS,UAAU,KAAK,MAAO,OAAO;AAC5C,MAAI,OAAO,OAAO,MAAM,GAAI,QAAO;AACnC,MAAI,MAAM,IAAK,QAAO;AACtB,MAAI,MAAM,IAAK,QAAO;AACtB,MAAI,MAAM,IAAK,QAAO;AACtB,SAAO;SACD;AACN,SAAO;;;AAIX,SAAS,UACP,OACA,OACA,UACA,SACA,SACA,YACU;CACV,MAAM,YAAY,MAAM;AACxB,QAAO;EACL,GAAG,MAAM,KAAK,GAAG,MAAM,WAAW,IAAI,EAAE,IAAI,EAAE,GAAG;EACjD,GAAG,MAAM,KAAK,GAAG,MAAM,YAAY,IAAI,EAAE,IAAI,EAAE,GAAG;EAClD,qBAAqB,SAAS;EAC9B,qBAAqB,QAAQ;EAC7B,oBAAoB,QAAQ;EAC5B,uBAAuB,WAAW;EAClC,sBAAsB,UAAU;EAChC,kBAAkB,UAAU;EAC7B"}
|