Comparing version 3.0.0-0 to 3.0.0-1
@@ -7,3 +7,2 @@ type ColorType = 'hex' | 'hsl' | 'oklab' | 'oklch' | 'rgb'; | ||
type ColorTuple = [number, number, number]; | ||
type ColorTupleWithRound = [number, number, number, boolean]; | ||
type ConverterParameters<TModel extends ColorModel> = TModel | ColorTuple; | ||
@@ -59,3 +58,2 @@ interface Colors { | ||
type PlainObject<T = any> = Record<string, T>; | ||
type Scheme = 'analogous' | 'complementary' | 'rectangle' | 'split' | 'split-complementary' | 'square' | 'tetradic' | 'triadic'; | ||
@@ -66,3 +64,3 @@ interface Options$1 { | ||
*/ | ||
type?: ColorType; | ||
format?: ColorType; | ||
} | ||
@@ -120,2 +118,4 @@ declare class Colorizr { | ||
private get selectedColor(); | ||
brightnessDifference(input: string): number; | ||
colorDifference(input: string): number; | ||
/** | ||
@@ -125,2 +125,3 @@ * Test 2 colors for compliance | ||
compare(input: string): Analysis; | ||
contrast(input: string): number; | ||
format(type: ColorType, precision?: number): string; | ||
@@ -187,3 +188,3 @@ /** | ||
/** | ||
* Convert a color string from one format to another | ||
* Convert a color string to another format. | ||
*/ | ||
@@ -216,3 +217,3 @@ declare function convert(input: string, format: ColorType): string; | ||
* The output color type. | ||
* @default 'rgb' | ||
* @default 'hex' | ||
*/ | ||
@@ -248,9 +249,18 @@ format?: ColorType; | ||
/** | ||
* Get the name of a color. | ||
* Returns the hex value if the color is not found. | ||
*/ | ||
declare function name(input: string): string; | ||
interface PaletteOptions { | ||
format?: ColorType; | ||
lightness?: number; | ||
saturation?: number; | ||
/** | ||
* The number of colors to generate | ||
* @default 6 | ||
*/ | ||
size?: number; | ||
type?: string; | ||
type?: 'monochromatic'; | ||
} | ||
@@ -266,3 +276,3 @@ declare function palette(input: string, options?: PaletteOptions): string[]; | ||
type ParseCSSReturn<T extends ColorType> = T extends 'hsl' ? HSL : T extends 'lab' ? LAB : T extends 'lch' ? LCH : T extends 'rgb' ? RGB : T extends 'hex' ? HEX : never; | ||
type ParseCSSReturn<T extends ColorType> = T extends 'hex' ? HEX : T extends 'hsl' ? HSL : T extends 'oklab' ? LAB : T extends 'oklch' ? LCH : T extends 'rgb' ? RGB : never; | ||
/** | ||
@@ -276,3 +286,3 @@ * Parse CSS color | ||
*/ | ||
declare function random(): string; | ||
declare function random(type?: ColorType): string; | ||
@@ -289,8 +299,33 @@ /** | ||
type Scheme = 'analogous' | 'complementary' | 'rectangle' | 'split' | 'split-complementary' | 'square' | 'tetradic' | 'triadic'; | ||
interface SchemeOptions { | ||
format?: ColorType; | ||
/** | ||
* The type of scheme to generate. | ||
* @default 'complementary' | ||
*/ | ||
type?: Scheme; | ||
} | ||
/** | ||
* Get the scheme for a color. | ||
*/ | ||
declare function scheme(input: string, type?: Scheme): string[]; | ||
declare function scheme(input: string, typeOrOptions?: Scheme | SchemeOptions): string[]; | ||
declare function swatch(input: string, variant?: 'up' | 'down'): string[]; | ||
type ColorTokens = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900; | ||
type Swatch = { | ||
[key in ColorTokens]: string; | ||
}; | ||
interface SwatchOptions { | ||
format?: ColorType; | ||
/** | ||
* The scale of the swatch. | ||
* Linear scale will have equal distance between each shade. | ||
* @default 'dynamic' | ||
*/ | ||
scale?: 'dynamic' | 'linear'; | ||
} | ||
/** | ||
* Generate a palette of shades of a color | ||
*/ | ||
declare function swatch(input: string, options?: SwatchOptions): Swatch; | ||
@@ -323,15 +358,15 @@ interface Options { | ||
*/ | ||
declare function transparentize(input: string, alpha: Alpha, output?: ColorType): string; | ||
declare function transparentize(input: string, alpha: Alpha, format?: ColorType): string; | ||
/** Convert HEX to HSL */ | ||
declare function hex2hsl(input: HEX): HSL; | ||
declare function hex2hsl(input: string): HSL; | ||
/** Convert HEX to oklab */ | ||
declare function hex2oklab(input: HEX, precision?: number): LAB; | ||
declare function hex2oklab(input: string, precision?: number): LAB; | ||
/** Convert HEX to oklch */ | ||
declare function hex2oklch(input: HEX, precision?: number): LCH; | ||
declare function hex2oklch(input: string, precision?: number): LCH; | ||
/** Convert HEX to RGB */ | ||
declare function hex2rgb(input: HEX): RGB; | ||
declare function hex2rgb(input: string): RGB; | ||
@@ -427,2 +462,2 @@ /** Convert HSL to HEX */ | ||
export { type Alpha, type Amount, type Analysis, type CSS, type ColorKeysTuple, type ColorModel, type ColorModelKey, type ColorModelKeys, type ColorTuple, type ColorTupleWithRound, type ColorType, type Colors, type ConverterParameters, type Degrees, type HEX, type HSL, type LAB, type LCH, type PlainObject, type RGB, type Scheme, addAlphaToHex, brightnessDifference, chroma, colorDifference, compare, contrast, convert, convertAlphaToHex, darken, Colorizr as default, desaturate, extractAlphaFromHex, extractColorParts, formatCSS, formatHex, hex2hsl, hex2oklab, hex2oklch, hex2rgb, hexadecimalToNumber, hsl2hex, hsl2oklab, hsl2oklch, hsl2rgb, isHSL, isHex, isLAB, isLCH, isRGB, isValidColor, lighten, luminance, name, oklab2hex, oklab2hsl, oklab2oklch, oklab2rgb, oklch2hex, oklch2hsl, oklch2oklab, oklch2rgb, opacify, opacity, palette, parseCSS, random, removeAlphaFromHex, rgb2hex, rgb2hsl, rgb2oklab, rgb2oklch, rotate, saturate, scheme, swatch, textColor, transparentize }; | ||
export { type Alpha, type Amount, type Analysis, type CSS, type ColorKeysTuple, type ColorModel, type ColorModelKey, type ColorModelKeys, type ColorTuple, type ColorType, type Colors, type ConverterParameters, type Degrees, type HEX, type HSL, type LAB, type LCH, type PlainObject, type RGB, addAlphaToHex, brightnessDifference, chroma, colorDifference, compare, contrast, convert, convertAlphaToHex, darken, Colorizr as default, desaturate, extractAlphaFromHex, extractColorParts, formatCSS, formatHex, hex2hsl, hex2oklab, hex2oklch, hex2rgb, hexadecimalToNumber, hsl2hex, hsl2oklab, hsl2oklch, hsl2rgb, isHSL, isHex, isLAB, isLCH, isRGB, isValidColor, lighten, luminance, name, oklab2hex, oklab2hsl, oklab2oklch, oklab2rgb, oklch2hex, oklch2hsl, oklch2oklab, oklch2rgb, opacify, opacity, palette, parseCSS, random, removeAlphaFromHex, rgb2hex, rgb2hsl, rgb2oklab, rgb2oklch, rotate, saturate, scheme, swatch, textColor, transparentize }; |
@@ -116,2 +116,12 @@ "use strict"; | ||
}; | ||
var LRGB_TO_LMS = { | ||
l: [0.4122214708, 0.5363325363, 0.0514459929], | ||
m: [0.2119034982, 0.6806995451, 0.1073969566], | ||
s: [0.0883024619, 0.2817188376, 0.6299787005] | ||
}; | ||
var LSM_TO_LAB = { | ||
l: [0.2104542553, 0.793617785, 0.0040720468], | ||
a: [1.9779984951, 2.428592205, 0.4505937099], | ||
b: [0.0259040371, 0.7827717662, 0.808675766] | ||
}; | ||
var LSM_TO_RGB = { | ||
@@ -628,9 +638,9 @@ r: [4.076741636075958, -3.307711539258063, 0.2309699031821043], | ||
const [lr, lg, lb] = [rgb2lrgb(value.r / 255), rgb2lrgb(value.g / 255), rgb2lrgb(value.b / 255)]; | ||
const l = cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb); | ||
const m = cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb); | ||
const s = cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb); | ||
const l = cbrt(LRGB_TO_LMS.l[0] * lr + LRGB_TO_LMS.l[1] * lg + LRGB_TO_LMS.l[2] * lb); | ||
const m = cbrt(LRGB_TO_LMS.m[0] * lr + LRGB_TO_LMS.m[1] * lg + LRGB_TO_LMS.m[2] * lb); | ||
const s = cbrt(LRGB_TO_LMS.s[0] * lr + LRGB_TO_LMS.s[1] * lg + LRGB_TO_LMS.s[2] * lb); | ||
const lab = { | ||
l: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s, | ||
a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s, | ||
b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s | ||
l: LSM_TO_LAB.l[0] * l + LSM_TO_LAB.l[1] * m - LSM_TO_LAB.l[2] * s, | ||
a: LSM_TO_LAB.a[0] * l - LSM_TO_LAB.a[1] * m + LSM_TO_LAB.a[2] * s, | ||
b: LSM_TO_LAB.b[0] * l + LSM_TO_LAB.b[1] * m - LSM_TO_LAB.b[2] * s | ||
}; | ||
@@ -746,7 +756,8 @@ return restrictValues(lab, precision); | ||
// src/converters/oklab2rgb.ts | ||
var { abs, sign: sign2 } = Math; | ||
var { abs } = Math; | ||
function lrgb2rgb(input) { | ||
const absoluteNumber = abs(input); | ||
const sign2 = input < 0 ? -1 : 1; | ||
if (absoluteNumber > 31308e-7) { | ||
return (sign2(input) || 1) * (1.055 * absoluteNumber ** (1 / 2.4) - 0.055); | ||
return sign2 * (absoluteNumber ** (1 / 2.4) * 1.055 - 0.055); | ||
} | ||
@@ -1001,11 +1012,2 @@ return input * 12.92; | ||
// src/chroma.ts | ||
function chroma(input) { | ||
invariant(isString(input), MESSAGES.inputString); | ||
const { r, g, b } = parseCSS(input, "rgb"); | ||
const max = Math.max(r, g, b); | ||
const min = Math.min(r, g, b); | ||
return round((max - min) / 255, 4); | ||
} | ||
// src/brightness-difference.ts | ||
@@ -1022,2 +1024,11 @@ function brightnessDifference(left, right, precision = PRECISION) { | ||
// src/chroma.ts | ||
function chroma(input) { | ||
invariant(isString(input), MESSAGES.inputString); | ||
const { r, g, b } = parseCSS(input, "rgb"); | ||
const max = Math.max(r, g, b); | ||
const min = Math.min(r, g, b); | ||
return round((max - min) / 255, 4); | ||
} | ||
// src/color-difference.ts | ||
@@ -1090,3 +1101,3 @@ function colorDifference(left, right) { | ||
invariant(isHex(input) || isValidColorModel(input), MESSAGES.invalid); | ||
const { alpha, format = "rgb", precision = PRECISION, separator: baseSeparator = " " } = options; | ||
const { alpha, format = "hex", precision = PRECISION, separator: baseSeparator = " " } = options; | ||
let value; | ||
@@ -1168,2 +1179,25 @@ if (isHex(input)) { | ||
// src/rotate.ts | ||
function rotate(input, degrees, format) { | ||
invariant(isString(input), MESSAGES.inputString); | ||
invariant(isNumber(degrees), "degrees must be a number"); | ||
const color = parseCSS(input, "hsl"); | ||
const output = isHex(input) || isNamedColor(input) ? "hex" : extractColorParts(input).model; | ||
return formatCSS( | ||
{ | ||
...color, | ||
h: constrainDegrees(color.h, degrees) | ||
}, | ||
{ format: format ?? output } | ||
); | ||
} | ||
// src/invert.ts | ||
function invert(input) { | ||
invariant(isString(input), MESSAGES.inputString); | ||
const format = isHex(input) || isNamedColor(input) ? "hex" : extractColorParts(input).model; | ||
const hex = parseCSS(input, "hex"); | ||
return formatCSS(rotate(hex, 180), { format }); | ||
} | ||
// src/lighten.ts | ||
@@ -1198,17 +1232,2 @@ function lighten(input, amount, format) { | ||
// src/rotate.ts | ||
function rotate(input, degrees, format) { | ||
invariant(isString(input), MESSAGES.inputString); | ||
invariant(isNumber(degrees), "degrees must be a number"); | ||
const color = parseCSS(input, "hsl"); | ||
const output = isHex(input) || isNamedColor(input) ? "hex" : extractColorParts(input).model; | ||
return formatCSS( | ||
{ | ||
...color, | ||
h: constrainDegrees(color.h, degrees) | ||
}, | ||
{ format: format ?? output } | ||
); | ||
} | ||
// src/saturate.ts | ||
@@ -1230,3 +1249,3 @@ function saturate(input, amount, format) { | ||
// src/transparentize.ts | ||
function transparentize(input, alpha, output) { | ||
function transparentize(input, alpha, format) { | ||
invariant(isString(input), MESSAGES.inputString); | ||
@@ -1237,3 +1256,3 @@ invariant(isNumber(alpha), MESSAGES.alpha); | ||
const value = round(clamp(opacity(input) - alpha, 0, 1)); | ||
return formatCSS(oklch, { format: output, alpha: value }); | ||
return formatCSS(oklch, { format, alpha: value }); | ||
} | ||
@@ -1259,3 +1278,3 @@ | ||
this.alpha = alpha; | ||
this.type = options.type ?? type; | ||
this.type = options.format ?? type; | ||
} | ||
@@ -1328,2 +1347,8 @@ /** | ||
} | ||
brightnessDifference(input) { | ||
return brightnessDifference(this.selectedColor, input); | ||
} | ||
colorDifference(input) { | ||
return colorDifference(this.selectedColor, input); | ||
} | ||
/** | ||
@@ -1335,2 +1360,5 @@ * Test 2 colors for compliance | ||
} | ||
contrast(input) { | ||
return contrast(this.selectedColor, input); | ||
} | ||
format(type, precision) { | ||
@@ -1371,3 +1399,3 @@ return formatCSS(this.rgb, { | ||
invert() { | ||
return rotate(this.selectedColor, 180); | ||
return invert(this.selectedColor); | ||
} | ||
@@ -1422,5 +1450,6 @@ /** | ||
invariant(isPlainObject(options), MESSAGES.options); | ||
const { lightness, saturation, size = 6, type } = options; | ||
const hsl = hex2hsl(parseCSS(input, "hex")); | ||
const { format, lightness, saturation, size = 6, type } = options; | ||
const hsl = parseCSS(input, "hsl"); | ||
const output = []; | ||
const colorFormat = isHex(input) || isNamedColor(input) ? "hex" : extractColorParts(input).model; | ||
switch (type) { | ||
@@ -1444,7 +1473,7 @@ case "monochromatic": { | ||
} | ||
return output; | ||
return output.map((color) => convert(color, format ?? colorFormat)); | ||
} | ||
// src/random.ts | ||
function random() { | ||
function random(type = "hex") { | ||
const hsl = { | ||
@@ -1455,17 +1484,18 @@ h: Math.floor(Math.random() * 360) + 1, | ||
}; | ||
return hsl2hex(hsl); | ||
return formatCSS(hsl, { format: type }); | ||
} | ||
// src/scheme.ts | ||
function scheme(input, type = "complementary") { | ||
function scheme(input, typeOrOptions) { | ||
invariant(isString(input), MESSAGES.inputString); | ||
const hex = parseCSS(input, "hex"); | ||
const output = []; | ||
const { format, type = "complementary" } = isString(typeOrOptions) ? { type: typeOrOptions } : typeOrOptions ?? {}; | ||
const output = isHex(input) || isNamedColor(input) ? "hex" : extractColorParts(input).model; | ||
const colors = []; | ||
switch (type) { | ||
case "analogous": { | ||
output.push(rotate(hex, -30), hex, rotate(hex, 30)); | ||
colors.push(rotate(input, -30), input, rotate(input, 30)); | ||
break; | ||
} | ||
case "complementary": { | ||
output.push(hex, rotate(hex, 180)); | ||
colors.push(input, rotate(input, 180)); | ||
break; | ||
@@ -1475,7 +1505,7 @@ } | ||
case "split-complementary": { | ||
output.push(hex, rotate(hex, 150), rotate(hex, 210)); | ||
colors.push(input, rotate(input, 150), rotate(input, 210)); | ||
break; | ||
} | ||
case "triadic": { | ||
output.push(hex, rotate(hex, 120), rotate(hex, 240)); | ||
colors.push(input, rotate(input, 120), rotate(input, 240)); | ||
break; | ||
@@ -1485,7 +1515,7 @@ } | ||
case "rectangle": { | ||
output.push(hex, rotate(hex, 60), rotate(hex, 180), rotate(hex, 240)); | ||
colors.push(input, rotate(input, 60), rotate(input, 180), rotate(input, 240)); | ||
break; | ||
} | ||
case "square": { | ||
output.push(hex, rotate(hex, 90), rotate(hex, 180), rotate(hex, 270)); | ||
colors.push(input, rotate(input, 90), rotate(input, 180), rotate(input, 270)); | ||
break; | ||
@@ -1497,43 +1527,59 @@ } | ||
} | ||
return output; | ||
return colors.map((color) => convert(color, format ?? output)); | ||
} | ||
// src/swatch.ts | ||
var HUE_MAP = [0, 4, 8, 12, 16, 20, 24, 28, 32, 36]; | ||
var LIGHTNESS_MAP = [90, 80, 70, 60, 50, 40, 30, 20, 10, 5]; | ||
var SATURATION_MAP = [32, 16, 8, 4, 0, 0, 4, 8, 16, 32]; | ||
function swatch(input, variant) { | ||
invariant(isString(input), MESSAGES.inputString); | ||
const hsl = parseCSS(input, "hsl"); | ||
const lightnessGoal = hsl.l; | ||
const closestLightness = LIGHTNESS_MAP.reduce( | ||
(previous, current) => Math.abs(current - lightnessGoal) < Math.abs(previous - lightnessGoal) ? current : previous | ||
); | ||
const baseColorIndex = LIGHTNESS_MAP.findIndex((l) => l === closestLightness); | ||
const colors = LIGHTNESS_MAP.map((l) => hsl2hex({ ...hsl, l })).map((color, index) => { | ||
const saturationDelta = SATURATION_MAP[index] - SATURATION_MAP[baseColorIndex]; | ||
return saturationDelta >= 0 ? saturate(color, saturationDelta) : desaturate(color, saturationDelta * -1); | ||
}); | ||
if (variant === "up") { | ||
return colors.map((color, index) => { | ||
const hueDelta = HUE_MAP[index] - HUE_MAP[baseColorIndex]; | ||
const nextColor = hex2hsl(color); | ||
return hueDelta >= 0 ? hsl2hex({ ...nextColor, h: constrainDegrees(nextColor.h, hueDelta) }) : hsl2hex({ | ||
...nextColor, | ||
h: constrainDegrees(nextColor.h, hueDelta * -1 / 2) | ||
}); | ||
}); | ||
var MIN_LIGHTNESS = 21; | ||
var MAX_LIGHTNESS = 97; | ||
function shadeColorDynamic(input, lightnessTuningFactor, chromaTuningFactor = 0) { | ||
if (lightnessTuningFactor === 0) { | ||
return rgb2hex(oklch2rgb({ ...input, l: input.l / 100 })); | ||
} | ||
if (variant === "down") { | ||
return colors.map((color, index) => { | ||
const hueDelta = HUE_MAP[index] - HUE_MAP[baseColorIndex]; | ||
const nextColor = hex2hsl(color); | ||
return hueDelta >= 0 ? hsl2hex({ ...nextColor, h: constrainDegrees(nextColor.h, -hueDelta) }) : hsl2hex({ | ||
...nextColor, | ||
h: constrainDegrees(nextColor.h, -(hueDelta * -1 / 2)) | ||
}); | ||
}); | ||
} | ||
return colors; | ||
return shadeColor(input, input.l + lightnessTuningFactor, chromaTuningFactor); | ||
} | ||
function shadeColor(input, lightness, chromaTuningFactor = 0) { | ||
const { c, h } = input; | ||
return oklch2hex({ l: lightness / 100, c: c + chromaTuningFactor, h }); | ||
} | ||
function swatch(input, options = {}) { | ||
invariant(isString(input), MESSAGES.inputString); | ||
const { format, scale = "dynamic" } = options; | ||
const lch = parseCSS(input, "oklch"); | ||
lch.l = 50; | ||
const colorFormat = isHex(input) || isNamedColor(input) ? "hex" : extractColorParts(input).model; | ||
const currentLightness = lch.l; | ||
const safeMaxLightness = currentLightness >= 88.5 ? 99.5 : MAX_LIGHTNESS; | ||
const safeMinLightness = currentLightness <= 33 ? 0 : MIN_LIGHTNESS; | ||
const lightBase = (safeMaxLightness - currentLightness) / 5; | ||
const darkBase = -1 * (currentLightness - safeMinLightness) / 8; | ||
const output = scale === "linear" ? { | ||
50: shadeColor(lch, 95, -375e-5), | ||
100: shadeColor(lch, 90, -375e-5), | ||
200: shadeColor(lch, 80, -375e-5), | ||
300: shadeColor(lch, 70, -375e-5), | ||
400: shadeColor(lch, 60, -375e-5), | ||
500: shadeColor(lch, 50), | ||
600: shadeColor(lch, 40, 0.025), | ||
700: shadeColor(lch, 30, 0.05), | ||
800: shadeColor(lch, 20, 0.075), | ||
900: shadeColor(lch, 10, 0.1) | ||
} : { | ||
50: shadeColorDynamic(lch, 5 * lightBase, -375e-5), | ||
100: shadeColorDynamic(lch, 4 * lightBase, -375e-5), | ||
200: shadeColorDynamic(lch, 3 * lightBase, -375e-5), | ||
300: shadeColorDynamic(lch, 2 * lightBase, -375e-5), | ||
400: shadeColorDynamic(lch, lightBase, -375e-5), | ||
500: shadeColorDynamic(lch, 0), | ||
600: shadeColorDynamic(lch, 1.6 * darkBase, 0.025), | ||
700: shadeColorDynamic(lch, 1.875 * 2 * darkBase, 0.05), | ||
800: shadeColorDynamic(lch, 3 * 2 * darkBase, 0.075), | ||
900: shadeColorDynamic(lch, 4 * 2 * darkBase, 0.1) | ||
}; | ||
return Object.entries(output).reduce((acc, [key, value]) => { | ||
return { | ||
...acc, | ||
[key]: convert(value, format ?? colorFormat) | ||
}; | ||
}, {}); | ||
} | ||
@@ -1540,0 +1586,0 @@ // src/index.ts |
{ | ||
"name": "colorizr", | ||
"version": "3.0.0-0", | ||
"version": "3.0.0-1", | ||
"description": "Manipulate colors like a boss", | ||
@@ -40,3 +40,3 @@ "author": "Gil Barbara <gilbarbara@gmail.com>", | ||
"@size-limit/preset-small-lib": "^11.1.6", | ||
"@types/node": "^22.8.6", | ||
"@types/node": "^22.9.0", | ||
"@vitest/coverage-v8": "^2.1.4", | ||
@@ -51,3 +51,3 @@ "del-cli": "^6.0.0", | ||
"typescript": "^5.6.3", | ||
"vite-tsconfig-paths": "^5.0.1", | ||
"vite-tsconfig-paths": "^5.1.0", | ||
"vitest": "^2.1.4", | ||
@@ -98,3 +98,3 @@ "watch-run": "^1.2.5" | ||
"path": "./dist/index.js", | ||
"limit": "7.1 kB" | ||
"limit": "7.5 kB" | ||
}, | ||
@@ -101,0 +101,0 @@ { |
314
README.md
# Colorizr | ||
[![NPM version](https://badge.fury.io/js/colorizr.svg)](https://www.npmjs.com/package/colorizr) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/colorizr)](https://bundlephobia.com/result?p=colorizr) [![CI](https://github.com/gilbarbara/colorizr/actions/workflows/main.yml/badge.svg)](https://github.com/gilbarbara/colorizr/actions/workflows/main.yml) [![Maintainability](https://api.codeclimate.com/v1/badges/6d686ce2a9f2a1a47d98/maintainability)](https://codeclimate.com/github/gilbarbara/colorizr/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/6d686ce2a9f2a1a47d98/test_coverage)](https://codeclimate.com/github/gilbarbara/colorizr/test_coverage) | ||
[![NPM version](https://badge.fury.io/js/colorizr.svg)](https://www.npmjs.com/package/colorizr) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/colorizr)](https://bundlephobia.com/result?p=colorizr) [![CI](https://github.com/gilbarbara/colorizr/actions/workflows/main.yml/badge.svg)](https://github.com/gilbarbara/colorizr/actions/workflows/main.yml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=gilbarbara_colorizr&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=gilbarbara_colorizr) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=gilbarbara_colorizr&metric=coverage)](https://sonarcloud.io/summary/new_code?id=gilbarbara_colorizr) | ||
@@ -19,3 +19,3 @@ Color conversion, generation, manipulation, comparison, and analysis. | ||
```bash | ||
npm install --save colorizr | ||
npm i colorizr | ||
``` | ||
@@ -25,2 +25,4 @@ | ||
You can use all the functions standalone: | ||
```typescript | ||
@@ -32,3 +34,3 @@ import { luminance } from 'colorizr'; | ||
Or you can create an instance to use all the methods for the selected color: | ||
Or you can create an instance to use all the methods for the same color: | ||
@@ -50,6 +52,6 @@ ```typescript | ||
- [Manipulators](#Manipulators) | ||
- [Utilities](#Utilities) | ||
- [Converters](#Converters) | ||
- [Generators](#Generators) | ||
- [Comparison](#Comparison) | ||
- [Utilities](#Utilities) | ||
- [Validators](#Validators) | ||
@@ -86,3 +88,3 @@ - [Class](#Class) | ||
name('#ffc0cb', 10); // pink | ||
name('#ffc0cb'); // pink | ||
name('rgb(176 224 230)'); // 'powderblue' | ||
@@ -98,5 +100,5 @@ name('hsl(344 100 50)'); // #ff0044 | ||
opacity('#ff0044'); 1 | ||
opacity('rgb(255 0 68 / 90%)'); 0.9 | ||
opacity('hsl(344 100 50 / 60%)'); 0.6 | ||
opacity('#ff0044'); // 1 | ||
opacity('rgb(255 0 68 / 90%)'); // 0.9 | ||
opacity('hsl(344 100 50 / 60%)'); // 0.6 | ||
``` | ||
@@ -106,3 +108,3 @@ | ||
**lighten(input: string, alpha: number): string** | ||
**lighten(input: string, amount: number): string** | ||
Get a color with increased lightness. | ||
@@ -114,5 +116,6 @@ | ||
lighten('#ff0044', 10); // #ff3369 | ||
lighten('hsl(136 100% 50%)', 10); // hsl(136 100% 60%) | ||
``` | ||
**darken(input: string, alpha = 10): string** | ||
**darken(input: string, amount: number): string** | ||
Get a color with decreased lightness. | ||
@@ -124,2 +127,3 @@ | ||
darken('#ff0044', 10); // #cc0036 | ||
darken('oklch(47.642% 0.29956 274.93693)', 10); // oklch(40.377% 0.25281 275.46794) | ||
``` | ||
@@ -144,2 +148,3 @@ | ||
desaturate('#ff0044', 10); // #f20d4a | ||
desaturate('oklab(70.622% 0.1374 0.14283)', 10); // oklab(69.021% 0.12176 0.13721) | ||
``` | ||
@@ -172,4 +177,4 @@ | ||
opacify('hsl(344, 100, 50)', 10); // rgba(255, 0, 68, 0.9) | ||
opacify('#ff0044', 50); // hsla(344, 100%, 50%, 0.5) | ||
opacify('hsl(344, 100, 50)', 0.9); // hsl(344 100 50 / 90%) | ||
opacify('#ff0044', 0.8); // #ff0044cc | ||
``` | ||
@@ -183,108 +188,6 @@ | ||
transparentize('hsl(344, 100, 50)', 10); // rgba(255, 0, 68, 0.9) | ||
transparentize('#ff0044', 50, 'hsl'); // hsla(344, 100%, 50%, 0.5) | ||
transparentize('hsl(344, 100, 50)', 10); // hsl(344 100 50 / 90%) | ||
transparentize('#ff0044', 50, 'hsl'); // #ff004480 | ||
``` | ||
### Utilities | ||
**addAlphaToHex(input: string, alpha: number): string** | ||
Add an alpha value to a hex string | ||
```typescript | ||
import { addAlphaToHex } from 'colorizr'; | ||
addAlphaToHex('#ff0044', 0.9); // '#ff0044e6' | ||
addAlphaToHex('#ff0044cc', 0.9); // '#ff0044e6' | ||
``` | ||
**convertAlphaToHex(input: number): string** | ||
Convert an alpha value to a hex value. | ||
```typescript | ||
import { convertAlphaToHex } from 'colorizr'; | ||
convertAlphaToHex(0.5); // '80' | ||
``` | ||
**extractAlphaFromHex(input: string): number** | ||
Extract the alpha value from a hex string | ||
```typescript | ||
import { extractAlphaFromHex } from 'colorizr'; | ||
convertAlphaToHex('#ff004480'); // 0.5 | ||
``` | ||
**extractColorParts(input: string): ExtractColorPartsReturn** | ||
Extract the color parts from a CSS color string. | ||
Hex colors are not supported. | ||
```typescript | ||
type ExtractColorPartsReturn = { | ||
alpha?: number; | ||
model: 'hsl' | 'oklab' | 'oklch' | 'rgb'; | ||
} & Record<string, number>; | ||
extractColorParts('rgb(255 0 68)') // { model: 'rgb', r: 255, g: 0, b: 68 } | ||
extractColorParts('hsl(344 100% 50% / 90%)') // { alpha: 0.9, model: 'hsl', h: 344, g: 100, l: 50 } | ||
``` | ||
**formatCSS(input: HSL | RGB, options?: FormatOptions): string** | ||
Get a css string from a color object. | ||
```typescript | ||
import { formatCSS } from 'colorizr'; | ||
formatCSS({ h: 344, s: 100, l: 50 }, { format: 'rgb' }); // 'rgb(255, 0, 68)' | ||
formatCSS({ r: 255, g: 0, b: 68 }, { alpha: 0.5, format: 'hsl' }); // 'hsla(344, 100%, 50%, 0.5)' | ||
``` | ||
**formatHex(input: string): string** | ||
Format a short hex string of 3 (or 4) digits into 6 (or 8) digits. | ||
```typescript | ||
import { formatHex } from 'colorizr'; | ||
formatHex('#07e'); // '#0077ee' | ||
formatHex('#f058'); // '#ff005588' | ||
``` | ||
**parseCSS(input: string, format?: ColorType): string | HSL | RGB** | ||
Parse a css string to hex, HSL, OKLAB, OKLCH, or RGB. | ||
If the format isn't set, it will return the same format as the input. | ||
```typescript | ||
import { parseCSS } from 'colorizr'; | ||
parseCSS('hsl(344 100% 50%)'); // { h: 344, l: 50, s: 100 } | ||
parseCSS('#ff0044', 'hsl'); // { h: 344, l: 50, s: 100 } | ||
``` | ||
**random(): string** | ||
Get a random color. | ||
```typescript | ||
import { random } from 'colorizr'; | ||
random(); // '#b385e0' | ||
``` | ||
**removeAlphaFromHex(input: string): string** | ||
Remove the alpha value from a hex string | ||
```typescript | ||
import { removeAlphaFromHex } from 'colorizr'; | ||
removeAlphaFromHex('#ff0044cc'); // '#ff0044' | ||
``` | ||
**textColor(input: string): string** | ||
Get a contrasting color (black or white) for the input color. | ||
```typescript | ||
import { textColor } from 'colorizr'; | ||
textColor('#ff0044'); // #ffffff | ||
textColor('#fff800'); // #000000 | ||
``` | ||
### Comparison | ||
@@ -307,3 +210,3 @@ | ||
colorDifference('hsl(0, 0%, 100%)', '#f04'); // 442 | ||
colorDifference('hsl(0 0% 100%)', '#f04'); // 442 | ||
``` | ||
@@ -337,3 +240,3 @@ | ||
contrast('hsl(0, 0%, 100%)', 'rgb(255, 0, 68)'); // 3.94 | ||
contrast('hsl(0 0% 100%)', 'rgb(255 0 68)'); // 3.94 | ||
``` | ||
@@ -361,3 +264,3 @@ ### Generators | ||
const complementary = scheme('rgb(255, 0, 68)'); // ['#ff0044', '#00ffbb'] | ||
const complementary = scheme('rgb(255 0 68)'); // ['#ff0044', '#00ffbb'] | ||
const triadic = scheme('#ff0044', 'triadic'); // ['#ff0044', '#44ff00', '#0044ff'] | ||
@@ -391,3 +294,3 @@ ``` | ||
**convert(input: string, format: ColorType): string** | ||
Convert a color string from one format to another. | ||
Convert a color string to another format. | ||
@@ -437,3 +340,3 @@ ```typescript | ||
**hsl2hex(input: HSL | ColorTupple): string** | ||
**hsl2hex(input: HSL | ColorTuple): string** | ||
Convert HSL to HEX. | ||
@@ -448,3 +351,3 @@ | ||
**hsl2oklab(input: HSL | ColorTupple, precision?: number): LAB** | ||
**hsl2oklab(input: HSL | ColorTuple, precision?: number): LAB** | ||
Convert HSL to OKLAB. | ||
@@ -459,3 +362,3 @@ | ||
**hsl2oklch(input: HSL | ColorTupple, precision?: number): string** | ||
**hsl2oklch(input: HSL | ColorTuple, precision?: number): LCH** | ||
Convert HSL to OKLCH. | ||
@@ -470,3 +373,3 @@ | ||
**hsl2rgb(input: HSL | ColorTupple): RGB** | ||
**hsl2rgb(input: HSL | ColorTuple): RGB** | ||
Convert HSL to RGB. | ||
@@ -481,3 +384,3 @@ | ||
**oklab2hex(input: LAB | ColorTupple): string** | ||
**oklab2hex(input: LAB | ColorTuple): string** | ||
Convert OKLAB to HEX. | ||
@@ -492,3 +395,3 @@ | ||
**oklab2hsl(input: LAB | ColorTupple): HSL** | ||
**oklab2hsl(input: LAB | ColorTuple): HSL** | ||
Convert OKLAB to HSL. | ||
@@ -503,3 +406,3 @@ | ||
**oklab2oklch(input: LAB | ColorTupple, precision?: number): LCH** | ||
**oklab2oklch(input: LAB | ColorTuple, precision?: number): LCH** | ||
Convert OKLAB to OKLCH. | ||
@@ -514,3 +417,3 @@ | ||
**oklab2rgb(input: LAB | ColorTupple, precision: number = 0): RGB** | ||
**oklab2rgb(input: LAB | ColorTuple, precision: number = 0): RGB** | ||
Convert OKLAB to RGB. | ||
@@ -525,3 +428,3 @@ | ||
**oklch2hex(input: LCH | ColorTupple): string** | ||
**oklch2hex(input: LCH | ColorTuple): string** | ||
Convert OKLCH to HEX. | ||
@@ -536,3 +439,3 @@ | ||
**oklch2hsl(input: LCH | ColorTupple): HSL** | ||
**oklch2hsl(input: LCH | ColorTuple): HSL** | ||
Convert OKLCH to HSL. | ||
@@ -547,3 +450,3 @@ | ||
**oklch2oklab(input: LCH | ColorTupple, precision?: number): LAB** | ||
**oklch2oklab(input: LCH | ColorTuple, precision?: number): LAB** | ||
Convert OKLCH to OKLAB. | ||
@@ -558,3 +461,3 @@ | ||
**oklch2rgb(input: LCH | ColorTupple, precision: number = 0): RGB** | ||
**oklch2rgb(input: LCH | ColorTuple, precision: number = 0): RGB** | ||
Convert OKLCH to RGB. | ||
@@ -569,3 +472,3 @@ | ||
**rgb2hex(input: RGB | ColorTupple): string** | ||
**rgb2hex(input: RGB | ColorTuple): string** | ||
Convert RGB to HEX. | ||
@@ -580,3 +483,3 @@ | ||
**rgb2hsl(input: RGB | ColorTupple): HSL** | ||
**rgb2hsl(input: RGB | ColorTuple): HSL** | ||
Convert RGB to HSL. | ||
@@ -591,3 +494,3 @@ | ||
**rgb2oklab(input: RGB | ColorTupple, precision: number): LAB** | ||
**rgb2oklab(input: RGB | ColorTuple, precision: number): LAB** | ||
Convert RGB to OKLAB. | ||
@@ -602,3 +505,3 @@ | ||
**rgb2oklch(input: RGB | ColorTupple, precision: number): LCH** | ||
**rgb2oklch(input: RGB | ColorTuple, precision: number): LCH** | ||
Convert RGB to OKLCH. | ||
@@ -613,2 +516,105 @@ | ||
### Utilities | ||
**addAlphaToHex(input: string, alpha: number): string** | ||
Add an alpha value to a hex string | ||
```typescript | ||
import { addAlphaToHex } from 'colorizr'; | ||
addAlphaToHex('#ff0044', 0.9); // '#ff0044e6' | ||
addAlphaToHex('#ff0044cc', 0.9); // '#ff0044e6' | ||
``` | ||
**convertAlphaToHex(input: number): string** | ||
Convert an alpha value to a hex value. | ||
```typescript | ||
import { convertAlphaToHex } from 'colorizr'; | ||
convertAlphaToHex(0.5); // '80' | ||
``` | ||
**extractAlphaFromHex(input: string): number** | ||
Extract the alpha value from a hex string | ||
```typescript | ||
import { extractAlphaFromHex } from 'colorizr'; | ||
convertAlphaToHex('#ff004480'); // 0.5 | ||
``` | ||
**extractColorParts(input: string): ExtractColorPartsReturn** | ||
Extract the color parts from a CSS color string. | ||
Hex colors are not supported. | ||
```typescript | ||
type ExtractColorPartsReturn = { | ||
alpha?: number; | ||
model: 'hsl' | 'oklab' | 'oklch' | 'rgb'; | ||
} & Record<string, number>; | ||
extractColorParts('rgb(255 0 68)') // { model: 'rgb', r: 255, g: 0, b: 68 } | ||
extractColorParts('hsl(344 100% 50% / 90%)') // { alpha: 0.9, model: 'hsl', h: 344, g: 100, l: 50 } | ||
``` | ||
**formatCSS(input: HSL | RGB, options?: FormatOptions): string** | ||
Get a css string from a color object. | ||
```typescript | ||
import { formatCSS } from 'colorizr'; | ||
formatCSS({ h: 344, s: 100, l: 50 }, { format: 'rgb' }); // 'rgb(255, 0, 68)' | ||
formatCSS({ r: 255, g: 0, b: 68 }, { alpha: 0.5, format: 'hsl' }); // 'hsla(344, 100%, 50%, 0.5)' | ||
``` | ||
**formatHex(input: string): string** | ||
Format a short hex string of 3 (or 4) digits into 6 (or 8) digits. | ||
```typescript | ||
import { formatHex } from 'colorizr'; | ||
formatHex('#07e'); // '#0077ee' | ||
formatHex('#f058'); // '#ff005588' | ||
``` | ||
**parseCSS(input: string, format?: ColorType): string | HSL | RGB** | ||
Parse a css string to hex, HSL, OKLAB, OKLCH, or RGB. | ||
If the format isn't set, it will return the same format as the input. | ||
```typescript | ||
import { parseCSS } from 'colorizr'; | ||
parseCSS('hsl(344 100% 50%)'); // { h: 344, l: 50, s: 100 } | ||
parseCSS('#ff0044', 'hsl'); // { h: 344, l: 50, s: 100 } | ||
``` | ||
**random(type: ColorType = 'hex'): string** | ||
Get a random color. | ||
```typescript | ||
import { random } from 'colorizr'; | ||
random(); // '#b385e0' | ||
random('oklch') // oklab(86.876% -0.22518 0.1597) | ||
``` | ||
**removeAlphaFromHex(input: string): string** | ||
Remove the alpha value from a hex string | ||
```typescript | ||
import { removeAlphaFromHex } from 'colorizr'; | ||
removeAlphaFromHex('#ff0044cc'); // '#ff0044' | ||
``` | ||
**textColor(input: string): string** | ||
Get a contrasting color (black or white) for the input color. | ||
```typescript | ||
import { textColor } from 'colorizr'; | ||
textColor('#ff0044'); // #ffffff | ||
textColor('#fff800'); // #000000 | ||
``` | ||
### Validators | ||
@@ -739,3 +745,3 @@ | ||
**colorizr.css** | ||
Get the css string of the same time as the input. | ||
Get the css string of the same type as the input. | ||
@@ -745,35 +751,11 @@ **colorizr.textColor** | ||
#### Manipulation | ||
#### Methods | ||
**colorizr.lighten(percentage: number)** | ||
Get a lighter color. | ||
**colorizr.format(type: ColorType, precision?: number)** | ||
Returns the formatted color with the type | ||
**colorizr.darken(percentage: number)** | ||
Get a darker color. | ||
... | ||
**colorizr.desaturate(percentage: number)** | ||
Get a desaturated color. | ||
Also, all the [manipulators](#Manipulators) and [comparison](#Comparison) utilities. | ||
**colorizr.saturate(percentage: number)** | ||
Get a saturated color. | ||
**colorizr.rotate(degrees: number)** | ||
Get a color with a hue rotated. | ||
**colorizr.invert()** | ||
Get the inverted color. | ||
**colorizr.transparentize(percentage: number)** | ||
Get a faded color. | ||
#### Comparison | ||
**colorizr.compare(color: string)** | ||
Returns an object with the analysis (check the compare format above) | ||
#### Fortmatting | ||
**colorizr.format(type: ColorType, precision?: number)** | ||
Returns the formatted color with the type | ||
## Credits / References | ||
@@ -780,0 +762,0 @@ |
import { invariant } from '~/modules/invariant'; | ||
import parseColor from '~/modules/parse-color'; | ||
import brightnessDifference from '~/brightness-difference'; | ||
import chroma from '~/chroma'; | ||
import colorDifference from '~/color-difference'; | ||
import compare from '~/compare'; | ||
import contrast from '~/contrast'; | ||
import darken from '~/darken'; | ||
import desaturate from '~/desaturate'; | ||
import formatCSS from '~/format-css'; | ||
import invert from '~/invert'; | ||
import lighten from '~/lighten'; | ||
@@ -23,3 +27,3 @@ import luminance from '~/luminance'; | ||
*/ | ||
type?: ColorType; | ||
format?: ColorType; | ||
} | ||
@@ -48,3 +52,3 @@ | ||
this.alpha = alpha; | ||
this.type = options.type ?? type; | ||
this.type = options.format ?? type; | ||
} | ||
@@ -130,2 +134,10 @@ | ||
public brightnessDifference(input: string): number { | ||
return brightnessDifference(this.selectedColor, input); | ||
} | ||
public colorDifference(input: string): number { | ||
return colorDifference(this.selectedColor, input); | ||
} | ||
/** | ||
@@ -138,2 +150,6 @@ * Test 2 colors for compliance | ||
public contrast(input: string): number { | ||
return contrast(this.selectedColor, input); | ||
} | ||
public format(type: ColorType, precision?: number): string { | ||
@@ -179,3 +195,3 @@ return formatCSS(this.rgb, { | ||
public invert(): string { | ||
return rotate(this.selectedColor, 180); | ||
return invert(this.selectedColor); | ||
} | ||
@@ -182,0 +198,0 @@ |
@@ -6,3 +6,3 @@ import formatCSS from '~/format-css'; | ||
/** | ||
* Convert a color string from one format to another | ||
* Convert a color string to another format. | ||
*/ | ||
@@ -9,0 +9,0 @@ export default function convert(input: string, format: ColorType) { |
@@ -7,6 +7,6 @@ import { MESSAGES } from '~/modules/constants'; | ||
import rgb2hsl from '~/converters/rgb2hsl'; | ||
import { HEX, HSL } from '~/types'; | ||
import { HSL } from '~/types'; | ||
/** Convert HEX to HSL */ | ||
export default function hex2hsl(input: HEX): HSL { | ||
export default function hex2hsl(input: string): HSL { | ||
invariant(isHex(input), MESSAGES.inputHex); | ||
@@ -13,0 +13,0 @@ |
@@ -7,6 +7,6 @@ import { MESSAGES } from '~/modules/constants'; | ||
import rgb2oklab from '~/converters/rgb2oklab'; | ||
import { HEX, LAB } from '~/types'; | ||
import { LAB } from '~/types'; | ||
/** Convert HEX to oklab */ | ||
export default function hex2oklab(input: HEX, precision?: number): LAB { | ||
export default function hex2oklab(input: string, precision?: number): LAB { | ||
invariant(isHex(input), MESSAGES.inputHex); | ||
@@ -13,0 +13,0 @@ |
@@ -7,6 +7,6 @@ import { MESSAGES } from '~/modules/constants'; | ||
import rgb2oklch from '~/converters/rgb2oklch'; | ||
import { HEX, LCH } from '~/types'; | ||
import { LCH } from '~/types'; | ||
/** Convert HEX to oklch */ | ||
export default function hex2oklch(input: HEX, precision?: number): LCH { | ||
export default function hex2oklch(input: string, precision?: number): LCH { | ||
invariant(isHex(input), MESSAGES.inputHex); | ||
@@ -13,0 +13,0 @@ |
@@ -6,6 +6,6 @@ import { MESSAGES } from '~/modules/constants'; | ||
import formatHex from '~/format-hex'; | ||
import { HEX, RGB } from '~/types'; | ||
import { RGB } from '~/types'; | ||
/** Convert HEX to RGB */ | ||
export default function hex2rgb(input: HEX): RGB { | ||
export default function hex2rgb(input: string): RGB { | ||
invariant(isHex(input), MESSAGES.inputHex); | ||
@@ -12,0 +12,0 @@ |
@@ -6,9 +6,10 @@ import { LAB_TO_LMS, LSM_TO_RGB } from '~/modules/constants'; | ||
const { abs, sign } = Math; | ||
const { abs } = Math; | ||
function lrgb2rgb(input: number) { | ||
const absoluteNumber = abs(input); | ||
const sign = input < 0 ? -1 : 1; | ||
if (absoluteNumber > 0.0031308) { | ||
return (sign(input) || 1) * (1.055 * absoluteNumber ** (1 / 2.4) - 0.055); | ||
return sign * (absoluteNumber ** (1 / 2.4) * 1.055 - 0.055); | ||
} | ||
@@ -15,0 +16,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { PRECISION } from '~/modules/constants'; | ||
import { LRGB_TO_LMS, LSM_TO_LAB, PRECISION } from '~/modules/constants'; | ||
import { parseInput, restrictValues } from '~/modules/utils'; | ||
@@ -23,10 +23,10 @@ | ||
const [lr, lg, lb] = [rgb2lrgb(value.r / 255), rgb2lrgb(value.g / 255), rgb2lrgb(value.b / 255)]; | ||
const l = cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb); | ||
const m = cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb); | ||
const s = cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb); | ||
const l = cbrt(LRGB_TO_LMS.l[0] * lr + LRGB_TO_LMS.l[1] * lg + LRGB_TO_LMS.l[2] * lb); | ||
const m = cbrt(LRGB_TO_LMS.m[0] * lr + LRGB_TO_LMS.m[1] * lg + LRGB_TO_LMS.m[2] * lb); | ||
const s = cbrt(LRGB_TO_LMS.s[0] * lr + LRGB_TO_LMS.s[1] * lg + LRGB_TO_LMS.s[2] * lb); | ||
const lab = { | ||
l: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s, | ||
a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s, | ||
b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s, | ||
l: LSM_TO_LAB.l[0] * l + LSM_TO_LAB.l[1] * m - LSM_TO_LAB.l[2] * s, | ||
a: LSM_TO_LAB.a[0] * l - LSM_TO_LAB.a[1] * m + LSM_TO_LAB.a[2] * s, | ||
b: LSM_TO_LAB.b[0] * l + LSM_TO_LAB.b[1] * m - LSM_TO_LAB.b[2] * s, | ||
}; | ||
@@ -33,0 +33,0 @@ |
@@ -14,3 +14,3 @@ import { MESSAGES, PRECISION } from '~/modules/constants'; | ||
* The output color type. | ||
* @default 'rgb' | ||
* @default 'hex' | ||
*/ | ||
@@ -37,3 +37,3 @@ format?: ColorType; | ||
const { alpha, format = 'rgb', precision = PRECISION, separator: baseSeparator = ' ' } = options; | ||
const { alpha, format = 'hex', precision = PRECISION, separator: baseSeparator = ' ' } = options; | ||
@@ -40,0 +40,0 @@ let value: HSL; |
import { MESSAGES } from '~/modules/constants'; | ||
import { invariant } from '~/modules/invariant'; | ||
import { isString } from '~/modules/validators'; | ||
import { isHex, isNamedColor, isString } from '~/modules/validators'; | ||
import extractColorParts from '~/extract-color-parts'; | ||
import formatCSS from '~/format-css'; | ||
import parseCSS from '~/parse-css'; | ||
import rotate from '~/rotate'; | ||
import { HEX } from '~/types'; | ||
@@ -13,5 +16,6 @@ /** | ||
invariant(isString(input), MESSAGES.inputString); | ||
const format = isHex(input) || isNamedColor(input) ? 'hex' : extractColorParts(input).model; | ||
const hex = parseCSS(input, 'hex'); | ||
return rotate(hex, 180); | ||
return formatCSS(rotate(hex, 180) as HEX, { format }); | ||
} |
@@ -16,2 +16,12 @@ import { ColorKeysTuple, ColorModelKey } from '~/types'; | ||
}; | ||
export const LRGB_TO_LMS = { | ||
l: [0.4122214708, 0.5363325363, 0.0514459929], | ||
m: [0.2119034982, 0.6806995451, 0.1073969566], | ||
s: [0.0883024619, 0.2817188376, 0.6299787005], | ||
}; | ||
export const LSM_TO_LAB = { | ||
l: [0.2104542553, 0.793617785, 0.0040720468], | ||
a: [1.9779984951, 2.428592205, 0.4505937099], | ||
b: [0.0259040371, 0.7827717662, 0.808675766], | ||
}; | ||
export const LSM_TO_RGB = { | ||
@@ -18,0 +28,0 @@ r: [4.0767416360759583, -3.3077115392580629, 0.2309699031821043], |
@@ -8,2 +8,6 @@ import { MESSAGES } from '~/modules/constants'; | ||
/** | ||
* Get the name of a color. | ||
* Returns the hex value if the color is not found. | ||
*/ | ||
export default function name(input: string): string { | ||
@@ -10,0 +14,0 @@ invariant(isString(input), MESSAGES.inputString); |
import { MESSAGES } from '~/modules/constants'; | ||
import { invariant } from '~/modules/invariant'; | ||
import { isPlainObject, isString } from '~/modules/validators'; | ||
import { isHex, isNamedColor, isPlainObject, isString } from '~/modules/validators'; | ||
import convert from '~/convert'; | ||
import hex2hsl from '~/converters/hex2hsl'; | ||
import hsl2hex from '~/converters/hsl2hex'; | ||
import extractColorParts from '~/extract-color-parts'; | ||
import parseCSS from '~/parse-css'; | ||
import rotate from '~/rotate'; | ||
import { HEX } from '~/types'; | ||
import { ColorType, HEX } from '~/types'; | ||
export interface PaletteOptions { | ||
format?: ColorType; | ||
lightness?: number; | ||
saturation?: number; | ||
/** | ||
* The number of colors to generate | ||
* @default 6 | ||
*/ | ||
size?: number; | ||
type?: string; | ||
type?: 'monochromatic'; | ||
} | ||
@@ -22,5 +29,6 @@ | ||
const { lightness, saturation, size = 6, type } = options; | ||
const hsl = hex2hsl(parseCSS(input, 'hex')); | ||
const { format, lightness, saturation, size = 6, type } = options; | ||
const hsl = parseCSS(input, 'hsl'); | ||
const output: string[] = []; | ||
const colorFormat = isHex(input) || isNamedColor(input) ? 'hex' : extractColorParts(input).model; | ||
@@ -52,3 +60,3 @@ switch (type) { | ||
return output; | ||
return output.map(color => convert(color, format ?? colorFormat)); | ||
} |
@@ -12,12 +12,12 @@ import { MESSAGES, PRECISION } from '~/modules/constants'; | ||
export type ParseCSSReturn<T extends ColorType> = T extends 'hsl' | ||
? HSL | ||
: T extends 'lab' | ||
? LAB | ||
: T extends 'lch' | ||
? LCH | ||
: T extends 'rgb' | ||
? RGB | ||
: T extends 'hex' | ||
? HEX | ||
export type ParseCSSReturn<T extends ColorType> = T extends 'hex' | ||
? HEX | ||
: T extends 'hsl' | ||
? HSL | ||
: T extends 'oklab' | ||
? LAB | ||
: T extends 'oklch' | ||
? LCH | ||
: T extends 'rgb' | ||
? RGB | ||
: never; | ||
@@ -24,0 +24,0 @@ |
@@ -1,2 +0,3 @@ | ||
import hsl2hex from '~/converters/hsl2hex'; | ||
import formatCSS from '~/format-css'; | ||
import { ColorType } from '~/types'; | ||
@@ -6,3 +7,3 @@ /** | ||
*/ | ||
export default function random(): string { | ||
export default function random(type: ColorType = 'hex'): string { | ||
const hsl = { | ||
@@ -14,3 +15,3 @@ h: Math.floor(Math.random() * 360) + 1, | ||
return hsl2hex(hsl); | ||
return formatCSS(hsl, { format: type }); | ||
} |
import { MESSAGES } from '~/modules/constants'; | ||
import { invariant } from '~/modules/invariant'; | ||
import { isString } from '~/modules/validators'; | ||
import { isHex, isNamedColor, isString } from '~/modules/validators'; | ||
import parseCSS from '~/parse-css'; | ||
import convert from '~/convert'; | ||
import extractColorParts from '~/extract-color-parts'; | ||
import rotate from '~/rotate'; | ||
import { Scheme } from '~/types'; | ||
import { ColorType } from '~/types'; | ||
export type Scheme = | ||
| 'analogous' | ||
| 'complementary' | ||
| 'rectangle' | ||
| 'split' | ||
| 'split-complementary' | ||
| 'square' | ||
| 'tetradic' | ||
| 'triadic'; | ||
export interface SchemeOptions { | ||
format?: ColorType; | ||
/** | ||
* The type of scheme to generate. | ||
* @default 'complementary' | ||
*/ | ||
type?: Scheme; | ||
} | ||
/** | ||
* Get the scheme for a color. | ||
*/ | ||
export default function scheme(input: string, type: Scheme = 'complementary'): string[] { | ||
export default function scheme(input: string, typeOrOptions?: Scheme | SchemeOptions): string[] { | ||
invariant(isString(input), MESSAGES.inputString); | ||
const { format, type = 'complementary' } = isString(typeOrOptions) | ||
? { type: typeOrOptions } | ||
: (typeOrOptions ?? {}); | ||
const hex = parseCSS(input, 'hex'); | ||
const output: string[] = []; | ||
const output = isHex(input) || isNamedColor(input) ? 'hex' : extractColorParts(input).model; | ||
const colors: string[] = []; | ||
switch (type) { | ||
case 'analogous': { | ||
output.push(rotate(hex, -30), hex, rotate(hex, 30)); | ||
colors.push(rotate(input, -30), input, rotate(input, 30)); | ||
break; | ||
} | ||
case 'complementary': { | ||
output.push(hex, rotate(hex, 180)); | ||
colors.push(input, rotate(input, 180)); | ||
break; | ||
@@ -30,7 +54,7 @@ } | ||
case 'split-complementary': { | ||
output.push(hex, rotate(hex, 150), rotate(hex, 210)); | ||
colors.push(input, rotate(input, 150), rotate(input, 210)); | ||
break; | ||
} | ||
case 'triadic': { | ||
output.push(hex, rotate(hex, 120), rotate(hex, 240)); | ||
colors.push(input, rotate(input, 120), rotate(input, 240)); | ||
break; | ||
@@ -41,7 +65,7 @@ } | ||
case 'rectangle': { | ||
output.push(hex, rotate(hex, 60), rotate(hex, 180), rotate(hex, 240)); | ||
colors.push(input, rotate(input, 60), rotate(input, 180), rotate(input, 240)); | ||
break; | ||
} | ||
case 'square': { | ||
output.push(hex, rotate(hex, 90), rotate(hex, 180), rotate(hex, 270)); | ||
colors.push(input, rotate(input, 90), rotate(input, 180), rotate(input, 270)); | ||
break; | ||
@@ -54,3 +78,3 @@ } | ||
return output; | ||
return colors.map(color => convert(color, format ?? output)); | ||
} |
import { MESSAGES } from '~/modules/constants'; | ||
import { invariant } from '~/modules/invariant'; | ||
import { constrainDegrees } from '~/modules/utils'; | ||
import { isString } from '~/modules/validators'; | ||
import { isHex, isNamedColor, isString } from '~/modules/validators'; | ||
import hex2hsl from '~/converters/hex2hsl'; | ||
import hsl2hex from '~/converters/hsl2hex'; | ||
import desaturate from '~/desaturate'; | ||
import convert from '~/convert'; | ||
import { oklch2hex } from '~/converters'; | ||
import oklch2rgb from '~/converters/oklch2rgb'; | ||
import rgb2hex from '~/converters/rgb2hex'; | ||
import extractColorParts from '~/extract-color-parts'; | ||
import parseCSS from '~/parse-css'; | ||
import saturate from '~/saturate'; | ||
import { HEX } from '~/types'; | ||
import { ColorType, HEX, LCH } from '~/types'; | ||
const HUE_MAP = [0, 4, 8, 12, 16, 20, 24, 28, 32, 36]; | ||
const LIGHTNESS_MAP = [90, 80, 70, 60, 50, 40, 30, 20, 10, 5]; | ||
const SATURATION_MAP = [32, 16, 8, 4, 0, 0, 4, 8, 16, 32]; | ||
type ColorTokens = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900; | ||
export default function swatch(input: string, variant?: 'up' | 'down'): string[] { | ||
invariant(isString(input), MESSAGES.inputString); | ||
const hsl = parseCSS(input, 'hsl'); | ||
type Swatch = { | ||
[key in ColorTokens]: string; | ||
}; | ||
const lightnessGoal = hsl.l; | ||
const closestLightness = LIGHTNESS_MAP.reduce((previous, current) => | ||
Math.abs(current - lightnessGoal) < Math.abs(previous - lightnessGoal) ? current : previous, | ||
); | ||
const baseColorIndex = LIGHTNESS_MAP.findIndex(l => l === closestLightness); | ||
export interface SwatchOptions { | ||
format?: ColorType; | ||
/** | ||
* The scale of the swatch. | ||
* Linear scale will have equal distance between each shade. | ||
* @default 'dynamic' | ||
*/ | ||
scale?: 'dynamic' | 'linear'; | ||
} | ||
const colors: HEX[] = LIGHTNESS_MAP.map(l => hsl2hex({ ...hsl, l })).map((color, index) => { | ||
const saturationDelta = SATURATION_MAP[index] - SATURATION_MAP[baseColorIndex]; | ||
const MIN_LIGHTNESS = 21; | ||
const MAX_LIGHTNESS = 97; | ||
return ( | ||
saturationDelta >= 0 | ||
? saturate(color, saturationDelta) | ||
: desaturate(color, saturationDelta * -1) | ||
) as HEX; | ||
}); | ||
/** | ||
* Generate a shade of a color based its lightness tuning factor | ||
*/ | ||
function shadeColorDynamic(input: LCH, lightnessTuningFactor: number, chromaTuningFactor = 0): HEX { | ||
if (lightnessTuningFactor === 0) { | ||
return rgb2hex(oklch2rgb({ ...input, l: input.l / 100 })); | ||
} | ||
if (variant === 'up') { | ||
return colors.map((color, index) => { | ||
const hueDelta = HUE_MAP[index] - HUE_MAP[baseColorIndex]; | ||
const nextColor = hex2hsl(color); | ||
// Convert back to RGB and make sure it's within the sRGB gamut | ||
return shadeColor(input, input.l + lightnessTuningFactor, chromaTuningFactor); | ||
} | ||
return hueDelta >= 0 | ||
? hsl2hex({ ...nextColor, h: constrainDegrees(nextColor.h, hueDelta) }) | ||
: hsl2hex({ | ||
...nextColor, | ||
h: constrainDegrees(nextColor.h, (hueDelta * -1) / 2), | ||
}); | ||
}); | ||
} | ||
/** | ||
* Generate a shade of a color based its lightness tuning factor | ||
*/ | ||
function shadeColor(input: LCH, lightness: number, chromaTuningFactor = 0): HEX { | ||
const { c, h } = input; | ||
if (variant === 'down') { | ||
return colors.map((color, index) => { | ||
const hueDelta = HUE_MAP[index] - HUE_MAP[baseColorIndex]; | ||
const nextColor = hex2hsl(color); | ||
// Convert back to RGB and make sure it's within the sRGB gamut | ||
return oklch2hex({ l: lightness / 100, c: c + chromaTuningFactor, h }); | ||
} | ||
return hueDelta >= 0 | ||
? hsl2hex({ ...nextColor, h: constrainDegrees(nextColor.h, -hueDelta) }) | ||
: hsl2hex({ | ||
...nextColor, | ||
h: constrainDegrees(nextColor.h, -((hueDelta * -1) / 2)), | ||
}); | ||
}); | ||
} | ||
/** | ||
* Generate a palette of shades of a color | ||
*/ | ||
export default function swatch(input: string, options: SwatchOptions = {}): Swatch { | ||
invariant(isString(input), MESSAGES.inputString); | ||
const { format, scale = 'dynamic' } = options; | ||
return colors; | ||
const lch = parseCSS(input, 'oklch'); | ||
lch.l = 50; | ||
const colorFormat = isHex(input) || isNamedColor(input) ? 'hex' : extractColorParts(input).model; | ||
const currentLightness = lch.l; | ||
const safeMaxLightness = currentLightness >= 88.5 ? 99.5 : MAX_LIGHTNESS; | ||
const safeMinLightness = currentLightness <= 33 ? 0 : MIN_LIGHTNESS; | ||
const lightBase = (safeMaxLightness - currentLightness) / 5; | ||
const darkBase = (-1 * (currentLightness - safeMinLightness)) / 8; | ||
const output: Swatch = | ||
scale === 'linear' | ||
? { | ||
50: shadeColor(lch, 95, -0.00375), | ||
100: shadeColor(lch, 90, -0.00375), | ||
200: shadeColor(lch, 80, -0.00375), | ||
300: shadeColor(lch, 70, -0.00375), | ||
400: shadeColor(lch, 60, -0.00375), | ||
500: shadeColor(lch, 50), | ||
600: shadeColor(lch, 40, 0.025), | ||
700: shadeColor(lch, 30, 0.05), | ||
800: shadeColor(lch, 20, 0.075), | ||
900: shadeColor(lch, 10, 0.1), | ||
} | ||
: { | ||
50: shadeColorDynamic(lch, 5 * lightBase, -0.00375), | ||
100: shadeColorDynamic(lch, 4 * lightBase, -0.00375), | ||
200: shadeColorDynamic(lch, 3 * lightBase, -0.00375), | ||
300: shadeColorDynamic(lch, 2 * lightBase, -0.00375), | ||
400: shadeColorDynamic(lch, lightBase, -0.00375), | ||
500: shadeColorDynamic(lch, 0), | ||
600: shadeColorDynamic(lch, 1.6 * darkBase, 0.025), | ||
700: shadeColorDynamic(lch, 1.875 * 2 * darkBase, 0.05), | ||
800: shadeColorDynamic(lch, 3 * 2 * darkBase, 0.075), | ||
900: shadeColorDynamic(lch, 4 * 2 * darkBase, 0.1), | ||
}; | ||
return Object.entries(output).reduce((acc, [key, value]) => { | ||
return { | ||
...acc, | ||
[key]: convert(value, format ?? colorFormat), | ||
}; | ||
}, {} as Swatch); | ||
} |
@@ -14,3 +14,3 @@ import { MESSAGES } from '~/modules/constants'; | ||
*/ | ||
export default function transparentize(input: string, alpha: Alpha, output?: ColorType): string { | ||
export default function transparentize(input: string, alpha: Alpha, format?: ColorType): string { | ||
invariant(isString(input), MESSAGES.inputString); | ||
@@ -25,3 +25,3 @@ invariant(isNumber(alpha), MESSAGES.alpha); | ||
return formatCSS(oklch, { format: output, alpha: value }); | ||
return formatCSS(oklch, { format, alpha: value }); | ||
} |
@@ -17,3 +17,2 @@ /* eslint-disable @typescript-eslint/member-ordering */ | ||
export type ColorTuple = [number, number, number]; | ||
export type ColorTupleWithRound = [number, number, number, boolean]; | ||
export type ConverterParameters<TModel extends ColorModel> = TModel | ColorTuple; | ||
@@ -87,11 +86,1 @@ export interface Colors { | ||
export type PlainObject<T = any> = Record<string, T>; | ||
export type Scheme = | ||
| 'analogous' | ||
| 'complementary' | ||
| 'rectangle' | ||
| 'split' | ||
| 'split-complementary' | ||
| 'square' | ||
| 'tetradic' | ||
| 'triadic'; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
417525
5571
0
736