@sardine/colour 2.1.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CIEDE2000.d.ts +1 -1
- package/dist/getContrastRatioFromHex.d.ts +9 -0
- package/dist/index.cjs +54 -35
- package/dist/index.d.ts +1 -0
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +54 -35
- package/package.json +4 -12
package/dist/CIEDE2000.d.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { WCAG } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Calculates the contrast ratio between two colours in hexadecimal format.
|
|
4
|
+
* @param colour1 The first colour in hexadecimal format
|
|
5
|
+
* @param colour2 The second colour in hexadecimal format
|
|
6
|
+
* @param standard The standard to evaluate the contrast ratio against, defaults to WCAG2.1
|
|
7
|
+
* @returns The contrast ratio between the two colours truncated to 3 decimal places
|
|
8
|
+
*/
|
|
9
|
+
export declare function getContrastRatioFromHex(colour1: string, colour2: string, standard: WCAG): number;
|
package/dist/index.cjs
CHANGED
|
@@ -68,42 +68,52 @@ function clamp(value, min, max) {
|
|
|
68
68
|
return Math.min(Math.max(value, min), max);
|
|
69
69
|
}
|
|
70
70
|
function ciede2000(colour1, colour2) {
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const G = 0.5 * (1 - bigSquare(
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
71
|
+
const lightness1 = colour1.L;
|
|
72
|
+
const greenRed1 = colour1.a;
|
|
73
|
+
const blueYellow1 = colour1.b;
|
|
74
|
+
const lightness2 = colour2.L;
|
|
75
|
+
const greenRed2 = colour2.a;
|
|
76
|
+
const blueYellow2 = colour2.b;
|
|
77
|
+
const luminanceWeight = 1;
|
|
78
|
+
const chromaWeight = 1;
|
|
79
|
+
const hueWeight = 1;
|
|
80
|
+
const chroma1 = Math.sqrt(greenRed1 ** 2 + blueYellow1 ** 2);
|
|
81
|
+
const chroma2 = Math.sqrt(greenRed2 ** 2 + blueYellow2 ** 2);
|
|
82
|
+
const deltaLightness = lightness2 - lightness1;
|
|
83
|
+
const meanChroma = (chroma1 + chroma2) / 2;
|
|
84
|
+
const G = 0.5 * (1 - bigSquare(meanChroma));
|
|
85
|
+
const greenRed1Prime = greenRed1 * (1 + G);
|
|
86
|
+
const greenRed2Prime = greenRed2 * (1 + G);
|
|
87
|
+
const chroma1Prime = Math.sqrt(greenRed1Prime ** 2 + blueYellow1 ** 2);
|
|
88
|
+
const chroma2Prime = Math.sqrt(greenRed2Prime ** 2 + blueYellow2 ** 2);
|
|
89
|
+
const meanChromaPrime = (chroma1Prime + chroma2Prime) / 2;
|
|
90
|
+
const deltaChromaPrime = chroma2Prime - chroma1Prime;
|
|
91
|
+
const hue1Prime = hue_d(blueYellow1, greenRed1Prime);
|
|
92
|
+
const hue2Prime = hue_d(blueYellow2, greenRed2Prime);
|
|
93
|
+
const deltaHuePrime = deltaHue_d({
|
|
94
|
+
C1: chroma1,
|
|
95
|
+
C2: chroma2,
|
|
96
|
+
h1_d: hue1Prime,
|
|
97
|
+
h2_d: hue2Prime
|
|
98
|
+
});
|
|
99
|
+
const deltaHue = 2 * Math.sqrt(chroma1Prime * chroma2Prime) * Math.sin(toRadians(deltaHuePrime) / 2);
|
|
100
|
+
const meanHuePrime = meanHue_d({
|
|
101
|
+
C1: chroma1,
|
|
102
|
+
C2: chroma2,
|
|
103
|
+
h1_d: hue1Prime,
|
|
104
|
+
h2_d: hue2Prime
|
|
105
|
+
});
|
|
106
|
+
const meanLightness = (lightness1 + lightness2) / 2;
|
|
107
|
+
const T = 1 - 0.17 * Math.cos(toRadians(meanHuePrime - 30)) + 0.24 * Math.cos(toRadians(2 * meanHuePrime)) + 0.32 * Math.cos(toRadians(3 * meanHuePrime + 6)) - 0.2 * Math.cos(toRadians(4 * meanHuePrime - 63));
|
|
108
|
+
const SL = 1 + 0.015 * (meanLightness - 50) ** 2 / Math.sqrt(20 + (meanLightness - 50) ** 2);
|
|
109
|
+
const SC = 0.045 * meanChromaPrime + 1;
|
|
110
|
+
const SH = 1 + 0.015 * meanChromaPrime * T;
|
|
111
|
+
const rotation = 30 * Math.exp(-(((meanHuePrime - 275) / 25) ** 2));
|
|
112
|
+
const RT = -2 * bigSquare(meanChromaPrime) * Math.sin(toRadians(rotation * 2));
|
|
113
|
+
const deltaE = Math.sqrt(
|
|
114
|
+
(deltaLightness / (luminanceWeight * SL)) ** 2 + (deltaChromaPrime / (chromaWeight * SC)) ** 2 + (deltaHue / (hueWeight * SH)) ** 2 + RT * (deltaChromaPrime / (chromaWeight * SC)) * (deltaHue / (hueWeight * SH))
|
|
105
115
|
);
|
|
106
|
-
return
|
|
116
|
+
return deltaE;
|
|
107
117
|
}
|
|
108
118
|
function convertRGBtoXYZ(colour) {
|
|
109
119
|
const { R, G, B } = colour;
|
|
@@ -508,6 +518,14 @@ function getSRGBLuminanceFromHex(colour, standard) {
|
|
|
508
518
|
const rgbColor = convertHextoRGB(colour);
|
|
509
519
|
return getSRGBLuminanceFromRGB(rgbColor, standard);
|
|
510
520
|
}
|
|
521
|
+
function getContrastRatioFromHex(colour1, colour2, standard) {
|
|
522
|
+
const luminance1 = getSRGBLuminanceFromHex(colour1, standard);
|
|
523
|
+
const luminance2 = getSRGBLuminanceFromHex(colour2, standard);
|
|
524
|
+
const lighter = Math.max(luminance1, luminance2);
|
|
525
|
+
const darker = Math.min(luminance1, luminance2);
|
|
526
|
+
const ratio = (lighter + 0.05) / (darker + 0.05);
|
|
527
|
+
return Math.floor(ratio * 1e3) / 1e3;
|
|
528
|
+
}
|
|
511
529
|
function isHexDarkColour(colour, standard) {
|
|
512
530
|
const colourLuminance = getSRGBLuminanceFromHex(colour, standard) + 0.05;
|
|
513
531
|
const whiteContrast = 1.05 / colourLuminance;
|
|
@@ -587,6 +605,7 @@ exports.findNearestColour = findNearestColour;
|
|
|
587
605
|
exports.findNearestHexColour = findNearestHexColour;
|
|
588
606
|
exports.findNearestNamedCSSColour = findNearestNamedCSSColour;
|
|
589
607
|
exports.findNearestRGBColour = findNearestRGBColour;
|
|
608
|
+
exports.getContrastRatioFromHex = getContrastRatioFromHex;
|
|
590
609
|
exports.getSRGBLuminanceFromHex = getSRGBLuminanceFromHex;
|
|
591
610
|
exports.getSRGBLuminanceFromRGB = getSRGBLuminanceFromRGB;
|
|
592
611
|
exports.isCSSNamedDarkColour = isCSSNamedDarkColour;
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export { findNearestColour } from "./findNearestColour";
|
|
|
17
17
|
export { findNearestHexColour } from "./findNearestHexColour";
|
|
18
18
|
export { findNearestNamedCSSColour } from "./findNearestNamedCSSColour";
|
|
19
19
|
export { findNearestRGBColour } from "./findNearestRGBColour";
|
|
20
|
+
export { getContrastRatioFromHex } from "./getContrastRatioFromHex";
|
|
20
21
|
export { getSRGBLuminanceFromHex } from "./getSRGBLuminanceFromHex";
|
|
21
22
|
export { getSRGBLuminanceFromRGB } from "./getSRGBLuminanceFromRGB";
|
|
22
23
|
export { isCSSNamedDarkColour } from "./isCSSNameDarkColour";
|
package/dist/index.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";function
|
|
1
|
+
"use strict";function T(e,t){if(e===0&&t===0)return 0;const n=Math.atan2(e,t)*(180/Math.PI);return n>=0?n:n+360}function be({C1:e,C2:t,h1_d:n,h2_d:r}){return e*t===0?0:Math.abs(r-n)<=180?r-n:r-n>180?r-n-360:r-n<-180?r-n+360:0}function ge({C1:e,C2:t,h1_d:n,h2_d:r}){return e*t===0?r+n:Math.abs(n-r)<=180?(r+n)/2:Math.abs(n-r)>180&&n+r<360?(r+n+360)/2:Math.abs(n-r)>180&&n+r>=360?(r+n-360)/2:0}const m=e=>e*(Math.PI/180),z=e=>Math.sqrt(e**7/(e**7+25**7));function b(e,t){const n=e/255,r=t?.03928:.04045;let o;return n>r?o=((n+.055)/1.055)**2.4:o=n/12.92,o}function v(e){const t=.20689655172413793,n=t**3;let r;return e>n?r=Math.cbrt(e):r=e/(3*t**2)+4/29,r}function he(e,t,n){return Math.min(Math.max(e,t),n)}function j(e,t){const n=e.L,r=e.a,o=e.b,a=t.L,s=t.a,f=t.b,c=1,d=1,G=1,g=Math.sqrt(r**2+o**2),h=Math.sqrt(s**2+f**2),fe=a-n,ce=(g+h)/2,P=.5*(1-z(ce)),A=r*(1+P),Y=s*(1+P),B=Math.sqrt(A**2+o**2),$=Math.sqrt(Y**2+f**2),k=(B+$)/2,F=$-B,W=T(o,A),E=T(f,Y),ie=be({C1:g,C2:h,h1_d:W,h2_d:E}),X=2*Math.sqrt(B*$)*Math.sin(m(ie)/2),C=ge({C1:g,C2:h,h1_d:W,h2_d:E}),Z=(n+a)/2,ue=1-.17*Math.cos(m(C-30))+.24*Math.cos(m(2*C))+.32*Math.cos(m(3*C+6))-.2*Math.cos(m(4*C-63)),le=1+.015*(Z-50)**2/Math.sqrt(20+(Z-50)**2),D=.045*k+1,x=1+.015*k*ue,de=30*Math.exp(-(((C-275)/25)**2)),me=-2*z(k)*Math.sin(m(de*2));return Math.sqrt((fe/(c*le))**2+(F/(d*D))**2+(X/(G*x))**2+me*(F/(d*D))*(X/(G*x)))}function O(e){const{R:t,G:n,B:r}=e,o=b(t)*100,a=b(n)*100,s=b(r)*100,f=o*.4124+a*.3576+s*.1805,c=o*.2126+a*.7152+s*.0722,d=o*.0193+a*.1192+s*.9505;return{X:f,Y:c,Z:d}}function U(e){const{X:t,Y:n,Z:r}=e,o=t/95.047,a=n/100,s=r/108.883,f=v(o),c=v(a),d=v(s),G=116*c-16,g=500*(f-c),h=200*(c-d);return{L:G,a:g,b:h}}function w(e){const t=O(e);return U(t)}const V=(e,t)=>{const n=w(e),r=w(t);return j(n,r)},J=/^#[a-fA-F0-9]{6}$/,K=/^#[a-fA-F0-9]{8}$/,Q=/^#[a-fA-F0-9]{3}$/,_=/^#[a-fA-F0-9]{4}$/,ee=/^rgba*\(\s*([-+]?\d+)\s*(?:,)?\s*([-+]?\d+)\s*(?:,)?\s*([-+]?\d+)\s*(?:,*|\/*)\s*([-+]?\d*[.]?\d+[%]?)*\)$/i;function i(e){const t=e.match(ee);if(!t)throw new Error(`convertCSSRGBtoHex expects a valid CSS RGB string but got ${e}`);const n=r=>r?Number.parseFloat(r):void 0;return{R:n(t[1]),G:n(t[2]),B:n(t[3]),A:n(t[4])}}function p({R:e,G:t,B:n,A:r}){const o=a=>he(a,0,255).toString(16).padStart(2,"0");return`#${o(e)}${o(t)}${o(n)}${r?o(Math.round(r*255)):""}`}function Ce(e){const t=i(e);return p(t)}const y=new Map([["aliceblue","#f0f8ff"],["antiquewhite","#faebd7"],["aqua","#00ffff"],["aquamarine","#7fffd4"],["azure","#f0ffff"],["beige","#f5f5dc"],["bisque","#ffe4c4"],["black","#000000"],["blanchedalmond","#ffebcd"],["blue","#0000ff"],["blueviolet","#8a2be2"],["brown","#a52a2a"],["burlywood","#deb887"],["cadetblue","#5f9ea0"],["chartreuse","#7fff00"],["chocolate","#d2691e"],["coral","#ff7f50"],["cornflowerblue","#6495ed"],["cornsilk","#fff8dc"],["crimson","#dc143c"],["cyan","#00ffff"],["darkblue","#00008b"],["darkcyan","#008b8b"],["darkgoldenrod","#b8860b"],["darkgray","#a9a9a9"],["darkgreen","#006400"],["darkgrey","#a9a9a9"],["darkkhaki","#bdb76b"],["darkmagenta","#8b008b"],["darkolivegreen","#556b2f"],["darkorange","#ff8c00"],["darkorchid","#9932cc"],["darkred","#8b0000"],["darksalmon","#e9967a"],["darkseagreen","#8fbc8f"],["darkslateblue","#483d8b"],["darkslategray","#2f4f4f"],["darkslategrey","#2f4f4f"],["darkturquoise","#00ced1"],["darkviolet","#9400d3"],["deeppink","#ff1493"],["deepskyblue","#00bfff"],["dimgray","#696969"],["dimgrey","#696969"],["dodgerblue","#1e90ff"],["firebrick","#b22222"],["floralwhite","#fffaf0"],["forestgreen","#228b22"],["fuchsia","#ff00ff"],["gainsboro","#dcdcdc"],["ghostwhite","#f8f8ff"],["gold","#ffd700"],["goldenrod","#daa520"],["gray","#808080"],["green","#008000"],["greenyellow","#adff2f"],["grey","#808080"],["honeydew","#f0fff0"],["hotpink","#ff69b4"],["indianred","#cd5c5c"],["indigo","#4b0082"],["ivory","#fffff0"],["khaki","#f0e68c"],["lavender","#e6e6fa"],["lavenderblush","#fff0f5"],["lawngreen","#7cfc00"],["lemonchiffon","#fffacd"],["lightblue","#add8e6"],["lightcoral","#f08080"],["lightcyan","#e0ffff"],["lightgoldenrodyellow","#fafad2"],["lightgray","#d3d3d3"],["lightgreen","#90ee90"],["lightgrey","#d3d3d3"],["lightpink","#ffb6c1"],["lightsalmon","#ffa07a"],["lightseagreen","#20b2aa"],["lightskyblue","#87cefa"],["lightslategray","#778899"],["lightslategrey","#778899"],["lightsteelblue","#b0c4de"],["lightyellow","#ffffe0"],["lime","#00ff00"],["limegreen","#32cd32"],["linen","#faf0e6"],["magenta","#ff00ff"],["maroon","#800000"],["mediumaquamarine","#66cdaa"],["mediumblue","#0000cd"],["mediumorchid","#ba55d3"],["mediumpurple","#9370db"],["mediumseagreen","#3cb371"],["mediumslateblue","#7b68ee"],["mediumspringgreen","#00fa9a"],["mediumturquoise","#48d1cc"],["mediumvioletred","#c71585"],["midnightblue","#191970"],["mintcream","#f5fffa"],["mistyrose","#ffe4e1"],["moccasin","#ffe4b5"],["navajowhite","#ffdead"],["navy","#000080"],["oldlace","#fdf5e6"],["olive","#808000"],["olivedrab","#6b8e23"],["orangered","#ff4500"],["orchid","#da70d6"],["palegoldenrod","#eee8aa"],["palegreen","#98fb98"],["paleturquoise","#afeeee"],["palevioletred","#db7093"],["papayawhip","#ffefd5"],["peachpuff","#ffdab9"],["peru","#cd853f"],["pink","#ffc0cb"],["plum","#dda0dd"],["powderblue","#b0e0e6"],["purple","#800080"],["red","#ff0000"],["rosybrown","#bc8f8f"],["royalblue","#4169e1"],["saddlebrown","#8b4513"],["salmon","#fa8072"],["sandybrown","#f4a460"],["seagreen","#2e8b57"],["seashell","#fff5ee"],["sienna","#a0522d"],["silver","#c0c0c0"],["skyblue","#87ceeb"],["slateblue","#6a5acd"],["slategray","#708090"],["slategrey","#708090"],["snow","#fffafa"],["springgreen","#00ff7f"],["steelblue","#4682b4"],["tan","#d2b48c"],["teal","#008080"],["thistle","#d8bfd8"],["tomato","#ff6347"],["transparent","#00000000"],["turquoise","#40e0d0"],["violet","#ee82ee"],["wheat","#f5deb3"],["white","#ffffff"],["whitesmoke","#f5f5f5"],["yellow","#ffff00"],["yellowgreen","#9acd32"]]);function te(e){for(const[t,n]of y.entries())if(n===e)return t}function u(e){if(typeof e!="string")throw new Error(`convertHextoRGB expects a string but got a ${typeof e}`);if(e.match(J))return{R:Number.parseInt(`${e[1]}${e[2]}`,16),G:Number.parseInt(`${e[3]}${e[4]}`,16),B:Number.parseInt(`${e[5]}${e[6]}`,16)};if(e.match(Q))return{R:Number.parseInt(`${e[1]}${e[1]}`,16),G:Number.parseInt(`${e[2]}${e[2]}`,16),B:Number.parseInt(`${e[3]}${e[3]}`,16)};if(e.match(K))return{R:Number.parseInt(`${e[1]}${e[2]}`,16),G:Number.parseInt(`${e[3]}${e[4]}`,16),B:Number.parseInt(`${e[5]}${e[6]}`,16),A:Number.parseInt(`${e[7]}${e[8]}`,16)/255};if(e.match(_))return{R:Number.parseInt(`${e[1]}${e[1]}`,16),G:Number.parseInt(`${e[2]}${e[2]}`,16),B:Number.parseInt(`${e[3]}${e[3]}`,16),A:Number.parseInt(`${e[4]}${e[4]}`,16)/255};throw new Error(`convertHextoRGB expects an valid hexadecimal colour value but got ${e}`)}function M(e){return y.get(e)}function R(e){const t=M(e);if(t)return u(t)}function N({R:e,G:t,B:n,A:r}){return`rgb(${e} ${t} ${n}${r?` / ${r}`:""})`}function H(e){const t=p(e);return te(t)}function S(e,t){if(!t||t.length<2)return e;const n=[];for(const o of t){const a=V(e,o);n.push([o,a])}return n.sort((o,a)=>o[1]-a[1])[0][0]}function pe(e,t){if(!t||t.length<2)return e;const n=i(e),r=t.map(a=>i(a)),o=S(n,r);return N(o)}function q(e){return!!e.match(ee)}function ne(e){return!!e.match(J)||!!e.match(K)||!!e.match(_)||!!e.match(Q)}function re(e){return y.has(e)}function Re(e,t){if(!t||t.length<2)return e;let n,r;const o=[];if(ne(e)&&(n=u(e),r="hex"),q(e)&&(n=i(e),r="cssRGB"),re(e)&&(n=R(e),r="namedCSS"),!n)return;for(const s of t)ne(s)&&o.push(u(s)),q(s)&&o.push(i(s)),re(s)&&o.push(R(s));if(o.length<2)return e;const a=S(n,o);if(r==="hex")return p(a);if(r==="cssRGB")return N(a);if(r==="namedCSS")return H(a)}function Se(e,t){if(!t||t.length<2)return e;const n=u(e),r=t.map(a=>u(a)),o=S(n,r);return p(o)}function Ge(e,t){if(!t||t.length<2)return e;const n=R(e),o=t.map(s=>R(s)).filter(s=>s!==void 0);if(!n||o.length<2)return e;const a=S(n,o);return H(a)}function I({R:e,G:t,B:n},r){const o=r==="WCAG2.1",a=b(e,o),s=b(t,o),f=b(n,o);return .2126*a+.7152*s+.0722*f}function l(e,t){const n=u(e);return I(n,t)}function Be(e,t,n){const r=l(e,n),o=l(t,n),a=Math.max(r,o),s=Math.min(r,o),f=(a+.05)/(s+.05);return Math.floor(f*1e3)/1e3}function L(e,t){const n=l(e,t)+.05,r=1.05/n,o=n/.05;return r>o}function oe(e,t){const n=M(e);if(n)return L(n,t);throw new Error(`${e} is not a valid colour format. isCSSNamedDarkColour only accepts CSS named colours. Check more details here https://developer.mozilla.org/en-US/docs/Web/CSS/named-color`)}function ae(e,t){const n=i(e),r=I(n,t),o=1.05/r,a=r/.05;return o>a}function $e(e,t){try{return e.startsWith("#")?L(e,t):e.startsWith("rgb")?ae(e,t):oe(e,t)}catch{throw new Error(`${e} is not a valid colour format. isDarkColour accepts CSS RGB formats, ie rgb(0,0,0) and rgba(255, 255, 255, 0.4), hexadecimal and CSS named colours.`)}}const se=(e,t)=>e>t?e/t:t/e,ke=({backgroundColour:e,optionOneColour:t,optionTwoColour:n},r)=>{const o=l(e,r)+.05,a=l(t,r)+.05,s=l(n,r)+.05,f=se(a,o),c=se(s,o);return f>c?t:n};export{V as RGBdistance,j as ciede2000,Ce as convertCSSRGBtoHex,i as convertCSSRGBtoRGB,te as convertHextoNamedCSSColour,u as convertHextoRGB,M as convertNamedCSSColourtoHex,R as convertNamedCSSColourtoRGB,N as convertRGBtoCSSRGB,p as convertRGBtoHex,w as convertRGBtoLab,H as convertRGBtoNamedCSSColour,O as convertRGBtoXYZ,U as convertXYZtoLab,pe as findNearestCSSRGBColour,Re as findNearestColour,Se as findNearestHexColour,Ge as findNearestNamedCSSColour,S as findNearestRGBColour,Be as getContrastRatioFromHex,l as getSRGBLuminanceFromHex,I as getSRGBLuminanceFromRGB,oe as isCSSNamedDarkColour,q as isCSSRGBColour,ae as isCSSRGBDarkColour,$e as isDarkColour,L as isHexDarkColour,ke as pickHexColourContrast};
|
package/dist/index.mjs
CHANGED
|
@@ -66,42 +66,52 @@ function clamp(value, min, max) {
|
|
|
66
66
|
return Math.min(Math.max(value, min), max);
|
|
67
67
|
}
|
|
68
68
|
function ciede2000(colour1, colour2) {
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const G = 0.5 * (1 - bigSquare(
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
69
|
+
const lightness1 = colour1.L;
|
|
70
|
+
const greenRed1 = colour1.a;
|
|
71
|
+
const blueYellow1 = colour1.b;
|
|
72
|
+
const lightness2 = colour2.L;
|
|
73
|
+
const greenRed2 = colour2.a;
|
|
74
|
+
const blueYellow2 = colour2.b;
|
|
75
|
+
const luminanceWeight = 1;
|
|
76
|
+
const chromaWeight = 1;
|
|
77
|
+
const hueWeight = 1;
|
|
78
|
+
const chroma1 = Math.sqrt(greenRed1 ** 2 + blueYellow1 ** 2);
|
|
79
|
+
const chroma2 = Math.sqrt(greenRed2 ** 2 + blueYellow2 ** 2);
|
|
80
|
+
const deltaLightness = lightness2 - lightness1;
|
|
81
|
+
const meanChroma = (chroma1 + chroma2) / 2;
|
|
82
|
+
const G = 0.5 * (1 - bigSquare(meanChroma));
|
|
83
|
+
const greenRed1Prime = greenRed1 * (1 + G);
|
|
84
|
+
const greenRed2Prime = greenRed2 * (1 + G);
|
|
85
|
+
const chroma1Prime = Math.sqrt(greenRed1Prime ** 2 + blueYellow1 ** 2);
|
|
86
|
+
const chroma2Prime = Math.sqrt(greenRed2Prime ** 2 + blueYellow2 ** 2);
|
|
87
|
+
const meanChromaPrime = (chroma1Prime + chroma2Prime) / 2;
|
|
88
|
+
const deltaChromaPrime = chroma2Prime - chroma1Prime;
|
|
89
|
+
const hue1Prime = hue_d(blueYellow1, greenRed1Prime);
|
|
90
|
+
const hue2Prime = hue_d(blueYellow2, greenRed2Prime);
|
|
91
|
+
const deltaHuePrime = deltaHue_d({
|
|
92
|
+
C1: chroma1,
|
|
93
|
+
C2: chroma2,
|
|
94
|
+
h1_d: hue1Prime,
|
|
95
|
+
h2_d: hue2Prime
|
|
96
|
+
});
|
|
97
|
+
const deltaHue = 2 * Math.sqrt(chroma1Prime * chroma2Prime) * Math.sin(toRadians(deltaHuePrime) / 2);
|
|
98
|
+
const meanHuePrime = meanHue_d({
|
|
99
|
+
C1: chroma1,
|
|
100
|
+
C2: chroma2,
|
|
101
|
+
h1_d: hue1Prime,
|
|
102
|
+
h2_d: hue2Prime
|
|
103
|
+
});
|
|
104
|
+
const meanLightness = (lightness1 + lightness2) / 2;
|
|
105
|
+
const T = 1 - 0.17 * Math.cos(toRadians(meanHuePrime - 30)) + 0.24 * Math.cos(toRadians(2 * meanHuePrime)) + 0.32 * Math.cos(toRadians(3 * meanHuePrime + 6)) - 0.2 * Math.cos(toRadians(4 * meanHuePrime - 63));
|
|
106
|
+
const SL = 1 + 0.015 * (meanLightness - 50) ** 2 / Math.sqrt(20 + (meanLightness - 50) ** 2);
|
|
107
|
+
const SC = 0.045 * meanChromaPrime + 1;
|
|
108
|
+
const SH = 1 + 0.015 * meanChromaPrime * T;
|
|
109
|
+
const rotation = 30 * Math.exp(-(((meanHuePrime - 275) / 25) ** 2));
|
|
110
|
+
const RT = -2 * bigSquare(meanChromaPrime) * Math.sin(toRadians(rotation * 2));
|
|
111
|
+
const deltaE = Math.sqrt(
|
|
112
|
+
(deltaLightness / (luminanceWeight * SL)) ** 2 + (deltaChromaPrime / (chromaWeight * SC)) ** 2 + (deltaHue / (hueWeight * SH)) ** 2 + RT * (deltaChromaPrime / (chromaWeight * SC)) * (deltaHue / (hueWeight * SH))
|
|
103
113
|
);
|
|
104
|
-
return
|
|
114
|
+
return deltaE;
|
|
105
115
|
}
|
|
106
116
|
function convertRGBtoXYZ(colour) {
|
|
107
117
|
const { R, G, B } = colour;
|
|
@@ -506,6 +516,14 @@ function getSRGBLuminanceFromHex(colour, standard) {
|
|
|
506
516
|
const rgbColor = convertHextoRGB(colour);
|
|
507
517
|
return getSRGBLuminanceFromRGB(rgbColor, standard);
|
|
508
518
|
}
|
|
519
|
+
function getContrastRatioFromHex(colour1, colour2, standard) {
|
|
520
|
+
const luminance1 = getSRGBLuminanceFromHex(colour1, standard);
|
|
521
|
+
const luminance2 = getSRGBLuminanceFromHex(colour2, standard);
|
|
522
|
+
const lighter = Math.max(luminance1, luminance2);
|
|
523
|
+
const darker = Math.min(luminance1, luminance2);
|
|
524
|
+
const ratio = (lighter + 0.05) / (darker + 0.05);
|
|
525
|
+
return Math.floor(ratio * 1e3) / 1e3;
|
|
526
|
+
}
|
|
509
527
|
function isHexDarkColour(colour, standard) {
|
|
510
528
|
const colourLuminance = getSRGBLuminanceFromHex(colour, standard) + 0.05;
|
|
511
529
|
const whiteContrast = 1.05 / colourLuminance;
|
|
@@ -586,6 +604,7 @@ export {
|
|
|
586
604
|
findNearestHexColour,
|
|
587
605
|
findNearestNamedCSSColour,
|
|
588
606
|
findNearestRGBColour,
|
|
607
|
+
getContrastRatioFromHex,
|
|
589
608
|
getSRGBLuminanceFromHex,
|
|
590
609
|
getSRGBLuminanceFromRGB,
|
|
591
610
|
isCSSNamedDarkColour,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sardine/colour",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "It does things to colours",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -20,8 +20,7 @@
|
|
|
20
20
|
"test": "npx vitest run --coverage",
|
|
21
21
|
"types:emit": "npx tsc -p tsconfig.emit.json",
|
|
22
22
|
"types:check": "npx tsc --noEmit",
|
|
23
|
-
"static": "npx biome ci src"
|
|
24
|
-
"prepare": "husky install"
|
|
23
|
+
"static": "npx biome ci src"
|
|
25
24
|
},
|
|
26
25
|
"repository": {
|
|
27
26
|
"type": "git",
|
|
@@ -47,21 +46,14 @@
|
|
|
47
46
|
"node": ">= 16"
|
|
48
47
|
},
|
|
49
48
|
"devDependencies": {
|
|
50
|
-
"@biomejs/biome": "1.
|
|
49
|
+
"@biomejs/biome": "1.9.4",
|
|
51
50
|
"@changesets/cli": "^2.27.1",
|
|
52
51
|
"@tsconfig/node20": "^20.1.4",
|
|
53
52
|
"@tsconfig/strictest": "^2.0.5",
|
|
54
53
|
"@vitest/coverage-v8": "^2.0.0",
|
|
55
|
-
"
|
|
56
|
-
"lint-staged": "^15.2.2",
|
|
54
|
+
"lefthook": "^1.8.1",
|
|
57
55
|
"typescript": "^5.4.5",
|
|
58
56
|
"vite": "^5.2.11",
|
|
59
57
|
"vitest": "^2.0.0"
|
|
60
|
-
},
|
|
61
|
-
"lint-staged": {
|
|
62
|
-
"*.{js,ts}": [
|
|
63
|
-
"npx @biomejs/biome format --write ./src",
|
|
64
|
-
"npx @biomejs/biome lint --apply ./src"
|
|
65
|
-
]
|
|
66
58
|
}
|
|
67
59
|
}
|