better-color-tools
Advanced tools
Comparing version 0.9.1 to 0.10.0
# better-color-tools | ||
## 0.10.0 | ||
### Minor Changes | ||
- c8cb79a: Add adjust() function | ||
### Patch Changes | ||
- 07fff97: Only allow 3, 4, 6, 8 length hex colors | ||
- 07fff97: Fix RGB object parsing | ||
- c8cb79a: Fix critical mix() bug with blue channel being ignored | ||
## 0.9.1 | ||
@@ -4,0 +16,0 @@ |
@@ -1,15 +0,19 @@ | ||
export declare type HSL = [number, number, number, number]; | ||
export declare type HWB = [number, number, number, number]; | ||
export declare type LAB = [number, number, number, number]; | ||
export declare type LCH = [number, number, number, number]; | ||
export declare type LUV = [number, number, number, number]; | ||
export declare type LMS = [number, number, number, number]; | ||
export declare type LinearRGB = [number, number, number, number]; | ||
export declare type Oklab = [number, number, number, number]; | ||
export declare type Oklch = [number, number, number, number]; | ||
export declare type sRGB = [number, number, number, number]; | ||
export declare type XYZ_D65 = [number, number, number, number]; | ||
export declare type Color = string | number | sRGB | LinearRGB | LMS | Oklab | Oklch; | ||
declare type MatrixRow = [number, number, number]; | ||
export declare type ColorMatrix = [MatrixRow, MatrixRow, MatrixRow]; | ||
export type HSL = [number, number, number, number]; | ||
export type HWB = [number, number, number, number]; | ||
export type LAB = [number, number, number, number]; | ||
export type LCH = [number, number, number, number]; | ||
export type LUV = [number, number, number, number]; | ||
export type LMS = [number, number, number, number]; | ||
export type LinearRGBD65 = [number, number, number, number]; | ||
export type Oklab = [number, number, number, number]; | ||
export type Oklch = [number, number, number, number]; | ||
export type sRGB = [number, number, number, number]; | ||
export type XYZ = [number, number, number, number]; | ||
export type Color = string | number | sRGB | LinearRGBD65 | LMS | Oklab | Oklch; | ||
type MatrixRow = [number, number, number]; | ||
export type ColorMatrix = [MatrixRow, MatrixRow, MatrixRow]; | ||
/** sRGB transfer function (D65 illuminant) */ | ||
export declare function sRGBTransferFunction(value: number): number; | ||
/** sRGB inverse transfer function (D65 illuminant) */ | ||
export declare function sRGBInverseTransferFunction(value: number): number; | ||
/** HSL -> sRGB */ | ||
@@ -25,10 +29,10 @@ export declare function hslTosRGB(hsl: HSL): sRGB; | ||
export declare function lmsToOklab(lms: LMS): Oklab; | ||
/** LMS -> Linear sRGB */ | ||
export declare function lmsToLinearRGB(lms: LMS): LinearRGB; | ||
/** Linear sRGB -> sRGB */ | ||
export declare function linearRGBTosRGB(rgb: LinearRGB, γ?: number): sRGB; | ||
/** Linear sRGB -> LMS */ | ||
export declare function linearRGBToLMS(lrgb: LinearRGB): LMS; | ||
/** Linear sRGB -> XYZ (D65) */ | ||
export declare function linearRGBToXYZ(rgb: LinearRGB): XYZ_D65; | ||
/** LMS -> Linear RGB D65 */ | ||
export declare function lmsToLinearRGBD65(lms: LMS): LinearRGBD65; | ||
/** Linear RGB D65 -> sRGB */ | ||
export declare function linearRGBD65TosRGB(rgb: LinearRGBD65): sRGB; | ||
/** Linear RGB D65 -> LMS */ | ||
export declare function linearRGBD65ToLMS(lrgb: LinearRGBD65): LMS; | ||
/** Linear RGB D65 -> XYZ */ | ||
export declare function linearRGBD65ToXYZ(rgb: LinearRGBD65): XYZ; | ||
/** LUV -> sRGB */ | ||
@@ -42,5 +46,5 @@ /** LUV -> XYZ (D65) */ | ||
export declare function oklchTosRGB(oklch: Oklch): sRGB; | ||
/** sRGB -> Linear sRGB */ | ||
export declare function sRGBToLinearRGB(rgb: sRGB, γ?: number): LinearRGB; | ||
/** Linear sRGB -> Luv */ | ||
/** sRGB -> Linear RGB D65 */ | ||
export declare function sRGBToLinearRGBD65(rgb: sRGB): LinearRGBD65; | ||
/** Linear RGB D65 -> Luv */ | ||
/** sRGB -> Oklab */ | ||
@@ -50,5 +54,5 @@ export declare function sRGBToOklab(rgb: sRGB): Oklab; | ||
export declare function sRGBToOklch(rgb: sRGB): Oklch; | ||
/** XYZ (D65) -> Linear sRGB */ | ||
export declare function xyzToLinearRGB(xyz: XYZ_D65): sRGB; | ||
/** XYZ -> Linear RGB D65 */ | ||
export declare function xyzToLinearRGBD65(xyz: XYZ): sRGB; | ||
export {}; | ||
/** XYZ (D65) -> Luv */ |
import { clamp, degToRad, multiplyColorMatrix, radToDeg } from './utils.js'; | ||
import { LMS_TO_OKLAB, LMS_TO_LINEAR_RGB, LINEAR_RGB_TO_LMS, OKLAB_TO_LMS, LINEAR_RGB_TO_XYZ_D65, XYZ_D65_TO_LINEAR_RGB, findGamutIntersection } from './lib.js'; | ||
import { LMS_TO_OKLAB, LMS_TO_LINEAR_RGB, LINEAR_RGB_TO_LMS, OKLAB_TO_LMS, LINEAR_RGB_D65_TO_XYZ, XYZ_TO_LINEAR_RGB_D65, findGamutIntersection } from './lib.js'; | ||
/** sRGB transfer function (D65 illuminant) */ | ||
export function sRGBTransferFunction(value) { | ||
const abs = Math.abs(value); | ||
return abs <= 0.0031308 ? value * 12.92 : 1.055 * Math.pow(abs, 1 / 2.4) - 0.055; | ||
} | ||
/** sRGB inverse transfer function (D65 illuminant) */ | ||
export function sRGBInverseTransferFunction(value) { | ||
return Math.abs(value) <= 0.04045 ? value / 12.92 : ((Math.abs(value) + 0.055) / 1.055) ** 2.4; | ||
} | ||
/** HSL -> sRGB */ | ||
@@ -73,3 +82,3 @@ export function hslTosRGB(hsl) { | ||
if (L === 0) { | ||
return [0, 0, 0, lch[3]]; | ||
return [0, 0, 0, alpha]; | ||
} | ||
@@ -92,5 +101,6 @@ while (h < 0) | ||
} | ||
/** LMS -> Linear sRGB */ | ||
export function lmsToLinearRGB(lms) { | ||
const [r, g, b, a] = multiplyColorMatrix([lms[0] ** 3, lms[1] ** 3, lms[2] ** 3, lms[3]], LMS_TO_LINEAR_RGB); | ||
/** LMS -> Linear RGB D65 */ | ||
export function lmsToLinearRGBD65(lms) { | ||
const [l, m, s, a] = lms; | ||
const [r, g, b] = multiplyColorMatrix([l ** 3, m ** 3, s ** 3, a], LMS_TO_LINEAR_RGB); | ||
return [ | ||
@@ -100,34 +110,32 @@ r, | ||
b, | ||
a, // a | ||
a, // alpha | ||
]; | ||
} | ||
/** Linear sRGB -> sRGB */ | ||
export function linearRGBTosRGB(rgb, γ = 2.4) { | ||
const r = Math.abs(rgb[0]); | ||
const g = Math.abs(rgb[1]); | ||
const b = Math.abs(rgb[2]); | ||
/** Linear RGB D65 -> sRGB */ | ||
export function linearRGBD65TosRGB(rgb) { | ||
const [r, g, b, a] = rgb; | ||
return [ | ||
r < 0.0031308 ? rgb[0] * 12.92 : 1.055 * Math.pow(r, 1 / γ) - 0.055, | ||
g < 0.0031308 ? rgb[1] * 12.92 : 1.055 * Math.pow(g, 1 / γ) - 0.055, | ||
b < 0.0031308 ? rgb[2] * 12.92 : 1.055 * Math.pow(b, 1 / γ) - 0.055, | ||
rgb[3], // alpha | ||
sRGBTransferFunction(r), | ||
sRGBTransferFunction(g), | ||
sRGBTransferFunction(b), | ||
a, // alpha | ||
]; | ||
} | ||
/** Linear sRGB -> LMS */ | ||
export function linearRGBToLMS(lrgb) { | ||
const lms = multiplyColorMatrix(lrgb, LINEAR_RGB_TO_LMS); | ||
/** Linear RGB D65 -> LMS */ | ||
export function linearRGBD65ToLMS(lrgb) { | ||
const [l, m, s, a] = multiplyColorMatrix(lrgb, LINEAR_RGB_TO_LMS); | ||
return [ | ||
Math.cbrt(lms[0]), | ||
Math.cbrt(lms[1]), | ||
Math.cbrt(lms[2]), | ||
lms[3], | ||
Math.cbrt(l), | ||
Math.cbrt(m), | ||
Math.cbrt(s), | ||
a, // alpha | ||
]; | ||
} | ||
/** Linear sRGB -> XYZ (D65) */ | ||
export function linearRGBToXYZ(rgb) { | ||
return multiplyColorMatrix(rgb, LINEAR_RGB_TO_XYZ_D65); | ||
/** Linear RGB D65 -> XYZ */ | ||
export function linearRGBD65ToXYZ(rgb) { | ||
return multiplyColorMatrix(rgb, LINEAR_RGB_D65_TO_XYZ); | ||
} | ||
/** LUV -> sRGB */ | ||
// export function luvTosRGB(luv: LUV): sRGB { | ||
// return linearRGBTosRGB(xyzToLinearRGB(luvToXYZ(luv))); | ||
// return linearRGBD65TosRGB(xyzToLinearRGB(luvToXYZ(luv))); | ||
// } | ||
@@ -155,7 +163,7 @@ /** LUV -> XYZ (D65) */ | ||
export function oklabTosRGB(oklab) { | ||
const rgb = linearRGBTosRGB(lmsToLinearRGB(oklabToLMS(oklab))); | ||
if (rgb[0] > 1.001 || rgb[0] < -0.001 || rgb[1] > 1.001 || rgb[1] < -0.001 || rgb[2] > 1.001 || rgb[2] < -0.001) { | ||
const [R, G, B, alpha] = lmsToLinearRGBD65(oklabToLMS(oklab)); | ||
if (R > 1.001 || R < -0.001 || G > 1.001 || G < -0.001 || B > 1.001 || B < -0.001) { | ||
// “Preserve light, clamp Chroma” method from https://bottosson.github.io/posts/gamutclipping/ | ||
const ε = 0.00001; | ||
const [L, a, b, alpha] = oklab; | ||
const [L, a, b] = oklab; | ||
const C = Math.max(ε, Math.sqrt(a ** 2 + b ** 2)); | ||
@@ -166,3 +174,3 @@ const Lgamut = clamp(L, 0, 1); | ||
const t = findGamutIntersection(aNorm, bNorm, L, C, Lgamut); | ||
return linearRGBTosRGB(lmsToLinearRGB(oklabToLMS([ | ||
return linearRGBD65TosRGB(lmsToLinearRGBD65(oklabToLMS([ | ||
Lgamut * (1 - t) + t * L, | ||
@@ -174,3 +182,3 @@ aNorm * (t * C), | ||
} | ||
return rgb; | ||
return linearRGBD65TosRGB([R, G, B, alpha]); | ||
} | ||
@@ -181,21 +189,19 @@ /** Oklch -> sRGB */ | ||
} | ||
/** sRGB -> Linear sRGB */ | ||
export function sRGBToLinearRGB(rgb, γ = 2.4) { | ||
const r = Math.abs(rgb[0]); | ||
const g = Math.abs(rgb[1]); | ||
const b = Math.abs(rgb[2]); | ||
/** sRGB -> Linear RGB D65 */ | ||
export function sRGBToLinearRGBD65(rgb) { | ||
const [r, g, b, a] = rgb; | ||
return [ | ||
r < 0.04045 ? rgb[0] / 12.92 : ((r + 0.055) / 1.055) ** γ, | ||
g < 0.04045 ? rgb[1] / 12.92 : ((g + 0.055) / 1.055) ** γ, | ||
b < 0.04045 ? rgb[2] / 12.92 : ((b + 0.055) / 1.055) ** γ, | ||
rgb[3], // alpha | ||
sRGBInverseTransferFunction(r), | ||
sRGBInverseTransferFunction(g), | ||
sRGBInverseTransferFunction(b), | ||
a, // alpha | ||
]; | ||
} | ||
/** Linear sRGB -> Luv */ | ||
/** Linear RGB D65 -> Luv */ | ||
// export function sRGBToLuv(rgb: LinearRGB): LUV { | ||
// return xyzToLuv(linearRGBToXYZ(sRGBToLinearRGB(rgb))); | ||
// return xyzToLuv(linearRGBD65ToXYZ(sRGBToLinearRGB(rgb))); | ||
// } | ||
/** sRGB -> Oklab */ | ||
export function sRGBToOklab(rgb) { | ||
return lmsToOklab(linearRGBToLMS(sRGBToLinearRGB(rgb))); | ||
return lmsToOklab(linearRGBD65ToLMS(sRGBToLinearRGBD65(rgb))); | ||
} | ||
@@ -206,5 +212,5 @@ /** sRGB -> Oklch */ | ||
} | ||
/** XYZ (D65) -> Linear sRGB */ | ||
export function xyzToLinearRGB(xyz) { | ||
return multiplyColorMatrix(xyz, XYZ_D65_TO_LINEAR_RGB); | ||
/** XYZ -> Linear RGB D65 */ | ||
export function xyzToLinearRGBD65(xyz) { | ||
return multiplyColorMatrix(xyz, XYZ_TO_LINEAR_RGB_D65); | ||
} | ||
@@ -211,0 +217,0 @@ /** XYZ (D65) -> Luv */ |
@@ -1,4 +0,6 @@ | ||
export type { ColorMatrix, HSL, HWB, LMS, LinearRGB, LUV, Oklab, Oklch, sRGB, XYZ_D65 } from './colorspace.js'; | ||
export type { AdjustOptions } from './adjust.js'; | ||
export type { ColorMatrix, HSL, HWB, LMS, LinearRGBD65, LUV, Oklab, Oklch, sRGB, XYZ } from './colorspace.js'; | ||
export type { MixColorSpace } from './mix.js'; | ||
export type { ColorOutput } from './parse.js'; | ||
export { default as adjust } from './adjust.js'; | ||
export { contrastRatio, darken, lighten, lightOrDark, lightness, luminance } from './luminance.js'; | ||
@@ -8,2 +10,3 @@ export { mix } from './mix.js'; | ||
export { clamp, colorFn, degToRad, leftPad, multiplyColorMatrix, radToDeg, round } from './utils.js'; | ||
import adjust from './adjust.js'; | ||
import { contrastRatio, darken, lighten, lightOrDark, lightness, luminance } from './luminance.js'; | ||
@@ -13,2 +16,3 @@ import { mix } from './mix.js'; | ||
declare const _default: { | ||
adjust: typeof adjust; | ||
contrastRatio: typeof contrastRatio; | ||
@@ -15,0 +19,0 @@ darken: typeof darken; |
@@ -0,1 +1,2 @@ | ||
export { default as adjust } from './adjust.js'; | ||
export { contrastRatio, darken, lighten, lightOrDark, lightness, luminance } from './luminance.js'; | ||
@@ -5,2 +6,3 @@ export { mix } from './mix.js'; | ||
export { clamp, colorFn, degToRad, leftPad, multiplyColorMatrix, radToDeg, round } from './utils.js'; | ||
import adjust from './adjust.js'; | ||
import { contrastRatio, darken, lighten, lightOrDark, lightness, luminance } from './luminance.js'; | ||
@@ -10,2 +12,3 @@ import { mix } from './mix.js'; | ||
export default { | ||
adjust, | ||
contrastRatio, | ||
@@ -12,0 +15,0 @@ darken, |
@@ -1,1 +0,1 @@ | ||
"use strict";function I(e,n=2){let t=e;for(;t.length<n;)t=`0${t}`;return t}function H(e){return e*(Math.PI/180)}function U(e){return e*(180/Math.PI)}function f(e,n,t){return Math.min(Math.max(e,n),t)}function R(e,n){let[t,r,a,o]=n,s=o<1?`/${k(o,5)}`:"";switch(e){case"rgb":case"rgba":return o<1?`rgba(${Math.round(t*255)}, ${Math.round(r*255)}, ${Math.round(a*255)}, ${k(o,5)})`:`rgb(${Math.round(t*255)}, ${Math.round(r*255)}, ${Math.round(a*255)})`;case"oklab":case"oklch":return`${e}(${k(t*100,6)}% ${k(r,6)} ${k(a,6)}${s})`;default:return`color(${e} ${k(t,6)} ${k(r,6)} ${k(a,6)}${s})`}}function B(e,n){let t=[...e];for(let r=0;r<n.length;r++){let a=0;for(let o=0;o<n[r].length;o++)a+=e[o]*n[r][o];t[r]=a}return t}function k(e,n=2){let t=10**n;return Math.round(e*t)/t}var l0=[[.4123907992659593,.357584339383878,.1804807884018343],[.2126390058715102,.715168678767756,.0721923153607337],[.0193308187155918,.11919477979462,.9505321522496607]],u0=[[3.240969941904522,-1.537383177570094,-.4986107602930034],[-.9692436362808793,1.8759675015077202,.0415550574071756],[.0556300796969937,-.2039769588889766,1.0569715142428782]],x0=[[.2104542553,.793617785,-.0040720468],[1.9779984951,-2.428592205,.4505937099],[.0259040371,.7827717662,-.808675766]],x=[[4.0767416621,-3.3077115913,.2309699292],[-1.2684380046,2.6097574011,-.3413193965],[-.0041960863,-.7034186147,1.707614701]],d0=[[.4122214708,.5363325363,.0514459929],[.2119034982,.6806995451,.1073969566],[.0883024619,.2817188376,.6299787005]],h0=[[1,.39633779217376774,.2158037580607588],[1,-.10556134232365633,-.0638541747717059],[1,-.08948418209496574,-1.2914855378640917]];function _0(e,n){let t=[1/0,1/0,1/0,1/0,1/0],r=1/0,a=1/0,o=1/0;-1.88170328*e-.80936493*n>1?(t=[1.19086277,1.76576728,.59662641,.75515197,.56771245],r=4.0767416621,a=-3.3077115913,o=.2309699292):1.81444104*e-1.19445276*n>1?(t=[.73956515,-.45954404,.08285427,.1254107,.14503204],r=-1.2684380046,a=2.6097574011,o=-.3413193965):(t=[1.35733652,-.00915799,-1.1513021,-.50559606,.00692167],r=-.0041960863,a=-.7034186147,o=1.707614701);let s=t[0]+t[1]*e+t[2]*n+t[3]*e*e+t[4]*e*n,i=.3963377774*e+.2158037573*n,c=-.1055613458*e-.0638541728*n,l=-.0894841775*e-1.291485548*n;{let h=1+s*i,d=1+s*c,b=1+s*l,O=h**3,$=d**3,y=b**3,T=3*i*h**2,w=3*c*d**2,L=3*l*b**2,g=6*i**2*h,u=6*c**2*d,p=6*l**2*b,A=r*O+a*$+o*y,q=r*T+a*w+o*L,F=r*g+a*u+o*p;s=s-A*q/(q*q-.5*A*F)}return s}function M0(e,n){let t=_0(e,n),r=v(X([1,t*e,t*n,1])),a=Math.cbrt(1/Math.max(r[0],r[1],r[3])),o=a*t;return{L:a,C:o}}function b0(e,n,t,r,a){let o=M0(e,n);if((t-a)*o.C-(o.L-a)*r<=0)return o.C*a/(r*o.L+o.C*(a-t));let s=o.C*(a-1)/(r*(o.L-1)+o.C*(a-t)),i=t-a,c=r,l=.3963377774*e+.2158037573*n,h=-.1055613458*e-.0638541728*n,d=-.0894841775*e-1.291485548*n,b=i+c*l,O=i+c*h,$=i+c*d,y=a*(1-s)+s*t,T=s*r,w=y+T*l,L=y+T*h,g=y+T*d,u=[[w**3,L**3,g**3],[3*b*w**2,3*O*L**2,3*$*g**2],[6*b**2*w,6*O**2*L,6*$**2*g]],p=x[0][0]*u[0][0]+x[0][1]*u[0][1]+x[0][2]*u[0][2]-1,A=x[0][0]*u[1][0]+x[0][1]*u[1][1]+x[0][2]*u[1][2],q=x[0][0]*u[2][0]+x[0][1]*u[2][1]+x[0][2]*u[2][2],F=A/(A*A-.5*p*q),m0=F>=0?-p*F:1/0,s0=x[1][0]*u[0][0]+x[1][1]*u[0][1]+x[1][2]*u[0][2]-1,K=x[1][0]*u[1][0]+x[1][1]*u[1][1]+x[1][2]*u[1][2],g0=x[1][0]*u[2][0]+x[1][1]*u[2][1]+x[1][2]*u[2][2],f0=K/(K*K-.5*s0*g0),k0=f0>=0?-s0*f0:1/0,c0=x[2][0]*u[0][0]+x[2][1]*u[0][1]+x[2][2]*u[0][2]-1,j=x[2][0]*u[1][0]+x[2][1]*u[1][1]+x[2][2]*u[1][2],y0=x[2][0]*u[2][0]+x[2][1]*u[2][1]+x[2][2]*u[2][2],i0=j/(j*j-.5*c0*y0),R0=i0>=0?-c0*i0:1/0;return s+Math.min(m0,k0,R0)}function Y(e){let[n,t,r,a]=e;n=Math.abs(n%360);let o=t*(1-Math.abs(2*r-1)),s=o*(1-Math.abs(n/60%2-1)),i=0,c=0,l=0;0<=n&&n<60?(i=o,c=s):60<=n&&n<120?(i=s,c=o):120<=n&&n<180?(c=o,l=s):180<=n&&n<240?(c=s,l=o):240<=n&&n<300?(i=s,l=o):300<=n&&n<360&&(i=o,l=s);let h=r-o/2;return[i+h,c+h,l+h,a]}function J(e){let[n,t,r,a]=e;if(t+r>=1){let s=t/(t+r);return[s,s,s,a]}let o=Y([n,100,50,a]);for(let s=0;s<3;s++)o[s]*=1-t-r,o[s]+=t;return o}function B0(e,n=2e-4){let[t,r,a,o]=e,s=Math.abs(r)<n&&Math.abs(a)<n?0:U(Math.atan2(a,r));for(;s<0;)s+=360;for(;s>=360;)s-=360;return[t,Math.sqrt(r**2+a**2),s,o]}function G0(e){let[n,t,r,a]=e;if(n===0)return[0,0,0,e[3]];for(;r<0;)r+=360;for(;r>=360;)r-=360;let o=H(r);return[n,Math.cos(o)*t,Math.sin(o)*t,a]}function T0(e){return B(e,x0)}function v(e){let[n,t,r,a]=B([e[0]**3,e[1]**3,e[2]**3,e[3]],x);return[n,t,r,a]}function _(e,n=2.4){let t=Math.abs(e[0]),r=Math.abs(e[1]),a=Math.abs(e[2]);return[t<.0031308?e[0]*12.92:1.055*Math.pow(t,1/n)-.055,r<.0031308?e[1]*12.92:1.055*Math.pow(r,1/n)-.055,a<.0031308?e[2]*12.92:1.055*Math.pow(a,1/n)-.055,e[3]]}function Q(e){let n=B(e,d0);return[Math.cbrt(n[0]),Math.cbrt(n[1]),Math.cbrt(n[2]),n[3]]}function W(e){return B(e,l0)}function X(e){return B(e,h0)}function S(e){let n=_(v(X(e)));if(n[0]>1.001||n[0]<-.001||n[1]>1.001||n[1]<-.001||n[2]>1.001||n[2]<-.001){let[r,a,o,s]=e,i=Math.max(1e-5,Math.sqrt(a**2+o**2)),c=f(r,0,1),l=a/i,h=o/i,d=b0(l,h,r,i,c);return _(v(X([c*(1-d)+d*r,l*(d*i),h*(d*i),s])))}return n}function z(e){return S(G0(e))}function M(e,n=2.4){let t=Math.abs(e[0]),r=Math.abs(e[1]),a=Math.abs(e[2]);return[t<.04045?e[0]/12.92:((t+.055)/1.055)**n,r<.04045?e[1]/12.92:((r+.055)/1.055)**n,a<.04045?e[2]/12.92:((a+.055)/1.055)**n,e[3]]}function E(e){return T0(Q(M(e)))}function D(e){return B0(E(e))}function C(e){return B(e,u0)}var w0={black:0,silver:12632256,gray:8421504,white:16777215,maroon:8388608,red:16711680,purple:8388736,fuchsia:16711935,green:32768,lime:65280,olive:8421376,yellow:16776960,navy:128,blue:255,teal:32896,aqua:65535,orange:16753920,aliceblue:15792383,antiquewhite:16444375,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,blanchedalmond:16772045,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,limegreen:3329330,linen:16445670,magenta:16711935,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,oldlace:16643558,olivedrab:7048739,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,whitesmoke:16119285,yellowgreen:10145074,rebeccapurple:6697881},e0=w0;var L0=/-?[0-9.]+%?/g,O0=/^#?[0-9a-f]{3,8}$/i,$0=16**6,t0=16**4,n0=16**2;function m(e){let n=A0(e),t={get hex(){let r="#";return r+=I(Math.round(f(n[0]*255,0,255)).toString(16),2),r+=I(Math.round(f(n[1]*255,0,255)).toString(16),2),r+=I(Math.round(f(n[2]*255,0,255)).toString(16),2),n[3]<1&&(r+=I(Math.round(n[3]*255).toString(16),2)),r},get hexVal(){n[3]<1&&console.warn(`hexVal converted a semi-transparent color (${n[3]*100}%) to fully opaque`);let r=Math.round(f(n[0]*255,0,255)),a=Math.round(f(n[1]*255,0,255)),o=Math.round(f(n[2]*255,0,255));return r*t0+a*n0+o},get rgb(){return R("rgb",n)},rgbVal:n,get rgba(){return R("rgb",n)},rgbaVal:n,get linearRGB(){return R("srgb-linear",M(n))},get linearRGBVal(){return M(n)},get p3(){return R("display-p3",n)},p3Val:n,get oklab(){return R("oklab",E(n))},get oklabVal(){return E(n)},get oklch(){return R("oklch",D(n))},get oklchVal(){return D(n)},get xyz(){return R("xyz-d65",W(M(n)))},get xyzVal(){return W(M(n))}};return t.toString=()=>t.hex,t}function p0(e){if(e>$0)throw new Error("8-digit hex values (with transparency) aren\u2019t supported");let n=e,t=Math.floor(n/t0);n-=t*t0;let r=Math.floor(n/n0);n-=r*n0;let a=n;return[t/255,r/255,a/255,1]}function G(e,n){let t=e.match(L0);if(!t)throw new Error(`Unexpected color format: ${e}`);let r=[0,0,0,1];return t.forEach((a,o)=>{a.includes("%")?r[o]=parseFloat(a)/100:n[o]===1/0||n[o]===0||n[o]===1?r[o]=parseFloat(a):r[o]=parseFloat(a)/n[o]}),r}function A0(e){let n=new Error(`Unable to parse color "${e}"`);if(e==null||e==null)throw n;if(Array.isArray(e)){if(typeof e[0]!="number"||typeof e[1]!="number"||typeof e[2]!="number")throw new Error(`Color array must be numbers, received ${e}`);if(e.length<3||e.length>4)throw new Error(`Expected [R, G, B, A?], received ${e}`);return[f(e[0],0,1),f(e[1],0,1),f(e[2],0,1),typeof e[3]=="number"?f(e[3],0,1):1]}if(typeof e=="object"){let t={...e},r=1;for(let a of Object.keys(t))a==="alpha"?r=f(t[a],0,1):t[a.toLowerCase()]=t[a];if("r"in t&&"g"in t&&"b"in t)return[f(t.r||t.R,0,1),f(t.r||t.R,0,1),f(t.r||t.R,0,1),r];if("h"in t&&"s"in t&&"l"in t)return Y([t.h,f(t.s,0,1),f(t.l,0,1),r]);if("h"in t&&"w"in t&&"b"in t)return J([t.h,f(t.w,0,1),f(t.b,0,1),r]);if("l"in t&&"a"in t&&"b"in t)return S([t.l,t.a,t.b,r]);if("l"in t&&"c"in t&&"h"in t)return z([t.l,t.c,t.h,r]);if("x"in t&&"y"in t&&"z"in t)return _(C([t.x,t.y,t.z,r]));throw n}if(typeof e=="number")return p0(e);if(typeof e=="string"){let t=e.trim();if(!t)throw new Error("Expected color, received empty string");let r=t.toLowerCase();if(typeof e0[r]=="number")return p0(e0[r]);if(O0.test(t)){let s=t.replace("#",""),i=[0,0,0,1];if(s.length>=6)for(let c=0;c<s.length/2;c++){let l=c*2,h=l+2,d=s.substring(l,h);i[c]=parseInt(d,16)/255}else for(let c=0;c<s.length;c++){let l=s.charAt(c);i[c]=parseInt(`${l}${l}`,16)/255}return i}let[a,o]=t.split("(");if(a==="color"){let s=o.indexOf(" ");a=o.substring(0,s),o=o.substring(s)}switch(a){case"rgb":case"rgba":case"srgb":{let[s,i,c,l]=G(o,[255,255,255,1]);return[f(s,0,1),f(i,0,1),f(c,0,1),f(l,0,1)]}case"linear-rgb":case"linear-srgb":case"rgb-linear":case"srgb-linear":{let s=G(o,[255,255,255,1]);return _(s)}case"hsl":case"hsla":{let[s,i,c,l]=G(o,[1,1,1,1]);return Y([s,f(i,0,1),f(c,0,1),f(l,0,1)])}case"hwb":case"hwba":{let[s,i,c,l]=G(o,[1,1,1,1]);return J([s,f(i,0,1),f(c,0,1),f(l,0,1)])}case"p3":case"display-p3":{let[s,i,c,l]=G(o,[1,1,1,1]);return[f(s,0,1),f(i,0,1),f(c,0,1),f(l,0,1)]}case"oklab":return S(G(o,[1,1,1,1]));case"oklch":return z(G(o,[1,1,1,1]));case"xyz":case"xyz-d65":return _(C(G(o,[1,1,1,1])))}}throw n}function V(e,n,t=.5,r="oklab"){let a=f(t,0,1);if(t===0)return m(e);if(t===1)return m(n);let o=1-a,s=a,i={oklch:D,oklab:E,lms:p=>M(Q(p)),linearRGB:M,sRGB:p=>p},c={oklch:z,oklab:S,lms:p=>v(_(p)),linearRGB:_,sRGB:p=>p},l=i[r],h=c[r];if(!l)throw new Error(`Unknown color space "${r}", try "oklab", "oklch", "linearRGB", or "sRGB"`);let d=m(e).rgbVal,b=m(n).rgbVal;r==="oklch"&&(d[0]===d[1]&&d[1]===d[2]||b[0]===b[1]&&b[1]===b[2])&&(l=i.oklab,h=c.oklab);let[O,$,y,T]=l(d),[w,L,g,u]=l(b);return r==="oklch"&&Math.abs(g-y)>180&&(Math.max(y,g)===g?g-=360:y-=360),m(h([O*o+w*s,$*o+L*s,y*o+g*s,T*o+u*s]))}function Z(e,n,t="oklab"){let r=f(n,-1,1);return r>=0?V(e,"black",r,t):P(e,-r)}function P(e,n,t="oklab"){let r=f(n,-1,1);return r>=0?V(e,"white",r,t):Z(e,-r)}function N(e){return m(e).xyzVal[1]}function r0(e){return k(m(e).oklabVal[0],5)}function o0(e){return N(e)<.36?"dark":"light"}function a0(e,n){let t=N(e),r=N(n),a=Math.max(t,r),o=Math.min(t,r),s=(a+.05)/(o+.05);return{ratio:s,AA:s>=4.5,AAA:s>=7}}var te={contrastRatio:a0,darken:Z,from:m,lighten:P,lightness:r0,lightOrDark:o0,luminance:N,mix:V};export{f as clamp,R as colorFn,a0 as contrastRatio,Z as darken,te as default,H as degToRad,m as from,I as leftPad,o0 as lightOrDark,P as lighten,r0 as lightness,N as luminance,V as mix,B as multiplyColorMatrix,U as radToDeg,k as round}; | ||
function N(r,t=2){let e=r;for(;e.length<t;)e=`0${e}`;return e}function J(r){return r*(Math.PI/180)}function Q(r){return r*(180/Math.PI)}function l(r,t,e){return Math.min(Math.max(r,t),e)}function _(r,t){let[e,n,f,o]=t,a=o<1?`/${k(o,5)}`:"";switch(r){case"rgb":case"rgba":return o<1?`rgba(${Math.round(e*255)}, ${Math.round(n*255)}, ${Math.round(f*255)}, ${k(o,5)})`:`rgb(${Math.round(e*255)}, ${Math.round(n*255)}, ${Math.round(f*255)})`;case"oklab":case"oklch":return`${r}(${k(e*100,6)}% ${k(n,6)} ${k(f,6)}${a})`;default:return`color(${r} ${k(e,6)} ${k(n,6)} ${k(f,6)}${a})`}}function L(r,t){let e=[...r];for(let n=0;n<t.length;n++){let f=0;for(let o=0;o<t[n].length;o++)f+=r[o]*t[n][o];e[n]=f}return e}function k(r,t=2){let e=10**t;return Math.round(r*e)/e}var he=[[.4123907992659593,.357584339383878,.1804807884018343],[.2126390058715102,.715168678767756,.0721923153607337],[.0193308187155918,.119194779794626,.9505321522496607]],be=[[3.240969941904522,-1.537383177570094,-.4986107602930034],[-.9692436362808793,1.8759675015077202,.0415550574071756],[.0556300796969937,-.2039769588889766,1.0569715142428782]],me=[[.2104542553,.793617785,-.0040720468],[1.9779984951,-2.428592205,.4505937099],[.0259040371,.7827717662,-.808675766]],x=[[4.0767416621,-3.3077115913,.2309699292],[-1.2684380046,2.6097574011,-.3413193965],[-.0041960863,-.7034186147,1.707614701]],ge=[[.4122214708,.5363325363,.0514459929],[.2119034982,.6806995451,.1073969566],[.0883024619,.2817188376,.6299787005]],pe=[[1,.39633779217376774,.2158037580607588],[1,-.10556134232365633,-.0638541747717059],[1,-.08948418209496574,-1.2914855378640917]];function Te(r,t){let e=[1/0,1/0,1/0,1/0,1/0],n=1/0,f=1/0,o=1/0;-1.88170328*r-.80936493*t>1?(e=[1.19086277,1.76576728,.59662641,.75515197,.56771245],n=4.0767416621,f=-3.3077115913,o=.2309699292):1.81444104*r-1.19445276*t>1?(e=[.73956515,-.45954404,.08285427,.1254107,.14503204],n=-1.2684380046,f=2.6097574011,o=-.3413193965):(e=[1.35733652,-.00915799,-1.1513021,-.50559606,.00692167],n=-.0041960863,f=-.7034186147,o=1.707614701);let a=e[0]+e[1]*r+e[2]*t+e[3]*r*r+e[4]*r*t,s=.3963377774*r+.2158037573*t,c=-.1055613458*r-.0638541728*t,i=-.0894841775*r-1.291485548*t;{let d=1+a*s,h=1+a*c,b=1+a*i,g=d**3,E=h**3,T=b**3,y=3*s*d**2,M=3*c*h**2,$=3*i*b**2,v=6*s**2*d,u=6*c**2*h,R=6*i**2*b,w=n*g+f*E+o*T,I=n*y+f*M+o*$,V=n*v+f*u+o*R;a=a-w*I/(I*I-.5*w*V)}return a}function Me(r,t){let e=Te(r,t),[n,f,o]=q(P([1,e*r,e*t,1])),a=Math.cbrt(1/Math.max(n,f,o)),s=a*e;return{L:a,C:s}}function ke(r,t,e,n,f){let o=Me(r,t);if((e-f)*o.C-(o.L-f)*n<=0)return o.C*f/(n*o.L+o.C*(f-e));let a=o.C*(f-1)/(n*(o.L-1)+o.C*(f-e)),s=e-f,c=n,i=.3963377774*r+.2158037573*t,d=-.1055613458*r-.0638541728*t,h=-.0894841775*r-1.291485548*t,b=s+c*i,g=s+c*d,E=s+c*h,T=f*(1-a)+a*e,y=a*n,M=T+y*i,$=T+y*d,v=T+y*h,u=[[M**3,$**3,v**3],[3*b*M**2,3*g*$**2,3*E*v**2],[6*b**2*M,6*g**2*$,6*E**2*v]],R=x[0][0]*u[0][0]+x[0][1]*u[0][1]+x[0][2]*u[0][2]-1,w=x[0][0]*u[1][0]+x[0][1]*u[1][1]+x[0][2]*u[1][2],I=x[0][0]*u[2][0]+x[0][1]*u[2][1]+x[0][2]*u[2][2],V=w/(w*w-.5*R*I),A=V>=0?-R*V:1/0,Z=x[1][0]*u[0][0]+x[1][1]*u[0][1]+x[1][2]*u[0][2]-1,p=x[1][0]*u[1][0]+x[1][1]*u[1][1]+x[1][2]*u[1][2],Re=x[1][0]*u[2][0]+x[1][1]*u[2][1]+x[1][2]*u[2][2],ue=p/(p*p-.5*Z*Re),_e=ue>=0?-Z*ue:1/0,xe=x[2][0]*u[0][0]+x[2][1]*u[0][1]+x[2][2]*u[0][2]-1,C=x[2][0]*u[1][0]+x[2][1]*u[1][1]+x[2][2]*u[1][2],Be=x[2][0]*u[2][0]+x[2][1]*u[2][1]+x[2][2]*u[2][2],de=C/(C*C-.5*xe*Be),Ge=de>=0?-xe*de:1/0;return a+Math.min(A,_e,Ge)}function W(r){let t=Math.abs(r);return t<=.0031308?r*12.92:1.055*Math.pow(t,1/2.4)-.055}function ee(r){return Math.abs(r)<=.04045?r/12.92:((Math.abs(r)+.055)/1.055)**2.4}function H(r){let[t,e,n,f]=r;t=Math.abs(t%360);let o=e*(1-Math.abs(2*n-1)),a=o*(1-Math.abs(t/60%2-1)),s=0,c=0,i=0;0<=t&&t<60?(s=o,c=a):60<=t&&t<120?(s=a,c=o):120<=t&&t<180?(c=o,i=a):180<=t&&t<240?(c=a,i=o):240<=t&&t<300?(s=a,i=o):300<=t&&t<360&&(s=o,i=a);let d=n-o/2;return[s+d,c+d,i+d,f]}function te(r){let[t,e,n,f]=r;if(e+n>=1){let a=e/(e+n);return[a,a,a,f]}let o=H([t,100,50,f]);for(let a=0;a<3;a++)o[a]*=1-e-n,o[a]+=e;return o}function we(r,t=2e-4){let[e,n,f,o]=r,a=Math.abs(n)<t&&Math.abs(f)<t?0:Q(Math.atan2(f,n));for(;a<0;)a+=360;for(;a>=360;)a-=360;return[e,Math.sqrt(n**2+f**2),a,o]}function Le(r){let[t,e,n,f]=r;if(t===0)return[0,0,0,f];for(;n<0;)n+=360;for(;n>=360;)n-=360;let o=J(n);return[t,Math.cos(o)*e,Math.sin(o)*e,f]}function Oe(r){return L(r,me)}function q(r){let[t,e,n,f]=r,[o,a,s]=L([t**3,e**3,n**3,f],x);return[o,a,s,f]}function B(r){let[t,e,n,f]=r;return[W(t),W(e),W(n),f]}function re(r){let[t,e,n,f]=L(r,ge);return[Math.cbrt(t),Math.cbrt(e),Math.cbrt(n),f]}function ne(r){return L(r,he)}function P(r){return L(r,pe)}function z(r){let[t,e,n,f]=q(P(r));if(t>1.001||t<-.001||e>1.001||e<-.001||n>1.001||n<-.001){let[a,s,c]=r,i=Math.max(1e-5,Math.sqrt(s**2+c**2)),d=l(a,0,1),h=s/i,b=c/i,g=ke(h,b,a,i,d);return B(q(P([d*(1-g)+g*a,h*(g*i),b*(g*i),f])))}return B([t,e,n,f])}function D(r){return z(Le(r))}function G(r){let[t,e,n,f]=r;return[ee(t),ee(e),ee(n),f]}function F(r){return Oe(re(G(r)))}function S(r){return we(F(r))}function oe(r){return L(r,be)}function j(r,t){let[e,n,f,o]=S(r);return typeof t.lightness=="number"&&(t.mode==="relative"?e+=t.lightness:e=t.lightness),typeof t.chroma=="number"&&(t.mode==="relative"?n+=t.chroma:n=t.chroma),typeof t.hue=="number"&&(t.mode==="relative"?f+=t.hue:f=t.hue),typeof t.alpha=="number"&&(t.mode==="relative"?o+=t.alpha:o=t.alpha),D([e,n,f,o])}var $e={black:0,silver:12632256,gray:8421504,white:16777215,maroon:8388608,red:16711680,purple:8388736,fuchsia:16711935,green:32768,lime:65280,olive:8421376,yellow:16776960,navy:128,blue:255,teal:32896,aqua:65535,orange:16753920,aliceblue:15792383,antiquewhite:16444375,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,blanchedalmond:16772045,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,limegreen:3329330,linen:16445670,magenta:16711935,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,oldlace:16643558,olivedrab:7048739,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,whitesmoke:16119285,yellowgreen:10145074,rebeccapurple:6697881},ae=$e;var ve=/-?[0-9.]+%?/g,Ie=/^#[0-9a-f]{3,8}$/i,Ae=16**6,fe=16**4,se=16**2;function m(r){let t=De(r),e={get hex(){let[n,f,o,a]=t,s="#";return s+=N(Math.round(l(n*255,0,255)).toString(16),2),s+=N(Math.round(l(f*255,0,255)).toString(16),2),s+=N(Math.round(l(o*255,0,255)).toString(16),2),t[3]<1&&(s+=N(Math.round(a*255).toString(16),2)),s},get hexVal(){let[n,f,o,a]=t;return a<1&&console.warn(`hexVal converted a semi-transparent color (${a*100}%) to fully opaque`),n=Math.round(l(n*255,0,255)),f=Math.round(l(f*255,0,255)),o=Math.round(l(o*255,0,255)),n*fe+f*se+o},get rgb(){return _("rgb",t)},rgbVal:t,get rgba(){return _("rgb",t)},rgbaVal:t,get linearRGB(){return _("srgb-linear",G(t))},get linearRGBVal(){return G(t)},get p3(){return _("display-p3",t)},p3Val:t,get oklab(){return _("oklab",F(t))},get oklabVal(){return F(t)},get oklch(){return _("oklch",S(t))},get oklchVal(){return S(t)},get xyz(){return _("xyz-d65",ne(G(t)))},get xyzVal(){return ne(G(t))},adjust(n){return m(j(t,n))}};return e.toString=()=>e.hex,e}function ye(r){if(r>Ae)throw new Error("8-digit hex values (with transparency) aren\u2019t supported");let t=r,e=Math.floor(t/fe);t-=e*fe;let n=Math.floor(t/se);t-=n*se;let f=t;return[e/255,n/255,f/255,1]}function O(r,t){let e=r.match(ve);if(!e)throw new Error(`Unexpected color format: ${r}`);let n=[0,0,0,1];return e.forEach((f,o)=>{f.includes("%")?n[o]=parseFloat(f)/100:!t||t[o]===1/0||t[o]===1?n[o]=parseFloat(f):n[o]=parseFloat(f)/t[o]}),n}function De(r){let t=new Error(`Unable to parse color ${JSON.stringify(r)}`);if(r==null||r==null||typeof r=="boolean")throw t;if(Array.isArray(r)){if(r.some(a=>typeof a!="number"))throw new Error(`Color array must be numbers, received ${r}`);if(r.length<3||r.length>4)throw new Error(`Expected [R, G, B, A?], received ${r}`);let[e,n,f,o]=r;return[l(e,0,1),l(n,0,1),l(f,0,1),typeof o=="number"?l(o,0,1):1]}if(typeof r=="number")return ye(r);if(typeof r=="string"){let e=r.trim();if(!e)throw new Error("Expected color, received empty string");let n=e.toLowerCase();if(typeof ae[n]=="number")return ye(ae[n]);if(Ie.test(n)){let a=n.replace("#",""),s=[0,0,0,1];if(a.length===6||a.length===8)for(let c=0;c<a.length/2;c++){let i=c*2,d=i+2,h=a.substring(i,d);s[c]=parseInt(h,16)/255}else if(a.length===3||a.length===4)for(let c=0;c<a.length;c++){let i=a.charAt(c);s[c]=parseInt(`${i}${i}`,16)/255}else throw new Error(`Hex value "${n}" not a valid sRGB color`);return s}let[f,o]=e.split("(");if(f==="color"){let a=o.indexOf(" ");f=o.substring(0,a),o=o.substring(a)}switch(f){case"rgb":case"rgba":case"srgb":{let[a,s,c,i]=O(o,[255,255,255,1]);return[l(a,0,1),l(s,0,1),l(c,0,1),l(i,0,1)]}case"linear-rgb":case"linear-srgb":case"rgb-linear":case"srgb-linear":{let a=O(o);return B(a)}case"hsl":case"hsla":{let[a,s,c,i]=O(o);return H([a,l(s,0,1),l(c,0,1),l(i,0,1)])}case"hwb":case"hwba":{let[a,s,c,i]=O(o);return te([a,l(s,0,1),l(c,0,1),l(i,0,1)])}case"p3":case"display-p3":{let[a,s,c,i]=O(o);return[l(a,0,1),l(s,0,1),l(c,0,1),l(i,0,1)]}case"oklab":return z(O(o));case"oklch":return D(O(o));case"xyz":case"xyz-d65":return B(oe(O(o)))}}if(typeof r=="object"){let e={...r},n=1;for(let f of Object.keys(e))f==="alpha"?n=l(e[f],0,1):e[f.toLowerCase()]=e[f];if("r"in e&&"g"in e&&"b"in e)return[l(e.r,0,1),l(e.g,0,1),l(e.b,0,1),n];if("h"in e&&"s"in e&&"l"in e)return H([e.h,l(e.s,0,1),l(e.l,0,1),n]);if("h"in e&&"w"in e&&"b"in e)return te([e.h,l(e.w,0,1),l(e.b,0,1),n]);if("l"in e&&"a"in e&&"b"in e)return z([e.l,e.a,e.b,n]);if("l"in e&&"c"in e&&"h"in e)return D([e.l,e.c,e.h,n]);if("x"in e&&"y"in e&&"z"in e)return B(oe([e.x,e.y,e.z,n]));throw t}throw t}function X(r,t,e=.5,n="oklab"){let f=l(e,0,1);if(e===0)return m(r);if(e===1)return m(t);let o=1-f,a=f,s={oklch:S,oklab:F,lms:p=>G(re(p)),linearRGB:G,sRGB:p=>p},c={oklch:D,oklab:z,lms:p=>q(B(p)),linearRGB:B,sRGB:p=>p},i=s[n],d=c[n];if(!i)throw new Error(`Unknown color space "${n}", try "oklab", "oklch", "linearRGB", or "sRGB"`);let[h,b,g,E]=m(r).rgbVal,[T,y,M,$]=m(t).rgbVal;n==="oklch"&&(h===b&&b===g||T===y&&y===M)&&(i=s.oklab,d=c.oklab);let[v,u,R,w]=i([h,b,g,E]),[I,V,A,Z]=i([T,y,M,$]);return n==="oklch"&&Math.abs(A-R)>180&&(Math.max(R,A)===A?A-=360:R-=360),m(d([v*o+I*a,u*o+V*a,R*o+A*a,w*o+Z*a]))}function K(r,t,e="oklab"){let n=l(t,-1,1);return n>=0?X(r,"black",n,e):U(r,-n)}function U(r,t,e="oklab"){let n=l(t,-1,1);return n>=0?X(r,"white",n,e):K(r,-n)}function Y(r){return m(r).xyzVal[1]}function le(r){return k(m(r).oklabVal[0],5)}function ce(r){return Y(r)<.36?"dark":"light"}function ie(r,t){let e=Y(r),n=Y(t),f=Math.max(e,n),o=Math.min(e,n),a=(f+.05)/(o+.05);return{ratio:a,AA:a>=4.5,AAA:a>=7}}var l0={adjust:j,contrastRatio:ie,darken:K,from:m,lighten:U,lightness:le,lightOrDark:ce,luminance:Y,mix:X};export{j as adjust,l as clamp,_ as colorFn,ie as contrastRatio,K as darken,l0 as default,J as degToRad,m as from,N as leftPad,ce as lightOrDark,U as lighten,le as lightness,Y as luminance,X as mix,L as multiplyColorMatrix,Q as radToDeg,k as round}; |
import type { ColorMatrix } from './index'; | ||
export declare const LINEAR_RGB_TO_XYZ_D65: ColorMatrix; | ||
export declare const XYZ_D65_TO_LINEAR_RGB: ColorMatrix; | ||
export declare const LINEAR_RGB_D65_TO_XYZ: ColorMatrix; | ||
export declare const XYZ_TO_LINEAR_RGB_D65: ColorMatrix; | ||
export declare const LMS_TO_OKLAB: ColorMatrix; | ||
@@ -5,0 +5,0 @@ export declare const LMS_TO_LINEAR_RGB: ColorMatrix; |
@@ -1,9 +0,9 @@ | ||
import { lmsToLinearRGB, oklabToLMS } from './colorspace.js'; | ||
import { lmsToLinearRGBD65, oklabToLMS } from './colorspace.js'; | ||
// https://observablehq.com/@danburzo/color-matrix-calculator | ||
export const LINEAR_RGB_TO_XYZ_D65 = [ | ||
export const LINEAR_RGB_D65_TO_XYZ = [ | ||
[0.4123907992659593, 0.357584339383878, 0.1804807884018343], | ||
[0.2126390058715102, 0.715168678767756, 0.0721923153607337], | ||
[0.0193308187155918, 0.11919477979462, 0.9505321522496607], | ||
[0.0193308187155918, 0.119194779794626, 0.9505321522496607], | ||
]; | ||
export const XYZ_D65_TO_LINEAR_RGB = [ | ||
export const XYZ_TO_LINEAR_RGB_D65 = [ | ||
[3.2409699419045221, -1.5373831775700939, -0.4986107602930034], | ||
@@ -122,5 +122,5 @@ [-0.9692436362808793, 1.8759675015077202, 0.0415550574071756], | ||
const S_cusp = computeMaxSaturation(a, b); | ||
// Convert to linear sRGB to find the first point where at least one of r,g or b >= 1: | ||
const rgb_at_max = lmsToLinearRGB(oklabToLMS([1, S_cusp * a, S_cusp * b, 1])); | ||
const L_cusp = Math.cbrt(1 / Math.max(rgb_at_max[0], rgb_at_max[1], rgb_at_max[3])); | ||
// Convert to linear RGB (D65) to find the first point where at least one of r,g or b >= 1: | ||
const [R, G, B] = lmsToLinearRGBD65(oklabToLMS([1, S_cusp * a, S_cusp * b, 1])); | ||
const L_cusp = Math.cbrt(1 / Math.max(R, G, B)); | ||
const C_cusp = L_cusp * S_cusp; | ||
@@ -127,0 +127,0 @@ return { L: L_cusp, C: C_cusp }; |
import { Color } from './colorspace.js'; | ||
import type { ColorOutput } from './parse.js'; | ||
export declare type MixColorSpace = 'oklab' | 'oklch' | 'lms' | 'linearRGB' | 'sRGB'; | ||
export type MixColorSpace = 'oklab' | 'oklch' | 'lms' | 'linearRGB' | 'sRGB'; | ||
/** | ||
@@ -5,0 +5,0 @@ * Mix colors via LMS (better for preserving hue than Oklab) |
@@ -1,3 +0,3 @@ | ||
import { linearRGBToLMS, lmsToLinearRGB } from './colorspace.js'; | ||
import { linearRGBTosRGB, sRGBToOklab, sRGBToOklch, sRGBToLinearRGB, oklabTosRGB, oklchTosRGB } from './colorspace.js'; | ||
import { linearRGBD65ToLMS, lmsToLinearRGBD65 } from './colorspace.js'; | ||
import { linearRGBD65TosRGB, sRGBToOklab, sRGBToOklch, sRGBToLinearRGBD65, oklabTosRGB, oklchTosRGB } from './colorspace.js'; | ||
import { from } from './parse.js'; | ||
@@ -24,4 +24,4 @@ import { clamp } from './utils.js'; | ||
oklab: sRGBToOklab, | ||
lms: (c) => sRGBToLinearRGB(linearRGBToLMS(c)), | ||
linearRGB: sRGBToLinearRGB, | ||
lms: (c) => sRGBToLinearRGBD65(linearRGBD65ToLMS(c)), | ||
linearRGB: sRGBToLinearRGBD65, | ||
sRGB: (c) => c, | ||
@@ -33,4 +33,4 @@ }; | ||
oklab: oklabTosRGB, | ||
lms: (c) => lmsToLinearRGB(linearRGBTosRGB(c)), | ||
linearRGB: linearRGBTosRGB, | ||
lms: (c) => lmsToLinearRGBD65(linearRGBD65TosRGB(c)), | ||
linearRGB: linearRGBD65TosRGB, | ||
sRGB: (c) => c, | ||
@@ -42,6 +42,6 @@ }; | ||
throw new Error(`Unknown color space "${colorSpace}", try "oklab", "oklch", "linearRGB", or "sRGB"`); | ||
const rgb1 = from(color1).rgbVal; | ||
const rgb2 = from(color2).rgbVal; | ||
const [r1, g1, b1, alpha1] = from(color1).rgbVal; | ||
const [r2, g2, b2, alpha2] = from(color2).rgbVal; | ||
// Oklch fix: if one color is neutral, use Oklab to prevent hue shifting | ||
if (colorSpace === 'oklch' && ((rgb1[0] === rgb1[1] && rgb1[1] === rgb1[2]) || (rgb2[0] === rgb2[1] && rgb2[1] === rgb2[2]))) { | ||
if (colorSpace === 'oklch' && ((r1 === g1 && g1 === b1) || (r2 === g2 && g2 === b2))) { | ||
converter = converters.oklab; | ||
@@ -51,4 +51,4 @@ deconverter = deconverters.oklab; | ||
// convert color into mix colorspace | ||
let [x1, y1, z1, a1] = converter(rgb1); | ||
let [x2, y2, z2, a2] = converter(rgb2); | ||
let [x1, y1, z1, a1] = converter([r1, g1, b1, alpha1]); | ||
let [x2, y2, z2, a2] = converter([r2, g2, b2, alpha2]); | ||
// Oklch: take shortest hue distance (e.g. 0 and 359 should only be 1 degree apart, not 359) | ||
@@ -55,0 +55,0 @@ if (colorSpace === 'oklch' && Math.abs(z2 - z1) > 180) { |
@@ -1,2 +0,2 @@ | ||
import type { Color, LinearRGB, Oklab, Oklch, sRGB, XYZ_D65 } from './colorspace.js'; | ||
import type { Color, LinearRGBD65, Oklab, Oklch, sRGB, XYZ } from './colorspace.js'; | ||
export interface ColorOutput { | ||
@@ -18,3 +18,3 @@ /** `#000000` */ | ||
/** [R, G, B, alpha] */ | ||
linearRGBVal: LinearRGB; | ||
linearRGBVal: LinearRGBD65; | ||
/** `color(luv 0 0 0/1)` */ | ||
@@ -34,6 +34,6 @@ /** [L, u, v, alpha] */ | ||
oklchVal: Oklch; | ||
/** `color(xyz 0 0 0/1)` (2°, D65 whitepoint) */ | ||
/** `color(xyz 0 0 0/1)` */ | ||
xyz: string; | ||
/** [X, Y, Z, alpha] (2°, D65 whitespace) */ | ||
xyzVal: XYZ_D65; | ||
/** [X, Y, Z, alpha] */ | ||
xyzVal: XYZ; | ||
toString(): string; | ||
@@ -40,0 +40,0 @@ } |
@@ -1,2 +0,3 @@ | ||
import { hslTosRGB, hwbTosRGB, linearRGBTosRGB, linearRGBToXYZ, oklabTosRGB, oklchTosRGB, sRGBToLinearRGB, sRGBToOklab, sRGBToOklch, xyzToLinearRGB } from './colorspace.js'; | ||
import adjust from './adjust.js'; | ||
import { hslTosRGB, hwbTosRGB, linearRGBD65TosRGB, linearRGBD65ToXYZ, oklabTosRGB, oklchTosRGB, sRGBToLinearRGBD65, sRGBToOklab, sRGBToOklch, xyzToLinearRGBD65 } from './colorspace.js'; | ||
import cssNames from './css-names.js'; | ||
@@ -6,3 +7,3 @@ import { clamp, colorFn, leftPad } from './utils.js'; | ||
const FLOAT_RE = /-?[0-9.]+%?/g; | ||
const HEX_RE = /^#?[0-9a-f]{3,8}$/i; | ||
const HEX_RE = /^#[0-9a-f]{3,8}$/i; | ||
const RGB_RANGE = 16 ** 6; | ||
@@ -29,16 +30,18 @@ const R_FACTOR = 16 ** 4; // base 16, starting after 4 digits (GGBB) | ||
get hex() { | ||
const [r, g, b, a] = color; | ||
let hexString = '#'; | ||
hexString += leftPad(Math.round(clamp(color[0] * 255, 0, 255)).toString(16), 2); // r | ||
hexString += leftPad(Math.round(clamp(color[1] * 255, 0, 255)).toString(16), 2); // g | ||
hexString += leftPad(Math.round(clamp(color[2] * 255, 0, 255)).toString(16), 2); // b | ||
hexString += leftPad(Math.round(clamp(r * 255, 0, 255)).toString(16), 2); // r | ||
hexString += leftPad(Math.round(clamp(g * 255, 0, 255)).toString(16), 2); // g | ||
hexString += leftPad(Math.round(clamp(b * 255, 0, 255)).toString(16), 2); // b | ||
if (color[3] < 1) | ||
hexString += leftPad(Math.round(color[3] * 255).toString(16), 2); // a | ||
hexString += leftPad(Math.round(a * 255).toString(16), 2); // a | ||
return hexString; | ||
}, | ||
get hexVal() { | ||
if (color[3] < 1) | ||
console.warn(`hexVal converted a semi-transparent color (${color[3] * 100}%) to fully opaque`); // eslint-disable-line no-console | ||
const r = Math.round(clamp(color[0] * 255, 0, 255)); | ||
const g = Math.round(clamp(color[1] * 255, 0, 255)); | ||
const b = Math.round(clamp(color[2] * 255, 0, 255)); | ||
let [r, g, b, a] = color; | ||
if (a < 1) | ||
console.warn(`hexVal converted a semi-transparent color (${a * 100}%) to fully opaque`); // eslint-disable-line no-console | ||
r = Math.round(clamp(r * 255, 0, 255)); | ||
g = Math.round(clamp(g * 255, 0, 255)); | ||
b = Math.round(clamp(b * 255, 0, 255)); | ||
return r * R_FACTOR + g * G_FACTOR + b; | ||
@@ -61,6 +64,6 @@ }, | ||
get linearRGB() { | ||
return colorFn('srgb-linear', sRGBToLinearRGB(color)); | ||
return colorFn('srgb-linear', sRGBToLinearRGBD65(color)); | ||
}, | ||
get linearRGBVal() { | ||
return sRGBToLinearRGB(color); | ||
return sRGBToLinearRGBD65(color); | ||
}, | ||
@@ -84,7 +87,10 @@ get p3() { | ||
get xyz() { | ||
return colorFn('xyz-d65', linearRGBToXYZ(sRGBToLinearRGB(color))); | ||
return colorFn('xyz-d65', linearRGBD65ToXYZ(sRGBToLinearRGBD65(color))); | ||
}, | ||
get xyzVal() { | ||
return linearRGBToXYZ(sRGBToLinearRGB(color)); | ||
return linearRGBD65ToXYZ(sRGBToLinearRGBD65(color)); | ||
}, | ||
adjust(options) { | ||
return from(adjust(color, options)); | ||
}, | ||
}; | ||
@@ -121,3 +127,3 @@ // JS helper | ||
// unbounded | ||
else if (normalize[n] === Infinity || normalize[n] === 0 || normalize[n] === 1) | ||
else if (!normalize || normalize[n] === Infinity || normalize[n] === 1) | ||
values[n] = parseFloat(value); | ||
@@ -132,57 +138,19 @@ // bounded | ||
export function parse(rawColor) { | ||
const unparsable = new Error(`Unable to parse color "${rawColor}"`); | ||
if (rawColor == undefined || rawColor == null) | ||
const unparsable = new Error(`Unable to parse color ${JSON.stringify(rawColor)}`); | ||
if (rawColor == undefined || rawColor == null || typeof rawColor === 'boolean') | ||
throw unparsable; | ||
// [R, G, B] or [R, G, B, A] | ||
if (Array.isArray(rawColor)) { | ||
if (typeof rawColor[0] != 'number' || typeof rawColor[1] != 'number' || typeof rawColor[2] != 'number') | ||
if (rawColor.some((v) => typeof v !== 'number')) | ||
throw new Error(`Color array must be numbers, received ${rawColor}`); | ||
if (rawColor.length < 3 || rawColor.length > 4) | ||
throw new Error(`Expected [R, G, B, A?], received ${rawColor}`); | ||
const [r, g, b, a] = rawColor; | ||
return [ | ||
clamp(rawColor[0], 0, 1), | ||
clamp(rawColor[1], 0, 1), | ||
clamp(rawColor[2], 0, 1), | ||
typeof rawColor[3] === 'number' ? clamp(rawColor[3], 0, 1) : 1, // apha | ||
clamp(r, 0, 1), | ||
clamp(g, 0, 1), | ||
clamp(b, 0, 1), | ||
typeof a === 'number' ? clamp(a, 0, 1) : 1, // alpha | ||
]; | ||
} | ||
if (typeof rawColor == 'object') { | ||
const c = { ...rawColor }; | ||
let alpha = 1; | ||
// grab alpha, ensure keys are lowercase | ||
for (const k of Object.keys(c)) { | ||
if (k === 'alpha') { | ||
alpha = clamp(c[k], 0, 1); | ||
} | ||
else { | ||
c[k.toLowerCase()] = c[k]; | ||
} | ||
} | ||
// RGB | ||
if ('r' in c && 'g' in c && 'b' in c) { | ||
return [ | ||
clamp(c.r || c.R, 0, 1), | ||
clamp(c.r || c.R, 0, 1), | ||
clamp(c.r || c.R, 0, 1), | ||
alpha, // alpha | ||
]; | ||
} | ||
// HSL | ||
if ('h' in c && 's' in c && 'l' in c) | ||
return hslTosRGB([c.h, clamp(c.s, 0, 1), clamp(c.l, 0, 1), alpha]); | ||
// HWB | ||
if ('h' in c && 'w' in c && 'b' in c) | ||
return hwbTosRGB([c.h, clamp(c.w, 0, 1), clamp(c.b, 0, 1), alpha]); | ||
// Oklab | ||
if ('l' in c && 'a' in c && 'b' in c) | ||
return oklabTosRGB([c.l, c.a, c.b, alpha]); | ||
// Oklch | ||
if ('l' in c && 'c' in c && 'h' in c) | ||
return oklchTosRGB([c.l, c.c, c.h, alpha]); | ||
// XYZ | ||
if ('x' in c && 'y' in c && 'z' in c) | ||
return linearRGBTosRGB(xyzToLinearRGB([c.x, c.y, c.z, alpha])); | ||
// unknown object | ||
throw unparsable; | ||
} | ||
// 0xff0000 (number) | ||
@@ -204,6 +172,6 @@ // !note: doesn’t support alpha | ||
// hex | ||
if (HEX_RE.test(strVal)) { | ||
const hex = strVal.replace('#', ''); | ||
if (HEX_RE.test(lc)) { | ||
const hex = lc.replace('#', ''); | ||
const rgb = [0, 0, 0, 1]; | ||
if (hex.length >= 6) { | ||
if (hex.length === 6 || hex.length === 8) { | ||
for (let n = 0; n < hex.length / 2; n++) { | ||
@@ -216,4 +184,3 @@ const start = n * 2; | ||
} | ||
// according to spec, any shortened hex should have characters doubled | ||
else { | ||
else if (hex.length === 3 || hex.length === 4) { | ||
for (let n = 0; n < hex.length; n++) { | ||
@@ -224,2 +191,5 @@ const value = hex.charAt(n); | ||
} | ||
else { | ||
throw new Error(`Hex value "${lc}" not a valid sRGB color`); | ||
} | ||
return rgb; | ||
@@ -246,8 +216,8 @@ } | ||
case 'srgb-linear': { | ||
const rgb = parseValueStr(valueStr, [255, 255, 255, 1]); | ||
return linearRGBTosRGB(rgb); | ||
const rgb = parseValueStr(valueStr); | ||
return linearRGBD65TosRGB(rgb); | ||
} | ||
case 'hsl': | ||
case 'hsla': { | ||
const [h, s, l, a] = parseValueStr(valueStr, [1, 1, 1, 1]); | ||
const [h, s, l, a] = parseValueStr(valueStr); | ||
return hslTosRGB([h, clamp(s, 0, 1), clamp(l, 0, 1), clamp(a, 0, 1)]); | ||
@@ -257,3 +227,3 @@ } | ||
case 'hwba': { | ||
const [h, w, b, a] = parseValueStr(valueStr, [1, 1, 1, 1]); | ||
const [h, w, b, a] = parseValueStr(valueStr); | ||
return hwbTosRGB([h, clamp(w, 0, 1), clamp(b, 0, 1), clamp(a, 0, 1)]); | ||
@@ -263,22 +233,61 @@ } | ||
case 'display-p3': { | ||
const [r, g, b, a] = parseValueStr(valueStr, [1, 1, 1, 1]); | ||
const [r, g, b, a] = parseValueStr(valueStr); | ||
return [clamp(r, 0, 1), clamp(g, 0, 1), clamp(b, 0, 1), clamp(a, 0, 1)]; | ||
} | ||
// case 'luv': { | ||
// const luv = parseValueStr(valueStr, [1, 1, 1, 1]); | ||
// const luv = parseValueStr(valueStr); | ||
// return luvTosRGB(luv); | ||
// } | ||
case 'oklab': { | ||
return oklabTosRGB(parseValueStr(valueStr, [1, 1, 1, 1])); | ||
return oklabTosRGB(parseValueStr(valueStr)); | ||
} | ||
case 'oklch': { | ||
return oklchTosRGB(parseValueStr(valueStr, [1, 1, 1, 1])); | ||
return oklchTosRGB(parseValueStr(valueStr)); | ||
} | ||
case 'xyz': | ||
case 'xyz-d65': { | ||
return linearRGBTosRGB(xyzToLinearRGB(parseValueStr(valueStr, [1, 1, 1, 1]))); | ||
return linearRGBD65TosRGB(xyzToLinearRGBD65(parseValueStr(valueStr))); | ||
} | ||
} | ||
} | ||
if (typeof rawColor == 'object') { | ||
const c = { ...rawColor }; | ||
let alpha = 1; | ||
// grab alpha, ensure keys are lowercase | ||
for (const k of Object.keys(c)) { | ||
if (k === 'alpha') { | ||
alpha = clamp(c[k], 0, 1); | ||
} | ||
else { | ||
c[k.toLowerCase()] = c[k]; | ||
} | ||
} | ||
// RGB | ||
if ('r' in c && 'g' in c && 'b' in c) { | ||
return [ | ||
clamp(c.r, 0, 1), | ||
clamp(c.g, 0, 1), | ||
clamp(c.b, 0, 1), | ||
alpha, // alpha | ||
]; | ||
} | ||
// HSL | ||
if ('h' in c && 's' in c && 'l' in c) | ||
return hslTosRGB([c.h, clamp(c.s, 0, 1), clamp(c.l, 0, 1), alpha]); | ||
// HWB | ||
if ('h' in c && 'w' in c && 'b' in c) | ||
return hwbTosRGB([c.h, clamp(c.w, 0, 1), clamp(c.b, 0, 1), alpha]); | ||
// Oklab | ||
if ('l' in c && 'a' in c && 'b' in c) | ||
return oklabTosRGB([c.l, c.a, c.b, alpha]); | ||
// Oklch | ||
if ('l' in c && 'c' in c && 'h' in c) | ||
return oklchTosRGB([c.l, c.c, c.h, alpha]); | ||
// XYZ | ||
if ('x' in c && 'y' in c && 'z' in c) | ||
return linearRGBD65TosRGB(xyzToLinearRGBD65([c.x, c.y, c.z, alpha])); | ||
// unknown object | ||
throw unparsable; | ||
} | ||
throw unparsable; | ||
} |
{ | ||
"name": "better-color-tools", | ||
"description": "Fast, minimal color conversion and tools for JS/Sass. Supports sRGB, Oklab, Oklch, Display P3, and more.", | ||
"version": "0.9.1", | ||
"version": "0.10.0", | ||
"author": { | ||
@@ -39,18 +39,18 @@ "name": "Drew Powers", | ||
"devDependencies": { | ||
"@changesets/cli": "^2.23.0", | ||
"@types/node": "^18.0.0", | ||
"@typescript-eslint/eslint-plugin": "^5.30.0", | ||
"@typescript-eslint/parser": "^5.30.0", | ||
"chai": "^4.3.6", | ||
"@changesets/cli": "^2.25.2", | ||
"@types/node": "^18.11.9", | ||
"@typescript-eslint/eslint-plugin": "^5.43.0", | ||
"@typescript-eslint/parser": "^5.43.0", | ||
"chai": "^4.3.7", | ||
"del-cli": "^4.0.1", | ||
"esbuild": "^0.14.47", | ||
"eslint": "^8.18.0", | ||
"esbuild": "^0.14.54", | ||
"eslint": "^8.28.0", | ||
"eslint-config-prettier": "^8.5.0", | ||
"eslint-plugin-prettier": "^4.1.0", | ||
"mocha": "^10.0.0", | ||
"eslint-plugin-prettier": "^4.2.1", | ||
"mocha": "^10.1.0", | ||
"npm-run-all": "^4.1.5", | ||
"prettier": "^2.7.1", | ||
"sass": "^1.53.0", | ||
"typescript": "^4.7.4" | ||
"sass": "^1.56.1", | ||
"typescript": "^4.9.3" | ||
} | ||
} |
@@ -84,7 +84,16 @@ # better-color-tools | ||
- CIELAB/CIELCh aren’t supported because Oklab/Oklch [are superior][oklab] | ||
- XYZ D50 isn’t supported because I didn’t feel like it | ||
- HSV is a great color space (not to be confused with HSL) and is on the roadmap but isn’t available right now | ||
For a comprehensive color conversion library, see [Culori]. | ||
**Adjust** | ||
To adjust a color via Oklch, append `.adjust()` along with the adjustments to make: | ||
```js | ||
better.from('#0060ff').adjust({ lightness: 0.5 }); // set lightness to 50% (absolute) | ||
better.from('#0060ff').adjust({ mode: 'relative', lightness: -0.1 }); // darken lightness by 10% | ||
``` | ||
You can adjust `lightness`, `chroma`, and `hue` altogether, and you can either operate in `relative` or `absolute` (default) mode. | ||
**P3** | ||
@@ -91,0 +100,0 @@ |
@@ -7,11 +7,11 @@ export type HSL = [number, number, number, number]; | ||
export type LMS = [number, number, number, number]; | ||
export type LinearRGB = [number, number, number, number]; | ||
export type LinearRGBD65 = [number, number, number, number]; | ||
export type Oklab = [number, number, number, number]; | ||
export type Oklch = [number, number, number, number]; | ||
export type sRGB = [number, number, number, number]; | ||
export type XYZ_D65 = [number, number, number, number]; | ||
export type Color = string | number | sRGB | LinearRGB | LMS | Oklab | Oklch; | ||
export type XYZ = [number, number, number, number]; | ||
export type Color = string | number | sRGB | LinearRGBD65 | LMS | Oklab | Oklch; | ||
import { clamp, degToRad, multiplyColorMatrix, radToDeg } from './utils.js'; | ||
import { LMS_TO_OKLAB, LMS_TO_LINEAR_RGB, LINEAR_RGB_TO_LMS, OKLAB_TO_LMS, LINEAR_RGB_TO_XYZ_D65, XYZ_D65_TO_LINEAR_RGB, findGamutIntersection } from './lib.js'; | ||
import { LMS_TO_OKLAB, LMS_TO_LINEAR_RGB, LINEAR_RGB_TO_LMS, OKLAB_TO_LMS, LINEAR_RGB_D65_TO_XYZ, XYZ_TO_LINEAR_RGB_D65, findGamutIntersection } from './lib.js'; | ||
@@ -29,2 +29,13 @@ // const D65_κ = 24389 / 27; | ||
/** sRGB transfer function (D65 illuminant) */ | ||
export function sRGBTransferFunction(value: number): number { | ||
const abs = Math.abs(value); | ||
return abs <= 0.0031308 ? value * 12.92 : 1.055 * Math.pow(abs, 1 / 2.4) - 0.055; | ||
} | ||
/** sRGB inverse transfer function (D65 illuminant) */ | ||
export function sRGBInverseTransferFunction(value: number): number { | ||
return Math.abs(value) <= 0.04045 ? value / 12.92 : ((Math.abs(value) + 0.055) / 1.055) ** 2.4; | ||
} | ||
/** HSL -> sRGB */ | ||
@@ -101,3 +112,3 @@ export function hslTosRGB(hsl: HSL): sRGB { | ||
if (L === 0) { | ||
return [0, 0, 0, lch[3]]; | ||
return [0, 0, 0, alpha]; | ||
} | ||
@@ -120,5 +131,6 @@ while (h < 0) h += 360; | ||
/** LMS -> Linear sRGB */ | ||
export function lmsToLinearRGB(lms: LMS): LinearRGB { | ||
const [r, g, b, a] = multiplyColorMatrix([lms[0] ** 3, lms[1] ** 3, lms[2] ** 3, lms[3]], LMS_TO_LINEAR_RGB); | ||
/** LMS -> Linear RGB D65 */ | ||
export function lmsToLinearRGBD65(lms: LMS): LinearRGBD65 { | ||
const [l, m, s, a] = lms; | ||
const [r, g, b] = multiplyColorMatrix([l ** 3, m ** 3, s ** 3, a], LMS_TO_LINEAR_RGB); | ||
return [ | ||
@@ -128,33 +140,31 @@ r, // r | ||
b, // b | ||
a, // a | ||
a, // alpha | ||
]; | ||
} | ||
/** Linear sRGB -> sRGB */ | ||
export function linearRGBTosRGB(rgb: LinearRGB, γ = 2.4): sRGB { | ||
const r = Math.abs(rgb[0]); | ||
const g = Math.abs(rgb[1]); | ||
const b = Math.abs(rgb[2]); | ||
/** Linear RGB D65 -> sRGB */ | ||
export function linearRGBD65TosRGB(rgb: LinearRGBD65): sRGB { | ||
const [r, g, b, a] = rgb; | ||
return [ | ||
r < 0.0031308 ? rgb[0] * 12.92 : 1.055 * Math.pow(r, 1 / γ) - 0.055, // r | ||
g < 0.0031308 ? rgb[1] * 12.92 : 1.055 * Math.pow(g, 1 / γ) - 0.055, // g | ||
b < 0.0031308 ? rgb[2] * 12.92 : 1.055 * Math.pow(b, 1 / γ) - 0.055, // b | ||
rgb[3], // alpha | ||
sRGBTransferFunction(r), // r | ||
sRGBTransferFunction(g), // g | ||
sRGBTransferFunction(b), // b | ||
a, // alpha | ||
]; | ||
} | ||
/** Linear sRGB -> LMS */ | ||
export function linearRGBToLMS(lrgb: LinearRGB): LMS { | ||
const lms = multiplyColorMatrix(lrgb, LINEAR_RGB_TO_LMS); | ||
/** Linear RGB D65 -> LMS */ | ||
export function linearRGBD65ToLMS(lrgb: LinearRGBD65): LMS { | ||
const [l, m, s, a] = multiplyColorMatrix(lrgb, LINEAR_RGB_TO_LMS); | ||
return [ | ||
Math.cbrt(lms[0]), // L | ||
Math.cbrt(lms[1]), // M | ||
Math.cbrt(lms[2]), // S | ||
lms[3], | ||
Math.cbrt(l), // L | ||
Math.cbrt(m), // M | ||
Math.cbrt(s), // S | ||
a, // alpha | ||
]; | ||
} | ||
/** Linear sRGB -> XYZ (D65) */ | ||
export function linearRGBToXYZ(rgb: LinearRGB): XYZ_D65 { | ||
return multiplyColorMatrix(rgb, LINEAR_RGB_TO_XYZ_D65); | ||
/** Linear RGB D65 -> XYZ */ | ||
export function linearRGBD65ToXYZ(rgb: LinearRGBD65): XYZ { | ||
return multiplyColorMatrix(rgb, LINEAR_RGB_D65_TO_XYZ); | ||
} | ||
@@ -164,3 +174,3 @@ | ||
// export function luvTosRGB(luv: LUV): sRGB { | ||
// return linearRGBTosRGB(xyzToLinearRGB(luvToXYZ(luv))); | ||
// return linearRGBD65TosRGB(xyzToLinearRGB(luvToXYZ(luv))); | ||
// } | ||
@@ -193,8 +203,8 @@ | ||
export function oklabTosRGB(oklab: Oklab): sRGB { | ||
const rgb = linearRGBTosRGB(lmsToLinearRGB(oklabToLMS(oklab))); | ||
const [R, G, B, alpha] = lmsToLinearRGBD65(oklabToLMS(oklab)); | ||
if (rgb[0] > 1.001 || rgb[0] < -0.001 || rgb[1] > 1.001 || rgb[1] < -0.001 || rgb[2] > 1.001 || rgb[2] < -0.001) { | ||
if (R > 1.001 || R < -0.001 || G > 1.001 || G < -0.001 || B > 1.001 || B < -0.001) { | ||
// “Preserve light, clamp Chroma” method from https://bottosson.github.io/posts/gamutclipping/ | ||
const ε = 0.00001; | ||
const [L, a, b, alpha] = oklab; | ||
const [L, a, b] = oklab; | ||
const C = Math.max(ε, Math.sqrt(a ** 2 + b ** 2)); | ||
@@ -206,4 +216,4 @@ const Lgamut = clamp(L, 0, 1); | ||
return linearRGBTosRGB( | ||
lmsToLinearRGB( | ||
return linearRGBD65TosRGB( | ||
lmsToLinearRGBD65( | ||
oklabToLMS([ | ||
@@ -219,3 +229,3 @@ Lgamut * (1 - t) + t * L, // L | ||
return rgb; | ||
return linearRGBD65TosRGB([R, G, B, alpha]); | ||
} | ||
@@ -228,18 +238,16 @@ | ||
/** sRGB -> Linear sRGB */ | ||
export function sRGBToLinearRGB(rgb: sRGB, γ = 2.4): LinearRGB { | ||
const r = Math.abs(rgb[0]); | ||
const g = Math.abs(rgb[1]); | ||
const b = Math.abs(rgb[2]); | ||
/** sRGB -> Linear RGB D65 */ | ||
export function sRGBToLinearRGBD65(rgb: sRGB): LinearRGBD65 { | ||
const [r, g, b, a] = rgb; | ||
return [ | ||
r < 0.04045 ? rgb[0] / 12.92 : ((r + 0.055) / 1.055) ** γ, // r | ||
g < 0.04045 ? rgb[1] / 12.92 : ((g + 0.055) / 1.055) ** γ, // g | ||
b < 0.04045 ? rgb[2] / 12.92 : ((b + 0.055) / 1.055) ** γ, // b | ||
rgb[3], // alpha | ||
sRGBInverseTransferFunction(r), // r | ||
sRGBInverseTransferFunction(g), // g | ||
sRGBInverseTransferFunction(b), // b | ||
a, // alpha | ||
]; | ||
} | ||
/** Linear sRGB -> Luv */ | ||
/** Linear RGB D65 -> Luv */ | ||
// export function sRGBToLuv(rgb: LinearRGB): LUV { | ||
// return xyzToLuv(linearRGBToXYZ(sRGBToLinearRGB(rgb))); | ||
// return xyzToLuv(linearRGBD65ToXYZ(sRGBToLinearRGB(rgb))); | ||
// } | ||
@@ -249,3 +257,3 @@ | ||
export function sRGBToOklab(rgb: sRGB): Oklab { | ||
return lmsToOklab(linearRGBToLMS(sRGBToLinearRGB(rgb))); | ||
return lmsToOklab(linearRGBD65ToLMS(sRGBToLinearRGBD65(rgb))); | ||
} | ||
@@ -258,5 +266,5 @@ | ||
/** XYZ (D65) -> Linear sRGB */ | ||
export function xyzToLinearRGB(xyz: XYZ_D65): sRGB { | ||
return multiplyColorMatrix(xyz, XYZ_D65_TO_LINEAR_RGB); | ||
/** XYZ -> Linear RGB D65 */ | ||
export function xyzToLinearRGBD65(xyz: XYZ): sRGB { | ||
return multiplyColorMatrix(xyz, XYZ_TO_LINEAR_RGB_D65); | ||
} | ||
@@ -263,0 +271,0 @@ |
@@ -1,5 +0,7 @@ | ||
export type { ColorMatrix, HSL, HWB, LMS, LinearRGB, LUV, Oklab, Oklch, sRGB, XYZ_D65 } from './colorspace.js'; | ||
export type { AdjustOptions } from './adjust.js'; | ||
export type { ColorMatrix, HSL, HWB, LMS, LinearRGBD65, LUV, Oklab, Oklch, sRGB, XYZ } from './colorspace.js'; | ||
export type { MixColorSpace } from './mix.js'; | ||
export type { ColorOutput } from './parse.js'; | ||
export { default as adjust } from './adjust.js'; | ||
export { contrastRatio, darken, lighten, lightOrDark, lightness, luminance } from './luminance.js'; | ||
@@ -10,2 +12,3 @@ export { mix } from './mix.js'; | ||
import adjust from './adjust.js'; | ||
import { contrastRatio, darken, lighten, lightOrDark, lightness, luminance } from './luminance.js'; | ||
@@ -16,2 +19,3 @@ import { mix } from './mix.js'; | ||
export default { | ||
adjust, | ||
contrastRatio, | ||
@@ -18,0 +22,0 @@ darken, |
import type { ColorMatrix } from './index'; | ||
import { lmsToLinearRGB, oklabToLMS } from './colorspace.js'; | ||
import { lmsToLinearRGBD65, oklabToLMS } from './colorspace.js'; | ||
// https://observablehq.com/@danburzo/color-matrix-calculator | ||
export const LINEAR_RGB_TO_XYZ_D65: ColorMatrix = [ | ||
export const LINEAR_RGB_D65_TO_XYZ: ColorMatrix = [ | ||
[0.4123907992659593, 0.357584339383878, 0.1804807884018343], | ||
[0.2126390058715102, 0.715168678767756, 0.0721923153607337], | ||
[0.0193308187155918, 0.11919477979462, 0.9505321522496607], | ||
[0.0193308187155918, 0.119194779794626, 0.9505321522496607], | ||
]; | ||
export const XYZ_D65_TO_LINEAR_RGB: ColorMatrix = [ | ||
export const XYZ_TO_LINEAR_RGB_D65: ColorMatrix = [ | ||
[3.2409699419045221, -1.5373831775700939, -0.4986107602930034], | ||
@@ -142,5 +142,5 @@ [-0.9692436362808793, 1.8759675015077202, 0.0415550574071756], | ||
// Convert to linear sRGB to find the first point where at least one of r,g or b >= 1: | ||
const rgb_at_max = lmsToLinearRGB(oklabToLMS([1, S_cusp * a, S_cusp * b, 1])); | ||
const L_cusp = Math.cbrt(1 / Math.max(rgb_at_max[0], rgb_at_max[1], rgb_at_max[3])); | ||
// Convert to linear RGB (D65) to find the first point where at least one of r,g or b >= 1: | ||
const [R, G, B] = lmsToLinearRGBD65(oklabToLMS([1, S_cusp * a, S_cusp * b, 1])); | ||
const L_cusp = Math.cbrt(1 / Math.max(R, G, B)); | ||
const C_cusp = L_cusp * S_cusp; | ||
@@ -147,0 +147,0 @@ |
@@ -1,5 +0,5 @@ | ||
import { Color, linearRGBToLMS, lmsToLinearRGB, sRGB } from './colorspace.js'; | ||
import { Color, linearRGBD65ToLMS, lmsToLinearRGBD65, sRGB } from './colorspace.js'; | ||
import type { ColorOutput } from './parse.js'; | ||
import { linearRGBTosRGB, sRGBToOklab, sRGBToOklch, sRGBToLinearRGB, oklabTosRGB, oklchTosRGB } from './colorspace.js'; | ||
import { linearRGBD65TosRGB, sRGBToOklab, sRGBToOklch, sRGBToLinearRGBD65, oklabTosRGB, oklchTosRGB } from './colorspace.js'; | ||
import { from } from './parse.js'; | ||
@@ -29,4 +29,4 @@ import { clamp } from './utils.js'; | ||
oklab: sRGBToOklab, | ||
lms: (c) => sRGBToLinearRGB(linearRGBToLMS(c)), | ||
linearRGB: sRGBToLinearRGB, | ||
lms: (c) => sRGBToLinearRGBD65(linearRGBD65ToLMS(c)), | ||
linearRGB: sRGBToLinearRGBD65, | ||
sRGB: (c) => c, | ||
@@ -38,4 +38,4 @@ }; | ||
oklab: oklabTosRGB, | ||
lms: (c) => lmsToLinearRGB(linearRGBTosRGB(c)), | ||
linearRGB: linearRGBTosRGB, | ||
lms: (c) => lmsToLinearRGBD65(linearRGBD65TosRGB(c)), | ||
linearRGB: linearRGBD65TosRGB, | ||
sRGB: (c) => c, | ||
@@ -47,7 +47,7 @@ }; | ||
const rgb1 = from(color1).rgbVal; | ||
const rgb2 = from(color2).rgbVal; | ||
const [r1, g1, b1, alpha1] = from(color1).rgbVal; | ||
const [r2, g2, b2, alpha2] = from(color2).rgbVal; | ||
// Oklch fix: if one color is neutral, use Oklab to prevent hue shifting | ||
if (colorSpace === 'oklch' && ((rgb1[0] === rgb1[1] && rgb1[1] === rgb1[2]) || (rgb2[0] === rgb2[1] && rgb2[1] === rgb2[2]))) { | ||
if (colorSpace === 'oklch' && ((r1 === g1 && g1 === b1) || (r2 === g2 && g2 === b2))) { | ||
converter = converters.oklab; | ||
@@ -58,4 +58,4 @@ deconverter = deconverters.oklab; | ||
// convert color into mix colorspace | ||
let [x1, y1, z1, a1] = converter(rgb1); | ||
let [x2, y2, z2, a2] = converter(rgb2); | ||
let [x1, y1, z1, a1] = converter([r1, g1, b1, alpha1]); | ||
let [x2, y2, z2, a2] = converter([r2, g2, b2, alpha2]); | ||
@@ -62,0 +62,0 @@ // Oklch: take shortest hue distance (e.g. 0 and 359 should only be 1 degree apart, not 359) |
166
src/parse.ts
@@ -1,4 +0,6 @@ | ||
import type { Color, LinearRGB, Oklab, Oklch, sRGB, XYZ_D65 } from './colorspace.js'; | ||
import type { AdjustOptions } from './adjust.js'; | ||
import type { Color, LinearRGBD65, Oklab, Oklch, sRGB, XYZ } from './colorspace.js'; | ||
import { hslTosRGB, hwbTosRGB, linearRGBTosRGB, linearRGBToXYZ, oklabTosRGB, oklchTosRGB, sRGBToLinearRGB, sRGBToOklab, sRGBToOklch, xyzToLinearRGB } from './colorspace.js'; | ||
import adjust from './adjust.js'; | ||
import { hslTosRGB, hwbTosRGB, linearRGBD65TosRGB, linearRGBD65ToXYZ, oklabTosRGB, oklchTosRGB, sRGBToLinearRGBD65, sRGBToOklab, sRGBToOklch, xyzToLinearRGBD65 } from './colorspace.js'; | ||
import cssNames from './css-names.js'; | ||
@@ -23,3 +25,3 @@ import { clamp, colorFn, leftPad } from './utils.js'; | ||
/** [R, G, B, alpha] */ | ||
linearRGBVal: LinearRGB; | ||
linearRGBVal: LinearRGBD65; | ||
/** `color(luv 0 0 0/1)` */ | ||
@@ -41,6 +43,6 @@ // luv: string; | ||
oklchVal: Oklch; | ||
/** `color(xyz 0 0 0/1)` (2°, D65 whitepoint) */ | ||
/** `color(xyz 0 0 0/1)` */ | ||
xyz: string; | ||
/** [X, Y, Z, alpha] (2°, D65 whitespace) */ | ||
xyzVal: XYZ_D65; | ||
/** [X, Y, Z, alpha] */ | ||
xyzVal: XYZ; | ||
toString(): string; // JS helper | ||
@@ -51,3 +53,3 @@ } | ||
const FLOAT_RE = /-?[0-9.]+%?/g; | ||
const HEX_RE = /^#?[0-9a-f]{3,8}$/i; | ||
const HEX_RE = /^#[0-9a-f]{3,8}$/i; | ||
const RGB_RANGE = 16 ** 6; | ||
@@ -76,14 +78,16 @@ const R_FACTOR = 16 ** 4; // base 16, starting after 4 digits (GGBB) | ||
get hex(): string { | ||
const [r, g, b, a] = color; | ||
let hexString = '#'; | ||
hexString += leftPad(Math.round(clamp(color[0] * 255, 0, 255)).toString(16), 2); // r | ||
hexString += leftPad(Math.round(clamp(color[1] * 255, 0, 255)).toString(16), 2); // g | ||
hexString += leftPad(Math.round(clamp(color[2] * 255, 0, 255)).toString(16), 2); // b | ||
if (color[3] < 1) hexString += leftPad(Math.round(color[3] * 255).toString(16), 2); // a | ||
hexString += leftPad(Math.round(clamp(r * 255, 0, 255)).toString(16), 2); // r | ||
hexString += leftPad(Math.round(clamp(g * 255, 0, 255)).toString(16), 2); // g | ||
hexString += leftPad(Math.round(clamp(b * 255, 0, 255)).toString(16), 2); // b | ||
if (color[3] < 1) hexString += leftPad(Math.round(a * 255).toString(16), 2); // a | ||
return hexString; | ||
}, | ||
get hexVal(): number { | ||
if (color[3] < 1) console.warn(`hexVal converted a semi-transparent color (${color[3] * 100}%) to fully opaque`); // eslint-disable-line no-console | ||
const r = Math.round(clamp(color[0] * 255, 0, 255)); | ||
const g = Math.round(clamp(color[1] * 255, 0, 255)); | ||
const b = Math.round(clamp(color[2] * 255, 0, 255)); | ||
let [r, g, b, a] = color; | ||
if (a < 1) console.warn(`hexVal converted a semi-transparent color (${a * 100}%) to fully opaque`); // eslint-disable-line no-console | ||
r = Math.round(clamp(r * 255, 0, 255)); | ||
g = Math.round(clamp(g * 255, 0, 255)); | ||
b = Math.round(clamp(b * 255, 0, 255)); | ||
return r * R_FACTOR + g * G_FACTOR + b; | ||
@@ -106,6 +110,6 @@ }, | ||
get linearRGB(): string { | ||
return colorFn('srgb-linear', sRGBToLinearRGB(color)); | ||
return colorFn('srgb-linear', sRGBToLinearRGBD65(color)); | ||
}, | ||
get linearRGBVal(): LinearRGB { | ||
return sRGBToLinearRGB(color); | ||
get linearRGBVal(): LinearRGBD65 { | ||
return sRGBToLinearRGBD65(color); | ||
}, | ||
@@ -129,7 +133,10 @@ get p3(): string { | ||
get xyz(): string { | ||
return colorFn('xyz-d65', linearRGBToXYZ(sRGBToLinearRGB(color))); | ||
return colorFn('xyz-d65', linearRGBD65ToXYZ(sRGBToLinearRGBD65(color))); | ||
}, | ||
get xyzVal(): XYZ_D65 { | ||
return linearRGBToXYZ(sRGBToLinearRGB(color)); | ||
get xyzVal(): XYZ { | ||
return linearRGBD65ToXYZ(sRGBToLinearRGBD65(color)); | ||
}, | ||
adjust(options: AdjustOptions): ColorOutput { | ||
return from(adjust(color, options)); | ||
}, | ||
}; | ||
@@ -159,3 +166,3 @@ | ||
/** only grabs numbers from a color string (ignores spaces, commas, slashes, etc.) */ | ||
function parseValueStr(colorStr: string, normalize: number[]): [number, number, number, number] { | ||
function parseValueStr(colorStr: string, normalize?: number[]): [number, number, number, number] { | ||
const matches = colorStr.match(FLOAT_RE); | ||
@@ -168,3 +175,3 @@ if (!matches) throw new Error(`Unexpected color format: ${colorStr}`); | ||
// unbounded | ||
else if (normalize[n] === Infinity || normalize[n] === 0 || normalize[n] === 1) values[n] = parseFloat(value); | ||
else if (!normalize || normalize[n] === Infinity || normalize[n] === 1) values[n] = parseFloat(value); | ||
// bounded | ||
@@ -178,52 +185,19 @@ else values[n] = parseFloat(value) / normalize[n]; | ||
export function parse(rawColor: Color): sRGB { | ||
const unparsable = new Error(`Unable to parse color "${rawColor}"`); | ||
const unparsable = new Error(`Unable to parse color ${JSON.stringify(rawColor)}`); | ||
if (rawColor == undefined || rawColor == null) throw unparsable; | ||
if (rawColor == undefined || rawColor == null || typeof rawColor === 'boolean') throw unparsable; | ||
// [R, G, B] or [R, G, B, A] | ||
if (Array.isArray(rawColor)) { | ||
if (typeof rawColor[0] != 'number' || typeof rawColor[1] != 'number' || typeof rawColor[2] != 'number') throw new Error(`Color array must be numbers, received ${rawColor}`); | ||
if (rawColor.some((v) => typeof v !== 'number')) throw new Error(`Color array must be numbers, received ${rawColor}`); | ||
if (rawColor.length < 3 || rawColor.length > 4) throw new Error(`Expected [R, G, B, A?], received ${rawColor}`); | ||
const [r, g, b, a] = rawColor; | ||
return [ | ||
clamp(rawColor[0], 0, 1), // r | ||
clamp(rawColor[1], 0, 1), // g | ||
clamp(rawColor[2], 0, 1), // b | ||
typeof rawColor[3] === 'number' ? clamp(rawColor[3], 0, 1) : 1, // apha | ||
clamp(r, 0, 1), // r | ||
clamp(g, 0, 1), // g | ||
clamp(b, 0, 1), // b | ||
typeof a === 'number' ? clamp(a, 0, 1) : 1, // alpha | ||
]; | ||
} | ||
if (typeof rawColor == 'object') { | ||
const c = { ...(rawColor as Record<string, number>) }; | ||
let alpha = 1; | ||
// grab alpha, ensure keys are lowercase | ||
for (const k of Object.keys(c)) { | ||
if (k === 'alpha') { | ||
alpha = clamp(c[k], 0, 1); | ||
} else { | ||
c[k.toLowerCase()] = c[k]; | ||
} | ||
} | ||
// RGB | ||
if ('r' in c && 'g' in c && 'b' in c) { | ||
return [ | ||
clamp(c.r || c.R, 0, 1), // r | ||
clamp(c.r || c.R, 0, 1), // g | ||
clamp(c.r || c.R, 0, 1), // b | ||
alpha, // alpha | ||
]; | ||
} | ||
// HSL | ||
if ('h' in c && 's' in c && 'l' in c) return hslTosRGB([c.h, clamp(c.s, 0, 1), clamp(c.l, 0, 1), alpha]); | ||
// HWB | ||
if ('h' in c && 'w' in c && 'b' in c) return hwbTosRGB([c.h, clamp(c.w, 0, 1), clamp(c.b, 0, 1), alpha]); | ||
// Oklab | ||
if ('l' in c && 'a' in c && 'b' in c) return oklabTosRGB([c.l, c.a, c.b, alpha]); | ||
// Oklch | ||
if ('l' in c && 'c' in c && 'h' in c) return oklchTosRGB([c.l, c.c, c.h, alpha]); | ||
// XYZ | ||
if ('x' in c && 'y' in c && 'z' in c) return linearRGBTosRGB(xyzToLinearRGB([c.x, c.y, c.z, alpha])); | ||
// unknown object | ||
throw unparsable; | ||
} | ||
// 0xff0000 (number) | ||
@@ -247,6 +221,6 @@ // !note: doesn’t support alpha | ||
// hex | ||
if (HEX_RE.test(strVal)) { | ||
const hex = strVal.replace('#', ''); | ||
if (HEX_RE.test(lc)) { | ||
const hex = lc.replace('#', ''); | ||
const rgb: sRGB = [0, 0, 0, 1]; | ||
if (hex.length >= 6) { | ||
if (hex.length === 6 || hex.length === 8) { | ||
for (let n = 0; n < hex.length / 2; n++) { | ||
@@ -258,5 +232,3 @@ const start = n * 2; | ||
} | ||
} | ||
// according to spec, any shortened hex should have characters doubled | ||
else { | ||
} else if (hex.length === 3 || hex.length === 4) { | ||
for (let n = 0; n < hex.length; n++) { | ||
@@ -266,2 +238,4 @@ const value = hex.charAt(n); | ||
} | ||
} else { | ||
throw new Error(`Hex value "${lc}" not a valid sRGB color`); | ||
} | ||
@@ -290,8 +264,8 @@ return rgb; | ||
case 'srgb-linear': { | ||
const rgb = parseValueStr(valueStr, [255, 255, 255, 1]); | ||
return linearRGBTosRGB(rgb); | ||
const rgb = parseValueStr(valueStr); | ||
return linearRGBD65TosRGB(rgb); | ||
} | ||
case 'hsl': | ||
case 'hsla': { | ||
const [h, s, l, a] = parseValueStr(valueStr, [1, 1, 1, 1]); | ||
const [h, s, l, a] = parseValueStr(valueStr); | ||
return hslTosRGB([h, clamp(s, 0, 1), clamp(l, 0, 1), clamp(a, 0, 1)]); | ||
@@ -301,3 +275,3 @@ } | ||
case 'hwba': { | ||
const [h, w, b, a] = parseValueStr(valueStr, [1, 1, 1, 1]); | ||
const [h, w, b, a] = parseValueStr(valueStr); | ||
return hwbTosRGB([h, clamp(w, 0, 1), clamp(b, 0, 1), clamp(a, 0, 1)]); | ||
@@ -307,18 +281,18 @@ } | ||
case 'display-p3': { | ||
const [r, g, b, a] = parseValueStr(valueStr, [1, 1, 1, 1]); | ||
const [r, g, b, a] = parseValueStr(valueStr); | ||
return [clamp(r, 0, 1), clamp(g, 0, 1), clamp(b, 0, 1), clamp(a, 0, 1)]; | ||
} | ||
// case 'luv': { | ||
// const luv = parseValueStr(valueStr, [1, 1, 1, 1]); | ||
// const luv = parseValueStr(valueStr); | ||
// return luvTosRGB(luv); | ||
// } | ||
case 'oklab': { | ||
return oklabTosRGB(parseValueStr(valueStr, [1, 1, 1, 1])); | ||
return oklabTosRGB(parseValueStr(valueStr)); | ||
} | ||
case 'oklch': { | ||
return oklchTosRGB(parseValueStr(valueStr, [1, 1, 1, 1])); | ||
return oklchTosRGB(parseValueStr(valueStr)); | ||
} | ||
case 'xyz': | ||
case 'xyz-d65': { | ||
return linearRGBTosRGB(xyzToLinearRGB(parseValueStr(valueStr, [1, 1, 1, 1]))); | ||
return linearRGBD65TosRGB(xyzToLinearRGBD65(parseValueStr(valueStr))); | ||
} | ||
@@ -328,3 +302,37 @@ } | ||
if (typeof rawColor == 'object') { | ||
const c = { ...(rawColor as Record<string, number>) }; | ||
let alpha = 1; | ||
// grab alpha, ensure keys are lowercase | ||
for (const k of Object.keys(c)) { | ||
if (k === 'alpha') { | ||
alpha = clamp(c[k], 0, 1); | ||
} else { | ||
c[k.toLowerCase()] = c[k]; | ||
} | ||
} | ||
// RGB | ||
if ('r' in c && 'g' in c && 'b' in c) { | ||
return [ | ||
clamp(c.r, 0, 1), // r | ||
clamp(c.g, 0, 1), // g | ||
clamp(c.b, 0, 1), // b | ||
alpha, // alpha | ||
]; | ||
} | ||
// HSL | ||
if ('h' in c && 's' in c && 'l' in c) return hslTosRGB([c.h, clamp(c.s, 0, 1), clamp(c.l, 0, 1), alpha]); | ||
// HWB | ||
if ('h' in c && 'w' in c && 'b' in c) return hwbTosRGB([c.h, clamp(c.w, 0, 1), clamp(c.b, 0, 1), alpha]); | ||
// Oklab | ||
if ('l' in c && 'a' in c && 'b' in c) return oklabTosRGB([c.l, c.a, c.b, alpha]); | ||
// Oklch | ||
if ('l' in c && 'c' in c && 'h' in c) return oklchTosRGB([c.l, c.c, c.h, alpha]); | ||
// XYZ | ||
if ('x' in c && 'y' in c && 'z' in c) return linearRGBD65TosRGB(xyzToLinearRGBD65([c.x, c.y, c.z, alpha])); | ||
// unknown object | ||
throw unparsable; | ||
} | ||
throw unparsable; | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
130243
36
2485
282