From 7b744c5470875656b3dbc9b9cd9461b0fba23e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Vassbotn=20R=C3=B8yne-Helgesen?= Date: Fri, 8 Dec 2023 14:56:27 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Add=20new=20feature:=20`?= =?UTF-8?q?darkmode`-palette?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/darkmode.ts | 83 +++++++++++++++++++++ src/utils/get-chroma-bezier-scale-colors.ts | 36 +++++++++ src/utils/get-chroma-scale-colors.ts | 35 +++++++++ 3 files changed, 154 insertions(+) create mode 100644 src/features/darkmode.ts create mode 100644 src/utils/get-chroma-bezier-scale-colors.ts create mode 100644 src/utils/get-chroma-scale-colors.ts diff --git a/src/features/darkmode.ts b/src/features/darkmode.ts new file mode 100644 index 0000000..7d15e13 --- /dev/null +++ b/src/features/darkmode.ts @@ -0,0 +1,83 @@ +import chroma from 'chroma-js'; + +import { + MoebiusChromaColorInputType, + MoebiusColorValueHexType, + MoebiusPaletteOptionsType +} from '../types'; +import { monochromatic } from './monochromatic'; +import { getChromaBezierScaleColors } from 'utils/get-chroma-bezier-scale-colors'; +import { getChromaScaleColors } from 'utils/get-chroma-scale-colors'; + +/** + * Generates a dark mode color palette based on the provided base and secondary colors. + * @param {MoebiusChromaColorInputType} baseColor - The base color for the palette. + * @param {MoebiusChromaColorInputType} secondaryColor - The secondary color for the palette. + * @param {Record | MoebiusPaletteOptionsType} [options={}] - Palette options. + * @param {boolean} [options.bezier=false] - Whether to use bezier interpolation for color scales. + * @param {string} [options.colorScaleMode] - The color scale mode for chroma.mix. + * @param {boolean} [options.noDuplicates=true] - Whether to remove duplicate colors from the palette. + * @returns {Record} - The generated dark mode color palette. + * @example + * ```ts + * const baseColor = '#3498db'; + * const secondaryColor = '#2ecc71'; + * const options = { bezier: true, colorScaleMode: 'lch' }; + * const palette = darkmode(baseColor, secondaryColor, options); + * console.log(palette); + * ``` + */ +export const darkmode = ( + baseColor: MoebiusChromaColorInputType, + secondaryColor: MoebiusChromaColorInputType, + options: Record | MoebiusPaletteOptionsType = {} +): Record => { + const { + bezier = false, + colorScaleMode, + noDuplicates = true + } = options as MoebiusPaletteOptionsType; + const ratio = 0.99; + const ratioMultiplier = 0.02; + const primaryAccents = monochromatic(baseColor, options); + const surfaceAccents = monochromatic(secondaryColor, options); + const mixedAccents = surfaceAccents.map((hex, index) => { + return chroma + .mix( + primaryAccents[index], + hex, + ratio - index * ratioMultiplier, + colorScaleMode + ) + .hex(); + }); + const palette = { + primary: primaryAccents, + surface: surfaceAccents, + mixed: mixedAccents + }; + + if (noDuplicates) { + palette.primary = [...new Set(primaryAccents)]; + palette.surface = [...new Set(surfaceAccents)]; + palette.mixed = [...new Set(mixedAccents)]; + } + + if (bezier) { + if ( + palette.primary.length > 1 && + palette.surface.length > 1 && + palette.mixed.length > 1 + ) { + palette.primary = getChromaBezierScaleColors(palette.primary, options); + palette.surface = getChromaBezierScaleColors(palette.surface, options); + palette.mixed = getChromaBezierScaleColors(palette.mixed, options); + } else { + palette.primary = getChromaScaleColors(palette.primary, options); + palette.surface = getChromaScaleColors(palette.surface, options); + palette.mixed = getChromaScaleColors(palette.mixed, options); + } + } + + return palette; +}; diff --git a/src/utils/get-chroma-bezier-scale-colors.ts b/src/utils/get-chroma-bezier-scale-colors.ts new file mode 100644 index 0000000..fe561c0 --- /dev/null +++ b/src/utils/get-chroma-bezier-scale-colors.ts @@ -0,0 +1,36 @@ +import chroma from 'chroma-js'; +import { MoebiusColorValueHexType, MoebiusPaletteOptionsType } from 'types'; + +/** + * Generates a color scale using Chroma.js's bezier interpolation. + * @param {MoebiusColorValueHexType[]} colors - The input colors for the bezier interpolation. + * @param {Record | MoebiusPaletteOptionsType} [options={}] - Options for generating the color scale. + * @param {number} [options.numberOfColors=8] - The number of colors in the resulting scale. + * @param {string} [options.colorScaleMode] - The color scale mode for Chroma.js. + * @param {boolean} [options.correctLightness=true] - Whether to correct lightness in the color scale. + * @returns {MoebiusColorValueHexType[]} - The generated color scale. + * @example + * ```ts + * const inputColors = ['#3498db', '#2ecc71', '#e74c3c']; + * const options = { numberOfColors: 5, colorScaleMode: 'lch', correctLightness: false }; + * const scaleColors = getChromaBezierScaleColors(inputColors, options); + * console.log(scaleColors); + * ``` + */ +export const getChromaBezierScaleColors = ( + colors: MoebiusColorValueHexType[], + options: Record | MoebiusPaletteOptionsType = {} +) => { + const { + numberOfColors = 8, + colorScaleMode, + correctLightness = true + } = options as MoebiusPaletteOptionsType; + + return chroma + .bezier(colors) + .scale() + .mode(colorScaleMode) + .correctLightness(correctLightness) + .colors(numberOfColors); +}; diff --git a/src/utils/get-chroma-scale-colors.ts b/src/utils/get-chroma-scale-colors.ts new file mode 100644 index 0000000..193d8a7 --- /dev/null +++ b/src/utils/get-chroma-scale-colors.ts @@ -0,0 +1,35 @@ +import chroma from 'chroma-js'; +import { MoebiusColorValueHexType, MoebiusPaletteOptionsType } from 'types'; + +/** + * Generates a color scale using Chroma.js. + * @param {MoebiusColorValueHexType[]} colors - The input colors for the color scale. + * @param {Record | MoebiusPaletteOptionsType} [options={}] - Options for generating the color scale. + * @param {number} [options.numberOfColors=8] - The number of colors in the resulting scale. + * @param {string} [options.colorScaleMode] - The color scale mode for Chroma.js. + * @param {boolean} [options.correctLightness=true] - Whether to correct lightness in the color scale. + * @returns {MoebiusColorValueHexType[]} - The generated color scale. + * @example + * ```ts + * const inputColors = ['#3498db', '#2ecc71', '#e74c3c']; + * const options = { numberOfColors: 5, colorScaleMode: 'lch', correctLightness: false }; + * const scaleColors = getChromaScaleColors(inputColors, options); + * console.log(scaleColors); + * ``` + */ +export const getChromaScaleColors = ( + colors: MoebiusColorValueHexType[], + options: Record | MoebiusPaletteOptionsType = {} +) => { + const { + numberOfColors = 8, + colorScaleMode, + correctLightness = true + } = options as MoebiusPaletteOptionsType; + + return chroma + .scale(colors) + .mode(colorScaleMode) + .correctLightness(correctLightness) + .colors(numberOfColors); +};