1// Adapted from @k-vyn/coloralgorithm
2
3import chroma, { Scale } from "chroma-js";
4import { ColorFamily, ColorProps, ColorSet } from "./types";
5
6function validColor(color: string) {
7 if (chroma.valid(color)) {
8 return color;
9 } else {
10 throw new Error(`Invalid color: ${color}`);
11 }
12}
13
14function assignColor(scale: Scale, steps: number, step: number) {
15 const color = scale(step / steps);
16 const lch = color.lch();
17 const rgbaArray = color.rgba();
18 const hex = color.hex();
19
20 const result = {
21 step,
22 hex,
23 lch,
24 rgbaArray,
25 };
26
27 return result;
28}
29
30/** Outputs 101 colors (0-100) */
31export function generateColors(props: ColorProps, inverted: boolean) {
32 const steps = 101;
33 const colors: ColorSet = [];
34
35 const { start, middle, end } = props.color;
36
37 const startColor = validColor(start);
38 const middleColor = validColor(middle);
39 const endColor = validColor(end);
40
41 // TODO: Use curve when generating colors
42
43 let scale: Scale;
44
45 if (inverted) {
46 scale = chroma.scale([endColor, middleColor, startColor]).mode("lch");
47 } else {
48 scale = chroma.scale([startColor, middleColor, endColor]).mode("lch");
49 }
50 for (let i = 0; i < steps; i++) {
51 const color = assignColor(scale, steps, i);
52 colors.push(color);
53 }
54 return colors;
55}
56
57/** Generates two color ramps:
58 * One for for light, and one for dark.
59 * By generating two ramps, rather than two default themes, we can use the same reference palette values for tokens in components.
60 *
61 * Each ramp has 101 colors (0-100)
62 */
63export function generateColorSet(props: ColorProps) {
64 const generatedColors = generateColors(props, false);
65 const generatedInvertedColors = generateColors(props, true);
66
67 const colors = generatedColors.map((color) => color.hex);
68 const invertedColors = generatedInvertedColors.map((color) => color.hex);
69
70 const result: ColorFamily = {
71 name: props.name,
72 colors: colors,
73 invertedColors: invertedColors,
74 colorsMeta: generatedColors,
75 invertedMeta: generatedInvertedColors,
76 };
77
78 return result;
79}