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  // Roughly  calculate if a color is dark or light
21  const isLight = lch[0] > 50;
22
23  const result = {
24    step,
25    hex,
26    lch,
27    rgbaArray,
28    isLight,
29  };
30
31  return result;
32}
33
34/** Outputs 101 colors (0-100) */
35export function generateColors(props: ColorProps, inverted: boolean) {
36  const steps = 101;
37  const colors: ColorSet = [];
38
39  const { start, middle, end } = props.color;
40
41  const startColor = validColor(start);
42  const middleColor = validColor(middle);
43  const endColor = validColor(end);
44
45  // TODO: Use curve when generating colors
46
47  let scale: Scale;
48
49  if (inverted) {
50    scale = chroma.scale([endColor, middleColor, startColor]).mode("lch");
51  } else {
52    scale = chroma.scale([startColor, middleColor, endColor]).mode("lch");
53  }
54  for (let i = 0; i < steps; i++) {
55    const color = assignColor(scale, steps, i);
56    colors.push(color);
57  }
58  return colors;
59}
60
61/** Generates two color ramps:
62 * One for for light, and one for dark.
63 * By generating two ramps, rather than two default themes, we can use the same reference palette values for tokens in components.
64 *
65 * Each ramp has 101 colors (0-100)
66 */
67export function generateColorSet(props: ColorProps) {
68  const generatedColors = generateColors(props, false);
69  const generatedInvertedColors = generateColors(props, true);
70
71  const colors = generatedColors.map((color) => color.hex);
72  const invertedColors = generatedInvertedColors.map((color) => color.hex);
73
74  const result: ColorFamily = {
75    name: props.name,
76    colors: colors,
77    invertedColors: invertedColors,
78    colorsMeta: generatedColors,
79    invertedMeta: generatedInvertedColors,
80  };
81
82  return result;
83}