UNPKG

themer

Version:

Customizable theme creator for editors, terminals, wallpaper, and more.

167 lines (153 loc) 4.36 kB
import Color from 'color'; import capitalize from 'lodash/capitalize.js'; import camelCase from 'lodash/camelCase.js'; import kebabCase from 'lodash/kebabCase.js'; import snakeCase from 'lodash/snakeCase.js'; import upperFirst from 'lodash/upperFirst.js'; type FullShades = { shade0: string; shade1: string; shade2: string; shade3: string; shade4: string; shade5: string; shade6: string; shade7: string; }; type PartialShades = { shade0: string; shade7: string; }; export type Accents = { accent0: string; accent1: string; accent2: string; accent3: string; accent4: string; accent5: string; accent6: string; accent7: string; }; type LightVariantBase<T extends Variant> = { light: T }; type DarkVariantBase<T extends Variant> = { dark: T }; type VariantBase<T extends Variant> = | LightVariantBase<T> | DarkVariantBase<T> | (LightVariantBase<T> & DarkVariantBase<T>); type ColorSetBase<T extends Variant> = { name: string; variants: VariantBase<T>; }; type PartialVariant = PartialShades & Accents; export type FullVariant = FullShades & Accents; export type Variant = PartialVariant | FullVariant; export type PartialColorSet = ColorSetBase<PartialVariant>; export type FullColorSet = ColorSetBase<FullVariant>; export type ColorSet = PartialColorSet | FullColorSet; export type AnnotatedVariant = { name: string; colors: FullVariant; isDark: boolean; title: { human: string; kebab: string; snake: string; upperCamel: string; }; }; export type AnnotatedColorSet = FullColorSet & { title: { human: string; kebab: string; snake: string; upperCamel: string; }; }; export function colorSetToVariants( colorSet: AnnotatedColorSet, ): AnnotatedVariant[] { return Object.entries(colorSet.variants).map(([name, colors]) => ({ name, colors, isDark: name === 'dark', title: { human: `${colorSet.title.human} ${capitalize(name)}`, kebab: `${colorSet.title.kebab}-${name}`, snake: `${colorSet.title.snake}_${name}`, upperCamel: `${colorSet.title.upperCamel}${capitalize(name)}`, }, })); } export function prepareColorSet(colorSet: ColorSet): AnnotatedColorSet { const variants: FullColorSet['variants'] = 'dark' in colorSet.variants && 'light' in colorSet.variants ? { dark: prepareVariant(colorSet.variants.dark), light: prepareVariant(colorSet.variants.light), } : 'dark' in colorSet.variants ? { dark: prepareVariant(colorSet.variants.dark) } : { light: prepareVariant(colorSet.variants.light) }; return { ...colorSet, variants, title: { human: `Themer ${colorSet.name}`, kebab: kebabCase(`themer-${colorSet.name}`), snake: snakeCase(`themer_${colorSet.name}`), upperCamel: upperFirst(camelCase(`Themer ${colorSet.name}`)), }, }; } export function prepareVariant(variant: Variant): FullVariant { if ('shade1' in variant) { return variant; } const start = new Color(variant.shade0); const end = new Color(variant.shade7); const rDelta = (end.red() - start.red()) / 7; const gDelta = (end.green() - start.green()) / 7; const bDelta = (end.blue() - start.blue()) / 7; return { ...variant, shade1: new Color({ r: start.red() + rDelta * 1, g: start.green() + gDelta * 1, b: start.blue() + bDelta * 1, }).hex(), shade2: new Color({ r: start.red() + rDelta * 2, g: start.green() + gDelta * 2, b: start.blue() + bDelta * 2, }).hex(), shade3: new Color({ r: start.red() + rDelta * 3, g: start.green() + gDelta * 3, b: start.blue() + bDelta * 3, }).hex(), shade4: new Color({ r: start.red() + rDelta * 4, g: start.green() + gDelta * 4, b: start.blue() + bDelta * 4, }).hex(), shade5: new Color({ r: start.red() + rDelta * 5, g: start.green() + gDelta * 5, b: start.blue() + bDelta * 5, }).hex(), shade6: new Color({ r: start.red() + rDelta * 6, g: start.green() + gDelta * 6, b: start.blue() + bDelta * 6, }).hex(), }; } export function brightMix( colors: FullVariant, key: keyof FullVariant, isDark: boolean, ): string { return Color(colors[key]) .mix(isDark ? Color(colors.shade7) : Color(colors.shade0), 0.2) .hex(); }