better-color-tools
Advanced tools
Comparing version 0.8.1 to 0.9.0
# better-color-tools | ||
## 0.9.0 | ||
### Minor Changes | ||
- 286d114: Add WCAG 2.1 contrast ratios | ||
- 286d114: Add lightOrDark utility | ||
- a5fc64b: Allow object notation inputs | ||
## 0.8.1 | ||
@@ -4,0 +12,0 @@ |
@@ -28,3 +28,3 @@ export declare type HSL = [number, number, number, number]; | ||
/** Linear sRGB -> sRGB */ | ||
export declare function linearRGBTosRGB(rgb: LinearRGB): sRGB; | ||
export declare function linearRGBTosRGB(rgb: LinearRGB, γ?: number): sRGB; | ||
/** Linear sRGB -> LMS */ | ||
@@ -43,3 +43,3 @@ export declare function linearRGBToLMS(lrgb: LinearRGB): LMS; | ||
/** sRGB -> Linear sRGB */ | ||
export declare function sRGBToLinearRGB(rgb: sRGB): LinearRGB; | ||
export declare function sRGBToLinearRGB(rgb: sRGB, γ?: number): LinearRGB; | ||
/** Linear sRGB -> Luv */ | ||
@@ -46,0 +46,0 @@ /** sRGB -> Oklab */ |
@@ -102,3 +102,3 @@ import { clamp, degToRad, multiplyColorMatrix, radToDeg } from './utils.js'; | ||
/** Linear sRGB -> sRGB */ | ||
export function linearRGBTosRGB(rgb) { | ||
export function linearRGBTosRGB(rgb, γ = 2.4) { | ||
const r = Math.abs(rgb[0]); | ||
@@ -108,5 +108,5 @@ const g = Math.abs(rgb[1]); | ||
return [ | ||
r < 0.0031308 ? rgb[0] * 12.92 : 1.055 * Math.pow(r, 1 / 2.4) - 0.055, | ||
g < 0.0031308 ? rgb[1] * 12.92 : 1.055 * Math.pow(g, 1 / 2.4) - 0.055, | ||
b < 0.0031308 ? rgb[2] * 12.92 : 1.055 * Math.pow(b, 1 / 2.4) - 0.055, | ||
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 | ||
@@ -178,3 +178,3 @@ ]; | ||
/** sRGB -> Linear sRGB */ | ||
export function sRGBToLinearRGB(rgb) { | ||
export function sRGBToLinearRGB(rgb, γ = 2.4) { | ||
const r = Math.abs(rgb[0]); | ||
@@ -184,5 +184,5 @@ const g = Math.abs(rgb[1]); | ||
return [ | ||
r < 0.04045 ? rgb[0] / 12.92 : ((r + 0.055) / 1.055) ** 2.4, | ||
g < 0.04045 ? rgb[1] / 12.92 : ((g + 0.055) / 1.055) ** 2.4, | ||
b < 0.04045 ? rgb[2] / 12.92 : ((b + 0.055) / 1.055) ** 2.4, | ||
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 | ||
@@ -189,0 +189,0 @@ ]; |
export type { ColorMatrix, HSL, HWB, LMS, LinearRGB, LUV, Oklab, Oklch, sRGB, XYZ_D65 } from './colorspace.js'; | ||
export type { LightenDarkenColorSpace } from './lighten-darken.js'; | ||
export type { MixColorSpace } from './mix.js'; | ||
export type { ColorOutput } from './parse.js'; | ||
export { darken, lighten } from './lighten-darken.js'; | ||
export { contrastRatio, darken, lighten, lightOrDark, lightness, luminance } from './luminance.js'; | ||
export { mix } from './mix.js'; | ||
export { from, lightness } from './parse.js'; | ||
export { from } from './parse.js'; | ||
export { clamp, colorFn, degToRad, leftPad, multiplyColorMatrix, radToDeg, round } from './utils.js'; | ||
import { darken, lighten } from './lighten-darken.js'; | ||
import { contrastRatio, darken, lighten, lightOrDark, lightness, luminance } from './luminance.js'; | ||
import { mix } from './mix.js'; | ||
import { from, lightness } from './parse.js'; | ||
import { from } from './parse.js'; | ||
declare const _default: { | ||
contrastRatio: typeof contrastRatio; | ||
darken: typeof darken; | ||
@@ -17,4 +17,6 @@ from: typeof from; | ||
lightness: typeof lightness; | ||
lightOrDark: typeof lightOrDark; | ||
luminance: typeof luminance; | ||
mix: typeof mix; | ||
}; | ||
export default _default; |
@@ -1,9 +0,10 @@ | ||
export { darken, lighten } from './lighten-darken.js'; | ||
export { contrastRatio, darken, lighten, lightOrDark, lightness, luminance } from './luminance.js'; | ||
export { mix } from './mix.js'; | ||
export { from, lightness } from './parse.js'; | ||
export { from } from './parse.js'; | ||
export { clamp, colorFn, degToRad, leftPad, multiplyColorMatrix, radToDeg, round } from './utils.js'; | ||
import { darken, lighten } from './lighten-darken.js'; | ||
import { contrastRatio, darken, lighten, lightOrDark, lightness, luminance } from './luminance.js'; | ||
import { mix } from './mix.js'; | ||
import { from, lightness } from './parse.js'; | ||
import { from } from './parse.js'; | ||
export default { | ||
contrastRatio, | ||
darken, | ||
@@ -13,3 +14,5 @@ from, | ||
lightness, | ||
lightOrDark, | ||
luminance, | ||
mix, | ||
}; |
@@ -1,1 +0,1 @@ | ||
function v(e,t=2){let o=e;for(;o.length<t;)o=`0${o}`;return o}function K(e){return e*(Math.PI/180)}function H(e){return e*(180/Math.PI)}function l(e,t,o){return Math.min(Math.max(e,t),o)}function R(e,t){let[o,r,a,n]=t,s=n<1?`/${g(n,5)}`:"";switch(e){case"rgb":case"rgba":return n<1?`rgba(${Math.round(o*255)}, ${Math.round(r*255)}, ${Math.round(a*255)}, ${g(n,5)})`:`rgb(${Math.round(o*255)}, ${Math.round(r*255)}, ${Math.round(a*255)})`;case"oklab":case"oklch":return`${e}(${g(o*100,6)}% ${g(r,6)} ${g(a,6)}${s})`;default:return`color(${e} ${g(o,6)} ${g(r,6)} ${g(a,6)}${s})`}}function _(e,t){let o=[...e];for(let r=0;r<t.length;r++){let a=0;for(let n=0;n<t[r].length;n++)a+=e[n]*t[r][n];o[r]=a}return o}function g(e,t=2){let o=10**t;return Math.round(e*o)/o}var a0=[[.4123907992659593,.357584339383878,.1804807884018343],[.2126390058715102,.715168678767756,.0721923153607337],[.0193308187155918,.11919477979462,.9505321522496607]],s0=[[3.240969941904522,-1.537383177570094,-.4986107602930034],[-.9692436362808793,1.8759675015077202,.0415550574071756],[.0556300796969937,-.2039769588889766,1.0569715142428782]],f0=[[.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]],c0=[[.4122214708,.5363325363,.0514459929],[.2119034982,.6806995451,.1073969566],[.0883024619,.2817188376,.6299787005]],l0=[[1,.39633779217376774,.2158037580607588],[1,-.10556134232365633,-.0638541747717059],[1,-.08948418209496574,-1.2914855378640917]];function k0(e,t){let o=[1/0,1/0,1/0,1/0,1/0],r=1/0,a=1/0,n=1/0;-1.88170328*e-.80936493*t>1?(o=[1.19086277,1.76576728,.59662641,.75515197,.56771245],r=4.0767416621,a=-3.3077115913,n=.2309699292):1.81444104*e-1.19445276*t>1?(o=[.73956515,-.45954404,.08285427,.1254107,.14503204],r=-1.2684380046,a=2.6097574011,n=-.3413193965):(o=[1.35733652,-.00915799,-1.1513021,-.50559606,.00692167],r=-.0041960863,a=-.7034186147,n=1.707614701);let s=o[0]+o[1]*e+o[2]*t+o[3]*e*e+o[4]*e*t,f=.3963377774*e+.2158037573*t,c=-.1055613458*e-.0638541728*t,u=-.0894841775*e-1.291485548*t;{let d=1+s*f,h=1+s*c,b=1+s*u,$=d**3,I=h**3,k=b**3,T=3*f*d**2,w=3*c*h**2,L=3*u*b**2,m=6*f**2*d,i=6*c**2*h,p=6*u**2*b,O=r*$+a*I+n*k,N=r*T+a*w+n*L,F=r*m+a*i+n*p;s=s-O*N/(N*N-.5*O*F)}return s}function y0(e,t){let o=k0(e,t),r=S(z([1,o*e,o*t,1])),a=Math.cbrt(1/Math.max(r[0],r[1],r[3])),n=a*o;return{L:a,C:n}}function i0(e,t,o,r,a){let n=y0(e,t);if((o-a)*n.C-(n.L-a)*r<=0)return n.C*a/(r*n.L+n.C*(a-o));let s=n.C*(a-1)/(r*(n.L-1)+n.C*(a-o)),f=o-a,c=r,u=.3963377774*e+.2158037573*t,d=-.1055613458*e-.0638541728*t,h=-.0894841775*e-1.291485548*t,b=f+c*u,$=f+c*d,I=f+c*h,k=a*(1-s)+s*o,T=s*r,w=k+T*u,L=k+T*d,m=k+T*h,i=[[w**3,L**3,m**3],[3*b*w**2,3*$*L**2,3*I*m**2],[6*b**2*w,6*$**2*L,6*I**2*m]],p=x[0][0]*i[0][0]+x[0][1]*i[0][1]+x[0][2]*i[0][2]-1,O=x[0][0]*i[1][0]+x[0][1]*i[1][1]+x[0][2]*i[1][2],N=x[0][0]*i[2][0]+x[0][1]*i[2][1]+x[0][2]*i[2][2],F=O/(O*O-.5*p*N),h0=F>=0?-p*F:1/0,t0=x[1][0]*i[0][0]+x[1][1]*i[0][1]+x[1][2]*i[0][2]-1,Z=x[1][0]*i[1][0]+x[1][1]*i[1][1]+x[1][2]*i[1][2],b0=x[1][0]*i[2][0]+x[1][1]*i[2][1]+x[1][2]*i[2][2],r0=Z/(Z*Z-.5*t0*b0),p0=r0>=0?-t0*r0:1/0,n0=x[2][0]*i[0][0]+x[2][1]*i[0][1]+x[2][2]*i[0][2]-1,P=x[2][0]*i[1][0]+x[2][1]*i[1][1]+x[2][2]*i[1][2],m0=x[2][0]*i[2][0]+x[2][1]*i[2][1]+x[2][2]*i[2][2],o0=P/(P*P-.5*n0*m0),g0=o0>=0?-n0*o0:1/0;return s+Math.min(h0,p0,g0)}function U(e){let[t,o,r,a]=e;t=Math.abs(t%360);let n=o*(1-Math.abs(2*r-1)),s=n*(1-Math.abs(t/60%2-1)),f=0,c=0,u=0;0<=t&&t<60?(f=n,c=s):60<=t&&t<120?(f=s,c=n):120<=t&&t<180?(c=n,u=s):180<=t&&t<240?(c=s,u=n):240<=t&&t<300?(f=s,u=n):300<=t&&t<360&&(f=n,u=s);let d=r-n/2;return[f+d,c+d,u+d,a]}function x0(e){let[t,o,r,a]=e;if(o+r>=1){let s=o/(o+r);return[s,s,s,a]}let n=U([t,100,50,a]);for(let s=0;s<3;s++)n[s]*=1-o-r,n[s]+=o;return n}function R0(e,t=2e-4){let[o,r,a,n]=e,s=Math.abs(r)<t&&Math.abs(a)<t?0:H(Math.atan2(a,r));for(;s<0;)s+=360;for(;s>=360;)s-=360;return[o,Math.sqrt(r**2+a**2),s,n]}function _0(e){let[t,o,r,a]=e;if(t===0)return[0,0,0,e[3]];for(;r<0;)r+=360;for(;r>=360;)r-=360;let n=K(r);return[t,Math.cos(n)*o,Math.sin(n)*o,a]}function M0(e){return _(e,f0)}function S(e){let[t,o,r,a]=_([e[0]**3,e[1]**3,e[2]**3,e[3]],x);return[t,o,r,a]}function M(e){let t=Math.abs(e[0]),o=Math.abs(e[1]),r=Math.abs(e[2]);return[t<.0031308?e[0]*12.92:1.055*Math.pow(t,1/2.4)-.055,o<.0031308?e[1]*12.92:1.055*Math.pow(o,1/2.4)-.055,r<.0031308?e[2]*12.92:1.055*Math.pow(r,1/2.4)-.055,e[3]]}function j(e){let t=_(e,c0);return[Math.cbrt(t[0]),Math.cbrt(t[1]),Math.cbrt(t[2]),t[3]]}function C(e){return _(e,a0)}function z(e){return _(e,l0)}function V(e){let t=M(S(z(e)));if(t[0]>1.001||t[0]<-.001||t[1]>1.001||t[1]<-.001||t[2]>1.001||t[2]<-.001){let[r,a,n,s]=e,f=Math.max(1e-5,Math.sqrt(a**2+n**2)),c=l(r,0,1),u=a/f,d=n/f,h=i0(u,d,r,f,c);return M(S(z([c*(1-h)+h*r,u*(h*f),d*(h*f),s])))}return t}function X(e){return V(_0(e))}function B(e){let t=Math.abs(e[0]),o=Math.abs(e[1]),r=Math.abs(e[2]);return[t<.04045?e[0]/12.92:((t+.055)/1.055)**2.4,o<.04045?e[1]/12.92:((o+.055)/1.055)**2.4,r<.04045?e[2]/12.92:((r+.055)/1.055)**2.4,e[3]]}function A(e){return M0(j(B(e)))}function q(e){return R0(A(e))}function u0(e){return _(e,s0)}var B0={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},J=B0;var G0=/-?[0-9.]+%?/g,T0=/^#?[0-9a-f]{3,8}$/i,w0=16**6,Q=16**4,W=16**2;function y(e){let t=L0(e),o={get hex(){let r="#";return r+=v(Math.round(l(t[0]*255,0,255)).toString(16),2),r+=v(Math.round(l(t[1]*255,0,255)).toString(16),2),r+=v(Math.round(l(t[2]*255,0,255)).toString(16),2),t[3]<1&&(r+=v(Math.round(t[3]*255).toString(16),2)),r},get hexVal(){t[3]<1&&console.warn(`hexVal converted a semi-transparent color (${t[3]*100}%) to fully opaque`);let r=Math.round(l(t[0]*255,0,255)),a=Math.round(l(t[1]*255,0,255)),n=Math.round(l(t[2]*255,0,255));return r*Q+a*W+n},get rgb(){return R("rgb",t)},rgbVal:t,get rgba(){return R("rgb",t)},rgbaVal:t,get linearRGB(){return B(t)},get p3(){return R("display-p3",t)},p3Val:t,get oklab(){return R("oklab",A(t))},get oklabVal(){return A(t)},get oklch(){return R("oklch",q(t))},get oklchVal(){return q(t)},get xyz(){return R("xyz-d65",C(B(t)))},get xyzVal(){return C(B(t))}};return o.toString=()=>o.hex,o}function d0(e){if(e>w0)throw new Error("8-digit hex values (with transparency) aren\u2019t supported");let t=e,o=Math.floor(t/Q);t-=o*Q;let r=Math.floor(t/W);t-=r*W;let a=t;return[o/255,r/255,a/255,1]}function G(e,t){let o=e.match(G0);if(!o)throw new Error(`Unexpected color format: ${e}`);let r=[0,0,0,1];return o.forEach((a,n)=>{a.includes("%")?r[n]=parseFloat(a)/100:t[n]===1/0||t[n]===0||t[n]===1?r[n]=parseFloat(a):r[n]=parseFloat(a)/t[n]}),r}function L0(e){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[l(e[0],0,1),l(e[1],0,1),l(e[2],0,1),typeof e[3]=="number"?l(e[3],0,1):1]}if(typeof e=="number")return d0(e);if(typeof e=="string"){let t=e.trim();if(!t)throw new Error("Expected color, received empty string");let o=t.toLowerCase();if(typeof J[o]=="number")return d0(J[o]);if(T0.test(t)){let n=t.replace("#",""),s=[0,0,0,1];if(n.length>=6)for(let f=0;f<n.length/2;f++){let c=f*2,u=c+2,d=n.substring(c,u);s[f]=parseInt(d,16)/255}else for(let f=0;f<n.length;f++){let c=n.charAt(f);s[f]=parseInt(`${c}${c}`,16)/255}return s}let[r,a]=t.split("(");if(r==="color"){let n=a.indexOf(" ");r=a.substring(0,n),a=a.substring(n)}switch(r){case"rgb":case"rgba":case"srgb":{let[n,s,f,c]=G(a,[255,255,255,1]);return[l(n,0,1),l(s,0,1),l(f,0,1),l(c,0,1)]}case"linear-rgb":case"linear-srgb":case"rgb-linear":case"srgb-linear":{let n=G(a,[255,255,255,1]);return M(n)}case"hsl":case"hsla":{let[n,s,f,c]=G(a,[1,1,1,1]);return U([n,l(s,0,1),l(f,0,1),l(c,0,1)])}case"hwb":case"hwba":{let[n,s,f,c]=G(a,[1,1,1,1]);return x0([n,l(s,0,1),l(f,0,1),l(c,0,1)])}case"p3":case"display-p3":{let[n,s,f,c]=G(a,[1,1,1,1]);return[l(n,0,1),l(s,0,1),l(f,0,1),l(c,0,1)]}case"oklab":return V(G(a,[1,1,1,1]));case"oklch":return X(G(a,[1,1,1,1]));case"xyz":case"xyz-d65":return M(u0(G(a,[1,1,1,1])))}}throw new Error(`Unable to parse color "${e}"`)}function e0(e){return g(y(e).oklabVal[0],5)}function E(e,t,o=.5,r="oklab"){let a=l(o,0,1);if(o===0)return y(e);if(o===1)return y(t);let n=1-a,s=a,f={oklch:q,oklab:A,lms:p=>B(j(p)),linearRGB:B,sRGB:p=>p},c={oklch:X,oklab:V,lms:p=>S(M(p)),linearRGB:M,sRGB:p=>p},u=f[r],d=c[r];if(!u)throw new Error(`Unknown color space "${r}", try "oklab", "oklch", "linearRGB", or "sRGB"`);let h=y(e).rgbVal,b=y(t).rgbVal;r==="oklch"&&(h[0]===h[1]&&h[1]===h[2]||b[0]===b[1]&&b[1]===b[2])&&(u=f.oklab,d=c.oklab);let[$,I,k,T]=u(h),[w,L,m,i]=u(b);return r==="oklch"&&Math.abs(m-k)>180&&(Math.max(k,m)===m?m-=360:k-=360),y(d([$*n+w*s,I*n+L*s,k*n+m*s,T*n+i*s]))}function D(e,t,o="oklab"){let r=l(t,-1,1);return r>=0?E(e,"black",r,o):Y(e,-r)}function Y(e,t,o="oklab"){let r=l(t,-1,1);return r>=0?E(e,"white",r,o):D(e,-r)}var J0={darken:D,from:y,lighten:Y,lightness:e0,mix:E};export{l as clamp,R as colorFn,D as darken,J0 as default,K as degToRad,y as from,v as leftPad,Y as lighten,e0 as lightness,E as mix,_ as multiplyColorMatrix,H as radToDeg,g as round}; | ||
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 _(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 M(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 M(e,x0)}function v(e){let[n,t,r,a]=M([e[0]**3,e[1]**3,e[2]**3,e[3]],x);return[n,t,r,a]}function R(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=M(e,d0);return[Math.cbrt(n[0]),Math.cbrt(n[1]),Math.cbrt(n[2]),n[3]]}function W(e){return M(e,l0)}function X(e){return M(e,h0)}function S(e){let n=R(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 R(v(X([c*(1-d)+d*r,l*(d*i),h*(d*i),s])))}return n}function z(e){return S(G0(e))}function B(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(B(e)))}function D(e){return B0(E(e))}function C(e){return M(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 _("rgb",n)},rgbVal:n,get rgba(){return _("rgb",n)},rgbaVal:n,get linearRGB(){return B(n)},get p3(){return _("display-p3",n)},p3Val:n,get oklab(){return _("oklab",E(n))},get oklabVal(){return E(n)},get oklch(){return _("oklch",D(n))},get oklchVal(){return D(n)},get xyz(){return _("xyz-d65",W(B(n)))},get xyzVal(){return W(B(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 R(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 R(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 R(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=>B(Q(p)),linearRGB:B,sRGB:p=>p},c={oklch:z,oklab:S,lms:p=>v(R(p)),linearRGB:R,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,_ 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,M as multiplyColorMatrix,U as radToDeg,k as round}; |
@@ -31,5 +31,5 @@ import type { Color, LinearRGB, Oklab, Oklch, sRGB, XYZ_D65 } from './colorspace.js'; | ||
oklchVal: Oklch; | ||
/** `color(xyz 0 0 0/1)` (2•, D65 whitepoint) */ | ||
/** `color(xyz 0 0 0/1)` (2°, D65 whitepoint) */ | ||
xyz: string; | ||
/** [X, Y, Z, alpha] (2•, D65 whitespace) */ | ||
/** [X, Y, Z, alpha] (2°, D65 whitespace) */ | ||
xyzVal: XYZ_D65; | ||
@@ -59,6 +59,1 @@ toString(): string; | ||
export declare function parse(rawColor: Color): sRGB; | ||
/** | ||
* Lightness | ||
* Shortcut of "L” from oklab | ||
*/ | ||
export declare function lightness(color: Color): number; |
import { hslTosRGB, hwbTosRGB, linearRGBTosRGB, linearRGBToXYZ, oklabTosRGB, oklchTosRGB, sRGBToLinearRGB, sRGBToOklab, sRGBToOklch, xyzToLinearRGB } from './colorspace.js'; | ||
import cssNames from './css-names.js'; | ||
import { clamp, colorFn, leftPad, round } from './utils.js'; | ||
import { clamp, colorFn, leftPad } from './utils.js'; | ||
// constants | ||
@@ -124,2 +124,5 @@ const FLOAT_RE = /-?[0-9.]+%?/g; | ||
export function parse(rawColor) { | ||
const unparsable = new Error(`Unable to parse color "${rawColor}"`); | ||
if (rawColor == undefined || rawColor == null) | ||
throw unparsable; | ||
// [R, G, B] or [R, G, B, A] | ||
@@ -138,2 +141,41 @@ if (Array.isArray(rawColor)) { | ||
} | ||
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) | ||
@@ -228,10 +270,3 @@ // !note: doesn’t support alpha | ||
} | ||
throw new Error(`Unable to parse color "${rawColor}"`); | ||
throw unparsable; | ||
} | ||
/** | ||
* Lightness | ||
* Shortcut of "L” from oklab | ||
*/ | ||
export function lightness(color) { | ||
return round(from(color).oklabVal[0], 5); // l == lightness | ||
} |
{ | ||
"name": "better-color-tools", | ||
"description": "Fast, minimal color conversion and tools for JS/Sass. Supports sRGB, Oklab, Oklch, Display P3, and more.", | ||
"version": "0.8.1", | ||
"version": "0.9.0", | ||
"author": { | ||
@@ -6,0 +6,0 @@ "name": "Drew Powers", |
@@ -5,3 +5,3 @@ # better-color-tools | ||
The JS version of this libray is fast (`~200k` ops/s), lightweight (`5 kB` gzip), and dependency-free. The Sass version… is Sass (which has no runtime). | ||
The JS version of this libray is fast (`~200k` ops/s), lightweight (`5.6 kB` gzip), and dependency-free. The Sass version… is Sass (which has no runtime). | ||
@@ -29,9 +29,19 @@ 👉 **Playground**: https://better-color-tools.pages.dev/ | ||
```js | ||
// CSS format | ||
better.from('#b3f6e6'); // hex string | ||
better.from(0xb3f6e6); // hex integer (note: only mode that doesn’t support transparency) | ||
better.from('rebeccapurple'); // CSS keyword | ||
better.from('rgb(136, 48, 62)'); // CSS RGB | ||
better.from('rgb(136, 48, 62)'); // CSS sRGB | ||
better.from('hsl(210, 85%, 37%)'); // CSS HSL | ||
better.from('hwb(210, 6%, 31%)'); // CSS HWB | ||
better.from('oklab(48.56949% -0.03971 -0.14459)'); // CSS Oklab | ||
better.from('oklch(83.11253% 0.22612 147.35276)'); // CSS Oklch | ||
// Other JS formats | ||
better.from(0xb3f6e6); // hex integer (note: only mode that doesn’t support transparency) | ||
better.from([0.533, 0.188, 0.243, 1]); // sRGB array/P3 (normalized to 1) | ||
better.from({ r: 0.533, g: 0.188, b: 0.243, alpha: 1 }); // sRGB/P3 object (normalized to 1) | ||
better.from({ h: 210, s: 0.85, l: 0.37, alpha: 1 }); // HSL object | ||
better.from({ h: 210, w: 0.06, b: 0.31, alpha: 1 }); // HWB object | ||
better.from({ l: 0.4856949, a: -0.03971, b: -0.14459, alpha: 1 }); // Oklab object (not CIELAB) | ||
better.from({ l: 0.8311253, c: 0.22612, h: 147.35276, alpha: 1 }); // Oklch object (not CIELCh) | ||
``` | ||
@@ -46,3 +56,4 @@ | ||
- **sRGB** (hex): `better.from('…').hex` / `better.from('…').hexVal` | ||
- **sRGB** (RGB): `better.from('…').rgb` / `better.from('–').rgbVal` | ||
- **sRGB** (RGB): `better.from('…').rgb` / `better.from('…').rgbVal` | ||
- **P3**: `better.from('…').p3` / `better.from('…').p3Val` | ||
- **Oklab**: `better.from('…').oklab` / `better.from('…').oklabVal` | ||
@@ -56,7 +67,23 @@ - **Oklch**: `better.from('…').oklch` / `better.from('…').oklchVal` | ||
- HWB isn’t supported because it’s another form of HSL | ||
- HSV is a great colorspace, but on no standards track for the web currently | ||
- CIE L\*a\*/CIE L\*C\*h aren’t supported because Oklab/Oklch [are superior](https://bottosson.github.io/posts/oklab/) | ||
- CIELAB/CIELCh aren’t supported because Oklab/Oklch [are superior](https://bottosson.github.io/posts/oklab/) | ||
- HSV is a great color space and is on the roadmap but isn’t available right now | ||
For a comprehensive color conversion library, see [culori](https://github.com/Evercoder/culori). | ||
**P3** | ||
This library supports [P3](https://webkit.org/blog/10042/wide-gamut-color-in-css-with-display-p3/) by expanding the sRGB space into the P3 gamut 1:1. For example, 100% red sRGB is converted to 100% red P3: | ||
```js | ||
const red = '#ff0000'; | ||
better.from(red).rgb; // rgb(255, 0, 0) | ||
better.from(red).p3; // color(display-p3 1 0 0) | ||
``` | ||
This is [the practice recommended by WebKit](https://webkit.org/blog/10042/wide-gamut-color-in-css-with-display-p3/) because when dealing with web colors you probably intend to take full advantage of that expanded gamut and this is the easiest, quickest | ||
way to do so without dealing with the specifics of both the sRGB and P3 gamuts. This gives you more vibrant colors in supporting browsers without your colors appearing “off.” | ||
While you wouldn’t want to use this technique for other methods such as photo manipulation, for CSS purposes this method is ideal. better-color-tools assumes a CSS application (or something similar), so any deviation between this library’s implementation | ||
of P3 from a more color-science-focused library like culori are intentional. | ||
**Mix** | ||
@@ -90,13 +117,40 @@ | ||
Manipulation is best done in a space like [Oklch](https://oklch.evilmartians.io/#70,0.1,17,100) which is optimized for manual tweaking. | ||
```js | ||
import better, { colorFn } from 'better-color-tools'; | ||
import better from 'better-color-tools'; | ||
let [L, C, h] = better.from('#5a00a6').oklchVal; | ||
h += 5; // rotate hue by 5° | ||
C += 0.01; // increase Chroma by 1% | ||
better.from(colorFn('oklch', [L, C, h])).hex; // #6f00ca | ||
let [l, c, h] = better.from('#5a00a6').oklchVal; | ||
better.from({ | ||
l, | ||
c: c + 0.01, // increase Chroma by 1% | ||
h: h + 5, // rotate hue by 5° | ||
}).hex; // #6f00ca | ||
``` | ||
Manipulation is best done in a space like [Oklch](https://oklch.evilmartians.io/#70,0.1,17,100) which is optimized for manual tweaking. | ||
**Contrast Ratio** | ||
Get [WCAG 2.1 contrast ratio](https://www.w3.org/WAI/WCAG21/quickref/?showtechniques=141%2C146#contrast-minimum) for 2 colors. The order doesn’t matter. | ||
```js | ||
import { contrastRatio } from 'better-color-tools'; | ||
contrastRatio('#37ca93', '#055af6'); // { ratio: 2.4, AA: false, AAA: false } | ||
contrastRatio('#37ca93', '#4474cc'); // { ratio: 4.5, AA: true, AAA: false } | ||
contrastRatio('#37ca93', '#002c7b'); // { ratio: 12.76, AA: true, AAA: true } | ||
``` | ||
**Light or dark?** | ||
Should you overlay white or black text over a color? This function will figure out whether a color is perceptually “dark” or “light.” You can then use white text for dark colors, and vice-versa. | ||
This subjective and nuanced, so to read more about the method see [Myndex’s “flip for color” technique](https://gist.github.com/Myndex/e1025706436736166561d339fd667493). | ||
```js | ||
import { lightOrDark } from 'better-color-tools'; | ||
lightOrDark('#2d659e'); // "dark" (white text will show better) | ||
lightOrDark('#b2d6d3'); // "light" (black text will show better) | ||
``` | ||
### Sass | ||
@@ -103,0 +157,0 @@ |
@@ -129,3 +129,3 @@ export type HSL = [number, number, number, number]; | ||
/** Linear sRGB -> sRGB */ | ||
export function linearRGBTosRGB(rgb: LinearRGB): sRGB { | ||
export function linearRGBTosRGB(rgb: LinearRGB, γ = 2.4): sRGB { | ||
const r = Math.abs(rgb[0]); | ||
@@ -135,5 +135,5 @@ const g = Math.abs(rgb[1]); | ||
return [ | ||
r < 0.0031308 ? rgb[0] * 12.92 : 1.055 * Math.pow(r, 1 / 2.4) - 0.055, // r | ||
g < 0.0031308 ? rgb[1] * 12.92 : 1.055 * Math.pow(g, 1 / 2.4) - 0.055, // g | ||
b < 0.0031308 ? rgb[2] * 12.92 : 1.055 * Math.pow(b, 1 / 2.4) - 0.055, // b | ||
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 | ||
@@ -222,3 +222,3 @@ ]; | ||
/** sRGB -> Linear sRGB */ | ||
export function sRGBToLinearRGB(rgb: sRGB): LinearRGB { | ||
export function sRGBToLinearRGB(rgb: sRGB, γ = 2.4): LinearRGB { | ||
const r = Math.abs(rgb[0]); | ||
@@ -228,5 +228,5 @@ const g = Math.abs(rgb[1]); | ||
return [ | ||
r < 0.04045 ? rgb[0] / 12.92 : ((r + 0.055) / 1.055) ** 2.4, // r | ||
g < 0.04045 ? rgb[1] / 12.92 : ((g + 0.055) / 1.055) ** 2.4, // g | ||
b < 0.04045 ? rgb[2] / 12.92 : ((b + 0.055) / 1.055) ** 2.4, // b | ||
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 | ||
@@ -233,0 +233,0 @@ ]; |
export type { ColorMatrix, HSL, HWB, LMS, LinearRGB, LUV, Oklab, Oklch, sRGB, XYZ_D65 } from './colorspace.js'; | ||
export type { LightenDarkenColorSpace } from './lighten-darken.js'; | ||
export type { MixColorSpace } from './mix.js'; | ||
export type { ColorOutput } from './parse.js'; | ||
export { darken, lighten } from './lighten-darken.js'; | ||
export { contrastRatio, darken, lighten, lightOrDark, lightness, luminance } from './luminance.js'; | ||
export { mix } from './mix.js'; | ||
export { from, lightness } from './parse.js'; | ||
export { from } from './parse.js'; | ||
export { clamp, colorFn, degToRad, leftPad, multiplyColorMatrix, radToDeg, round } from './utils.js'; | ||
import { darken, lighten } from './lighten-darken.js'; | ||
import { contrastRatio, darken, lighten, lightOrDark, lightness, luminance } from './luminance.js'; | ||
import { mix } from './mix.js'; | ||
import { from, lightness } from './parse.js'; | ||
import { from } from './parse.js'; | ||
export default { | ||
contrastRatio, | ||
darken, | ||
@@ -20,3 +20,5 @@ from, | ||
lightness, | ||
lightOrDark, | ||
luminance, | ||
mix, | ||
}; |
@@ -5,3 +5,3 @@ import type { Color, LinearRGB, Oklab, Oklch, sRGB, XYZ_D65 } from './colorspace.js'; | ||
import cssNames from './css-names.js'; | ||
import { clamp, colorFn, leftPad, round } from './utils.js'; | ||
import { clamp, colorFn, leftPad } from './utils.js'; | ||
@@ -39,5 +39,5 @@ export interface ColorOutput { | ||
oklchVal: Oklch; | ||
/** `color(xyz 0 0 0/1)` (2•, D65 whitepoint) */ | ||
/** `color(xyz 0 0 0/1)` (2°, D65 whitepoint) */ | ||
xyz: string; | ||
/** [X, Y, Z, alpha] (2•, D65 whitespace) */ | ||
/** [X, Y, Z, alpha] (2°, D65 whitespace) */ | ||
xyzVal: XYZ_D65; | ||
@@ -167,2 +167,6 @@ toString(): string; // JS helper | ||
export function parse(rawColor: Color): sRGB { | ||
const unparsable = new Error(`Unable to parse color "${rawColor}"`); | ||
if (rawColor == undefined || rawColor == null) throw unparsable; | ||
// [R, G, B] or [R, G, B, A] | ||
@@ -180,2 +184,36 @@ if (Array.isArray(rawColor)) { | ||
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) | ||
@@ -274,11 +312,3 @@ // !note: doesn’t support alpha | ||
throw new Error(`Unable to parse color "${rawColor}"`); | ||
throw unparsable; | ||
} | ||
/** | ||
* Lightness | ||
* Shortcut of "L” from oklab | ||
*/ | ||
export function lightness(color: Color): number { | ||
return round(from(color).oklabVal[0], 5); // l == lightness | ||
} |
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
126777
2362
248