Test using HSL + curves to build a scale

This commit is contained in:
Nate Butler 2023-02-11 23:34:46 -05:00
parent 187fac1579
commit a120996f0d
6 changed files with 578 additions and 351 deletions

View file

@ -9,9 +9,9 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@k-vyn/coloralgorithm": "^1.0.0",
"@types/chroma-js": "^2.1.3", "@types/chroma-js": "^2.1.3",
"@types/node": "^17.0.23", "@types/node": "^17.0.23",
"bezier-easing": "^2.1.0",
"case-anything": "^2.1.10", "case-anything": "^2.1.10",
"chroma-js": "^2.4.2", "chroma-js": "^2.4.2",
"toml": "^3.0.0", "toml": "^3.0.0",
@ -37,15 +37,6 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/@k-vyn/coloralgorithm": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@k-vyn/coloralgorithm/-/coloralgorithm-1.0.0.tgz",
"integrity": "sha512-a9aAOXxQ+c2Mw5sMC39elT0wYkPa3qktFjtxVkfY3mQEFBr7NMQEczCARVdkmIKo1dIrgNSx3z12sTXohzSZDg==",
"dependencies": {
"bezier-easing": "^2.1.0",
"chroma-js": "^2.1.0"
}
},
"node_modules/@tsconfig/node10": { "node_modules/@tsconfig/node10": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
@ -227,15 +218,6 @@
"@cspotcode/source-map-consumer": "0.8.0" "@cspotcode/source-map-consumer": "0.8.0"
} }
}, },
"@k-vyn/coloralgorithm": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@k-vyn/coloralgorithm/-/coloralgorithm-1.0.0.tgz",
"integrity": "sha512-a9aAOXxQ+c2Mw5sMC39elT0wYkPa3qktFjtxVkfY3mQEFBr7NMQEczCARVdkmIKo1dIrgNSx3z12sTXohzSZDg==",
"requires": {
"bezier-easing": "^2.1.0",
"chroma-js": "^2.1.0"
}
},
"@tsconfig/node10": { "@tsconfig/node10": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",

View file

@ -12,6 +12,7 @@
"dependencies": { "dependencies": {
"@types/chroma-js": "^2.1.3", "@types/chroma-js": "^2.1.3",
"@types/node": "^17.0.23", "@types/node": "^17.0.23",
"bezier-easing": "^2.1.0",
"case-anything": "^2.1.10", "case-anything": "^2.1.10",
"chroma-js": "^2.4.2", "chroma-js": "^2.4.2",
"toml": "^3.0.0", "toml": "^3.0.0",

View file

@ -1,6 +1,8 @@
// Adapted from @k-vyn/coloralgorithm // Adapted from @k-vyn/coloralgorithm
import bezier from "bezier-easing";
import chroma, { Scale } from "chroma-js"; import chroma, { Scale } from "chroma-js";
import { Curve } from "./curves";
import { ColorFamily, ColorProps, ColorSet } from "./types"; import { ColorFamily, ColorProps, ColorSet } from "./types";
function validColor(color: string) { function validColor(color: string) {
@ -58,6 +60,91 @@ export function generateColors(props: ColorProps, inverted: boolean) {
return colors; return colors;
} }
export function generateColorsUsingCurve(
startColor: string,
endColor: string,
curve: number[]
) {
const NUM_STEPS = 101;
const easing = bezier(curve[0], curve[1], curve[2], curve[3]);
const curveProgress = [];
for (let i = 0; i <= NUM_STEPS; i++) {
curveProgress.push(easing(i / NUM_STEPS));
}
const colors: chroma.Color[] = [];
for (let i = 0; i < NUM_STEPS; i++) {
// Use HSL as an input as it is easier to construct programatically
// const color = chroma.hsl();
const color = chroma.mix(startColor, endColor, curveProgress[i], "lch");
colors.push(color);
}
return colors;
}
export function generateColors2(
hue: {
start: number;
end: number;
curve: Curve;
},
saturation: {
start: number;
end: number;
curve: Curve;
},
lightness: {
start: number;
end: number;
curve: Curve;
}
) {
const NUM_STEPS = 9;
const hueEasing = bezier(
hue.curve.value[0],
hue.curve.value[1],
hue.curve.value[2],
hue.curve.value[3]
);
const saturationEasing = bezier(
saturation.curve.value[0],
saturation.curve.value[1],
saturation.curve.value[2],
saturation.curve.value[3]
);
const lightnessEasing = bezier(
lightness.curve.value[0],
lightness.curve.value[1],
lightness.curve.value[2],
lightness.curve.value[3]
);
const colors: chroma.Color[] = [];
for (let i = 0; i < NUM_STEPS; i++) {
const hueValue =
hueEasing(i / NUM_STEPS) * (hue.end - hue.start) + hue.start;
const saturationValue =
saturationEasing(i / NUM_STEPS) * (saturation.end - saturation.start) +
saturation.start;
const lightnessValue =
lightnessEasing(i / NUM_STEPS) * (lightness.end - lightness.start) +
lightness.start;
const color = chroma.hsl(
hueValue,
saturationValue / 100,
lightnessValue / 100
);
colors.push(color);
}
const scale = chroma.scale(colors).mode("lch");
return scale;
}
/** Generates two color ramps: /** Generates two color ramps:
* One for for light, and one for dark. * One for for light, and one for dark.
* By generating two ramps, rather than two default themes, we can use the same reference palette values for tokens in components. * By generating two ramps, rather than two default themes, we can use the same reference palette values for tokens in components.

View file

@ -7,6 +7,8 @@ export interface Curve {
} }
export interface Curves { export interface Curves {
lightness: Curve;
saturation: Curve;
linear: Curve; linear: Curve;
easeInCubic: Curve; easeInCubic: Curve;
easeOutCubic: Curve; easeOutCubic: Curve;
@ -35,6 +37,16 @@ export interface Curves {
} }
export const curve: Curves = { export const curve: Curves = {
lightness: {
name: "nate",
formatted_name: "Nate",
value: [0.2, 0, 0.85, 1.1],
},
saturation: {
name: "nate",
formatted_name: "Nate",
value: [0.67, 0.6, 0.55, 1.0],
},
linear: { linear: {
name: "linear", name: "linear",
formatted_name: "Linear", formatted_name: "Linear",

View file

@ -1,5 +1,10 @@
import chroma from "chroma-js"; import chroma from "chroma-js";
import { generateColorSet } from "../algorithm"; import {
generateColors2,
generateColorSet,
generateColorsUsingCurve,
} from "../algorithm";
import { curve } from "../curves";
import { ColorFamily } from "../types"; import { ColorFamily } from "../types";
// These are the source colors for the color scales in the system. // These are the source colors for the color scales in the system.
@ -59,6 +64,28 @@ const red: ColorFamily = generateColorSet({
export const redLight = chroma.scale(red.colors).mode("lch"); export const redLight = chroma.scale(red.colors).mode("lch");
export const redDark = chroma.scale(red.invertedColors).mode("lch"); export const redDark = chroma.scale(red.invertedColors).mode("lch");
// Red 2 ======================================== //
const red2 = generateColors2(
{
start: 0,
end: 0,
curve: curve.linear,
},
{
start: 95,
end: 75,
curve: curve.saturation,
},
{
start: 97,
end: 25,
curve: curve.lightness,
}
);
export const red2Light = red2;
// Orange ======================================== // // Orange ======================================== //
const orange: ColorFamily = generateColorSet({ const orange: ColorFamily = generateColorSet({
@ -73,6 +100,28 @@ const orange: ColorFamily = generateColorSet({
export const orangeLight = chroma.scale(orange.colors).mode("lch"); export const orangeLight = chroma.scale(orange.colors).mode("lch");
export const orangeDark = chroma.scale(orange.invertedColors).mode("lch"); export const orangeDark = chroma.scale(orange.invertedColors).mode("lch");
// Orange 2 ======================================== //
const orange2 = generateColors2(
{
start: 25,
end: 25,
curve: curve.linear,
},
{
start: 100,
end: 100,
curve: curve.saturation,
},
{
start: 97,
end: 25,
curve: curve.lightness,
}
);
export const orange2Light = orange2;
// Amber ======================================== // // Amber ======================================== //
const amber: ColorFamily = generateColorSet({ const amber: ColorFamily = generateColorSet({
@ -87,6 +136,28 @@ const amber: ColorFamily = generateColorSet({
export const amberLight = chroma.scale(amber.colors).mode("lch"); export const amberLight = chroma.scale(amber.colors).mode("lch");
export const amberDark = chroma.scale(amber.invertedColors).mode("lch"); export const amberDark = chroma.scale(amber.invertedColors).mode("lch");
// Amber 2 ======================================== //
const amber2 = generateColors2(
{
start: 34,
end: 34,
curve: curve.linear,
},
{
start: 100,
end: 100,
curve: curve.saturation,
},
{
start: 97,
end: 25,
curve: curve.lightness,
}
);
export const amber2Light = amber2;
// Yellow ======================================== // // Yellow ======================================== //
const yellow: ColorFamily = generateColorSet({ const yellow: ColorFamily = generateColorSet({
@ -101,6 +172,28 @@ const yellow: ColorFamily = generateColorSet({
export const yellowLight = chroma.scale(yellow.colors).mode("lch"); export const yellowLight = chroma.scale(yellow.colors).mode("lch");
export const yellowDark = chroma.scale(yellow.invertedColors).mode("lch"); export const yellowDark = chroma.scale(yellow.invertedColors).mode("lch");
// Yellow 2 ======================================== //
const yellow2 = generateColors2(
{
start: 48,
end: 48,
curve: curve.linear,
},
{
start: 90,
end: 100,
curve: curve.saturation,
},
{
start: 97,
end: 30,
curve: curve.lightness,
}
);
export const yellow2Light = yellow2;
// Lime ======================================== // // Lime ======================================== //
const lime: ColorFamily = generateColorSet({ const lime: ColorFamily = generateColorSet({
@ -115,6 +208,50 @@ const lime: ColorFamily = generateColorSet({
export const limeLight = chroma.scale(lime.colors).mode("lch"); export const limeLight = chroma.scale(lime.colors).mode("lch");
export const limeDark = chroma.scale(lime.invertedColors).mode("lch"); export const limeDark = chroma.scale(lime.invertedColors).mode("lch");
// Lime 2 ======================================== //
const lime2 = generateColors2(
{
start: 85,
end: 85,
curve: curve.linear,
},
{
start: 85,
end: 70,
curve: curve.saturation,
},
{
start: 97,
end: 30,
curve: curve.lightness,
}
);
export const lime2Light = lime2;
// Citron 2 ======================================== //
const citron2 = generateColors2(
{
start: 65,
end: 65,
curve: curve.linear,
},
{
start: 85,
end: 70,
curve: curve.saturation,
},
{
start: 97,
end: 30,
curve: curve.lightness,
}
);
export const citron2Light = citron2;
// Green ======================================== // // Green ======================================== //
const green: ColorFamily = generateColorSet({ const green: ColorFamily = generateColorSet({

View file

@ -57,6 +57,14 @@ export default function Home() {
<ColorChips colorScale={color.pinkLight} /> <ColorChips colorScale={color.pinkLight} />
<ColorChips colorScale={color.brownLight} /> <ColorChips colorScale={color.brownLight} />
</div> </div>
<div style={{ display: 'flex', gap: '1px' }}>
<ColorChips colorScale={color.red2Light} />
<ColorChips colorScale={color.orange2Light} />
<ColorChips colorScale={color.amber2Light} />
<ColorChips colorScale={color.yellow2Light} />
<ColorChips colorScale={color.citron2Light} />
<ColorChips colorScale={color.lime2Light} />
</div>
</main> </main>
); );
} }