algorithm.ts

 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}