colorizr
Version:
Manipulate colors like a boss
95 lines (79 loc) • 2.62 kB
text/typescript
import convertCSS from '~/convertCSS';
import hsl2hex from '~/converters/hsl2hex';
import { MESSAGES, MONOCHROMATIC_LIGHTNESS_MAX } from '~/modules/constants';
import { invariant } from '~/modules/invariant';
import { resolveColor } from '~/modules/parsed-color';
import { isPlainObject, isString } from '~/modules/validators';
import rotate from '~/rotate';
import { ColorType, HEX } from '~/types';
export interface PaletteOptions {
/**
* Output color format.
*
* If not specified, the output will use the same format as the input color.
*/
format?: ColorType;
/**
* Adjusts the lightness of the base color before generating the palette.
*
* Value should be between 0 and 100.
*/
lightness?: number;
/**
* Generate a monochromatic palette.
*
* For more options, use the `scale` function.
*/
monochromatic?: boolean;
/**
* Adjusts the saturation of the base color before generating the palette.
*
* Value should be between 0 and 100.
*/
saturation?: number;
/**
* The number of colors to generate in the palette.
*
* Minimum value is 2.
* @default 6
*/
size?: number;
}
/**
* Generate a color palette from a base color.
*
* @param input - The base color string.
* @param formatOrOptions - Output format or palette options object.
* @returns An array of color strings.
*/
export default function palette(
input: string,
formatOrOptions?: ColorType | PaletteOptions,
): string[] {
invariant(isString(input), MESSAGES.inputString);
const options = isString(formatOrOptions, false)
? { format: formatOrOptions }
: (formatOrOptions ?? {});
invariant(isPlainObject(options), MESSAGES.options);
const { format, lightness, monochromatic, saturation, size = 6 } = options;
invariant(size >= 2, MESSAGES.paletteSize);
const parsed = resolveColor(input);
const { hsl } = parsed;
const colorFormat = format ?? parsed.type;
const output: string[] = [];
if (monochromatic) {
const step = MONOCHROMATIC_LIGHTNESS_MAX / size;
for (let index = size; index > 0; index--) {
output.push(hsl2hex({ ...hsl, l: step * index }));
}
} else {
const step = 360 / size;
output.push(hsl2hex({ ...hsl, l: lightness ?? hsl.l, s: saturation ?? hsl.s }));
for (let index = 1; index < size; index++) {
const color = rotate(input, step * index, 'hex') as HEX;
const rotatedHsl = resolveColor(color).hsl;
output.push(hsl2hex({ ...rotatedHsl, l: lightness ?? hsl.l, s: saturation ?? hsl.s }));
}
}
return output.map(color => convertCSS(color, colorFormat));
}