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