@sardine/colour 2.1.1 → 2.2.1

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.
@@ -1,6 +1,6 @@
1
1
  import type { LabColour } from "./types";
2
2
  /**
3
- * Mesures the colour difference between two colours in the Lab space
3
+ * Measures the colour difference between two colours in the Lab space
4
4
  *
5
5
  * Math taken from:
6
6
  *
@@ -0,0 +1,9 @@
1
+ import type { NamedCSSColour, WCAG } from "./types";
2
+ /**
3
+ * Calculates the contrast ratio between two colours.
4
+ * @param colour1 The first colour
5
+ * @param colour2 The second colour
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 getContrastRatio(colour1: string | NamedCSSColour, colour2: string | NamedCSSColour, standard: WCAG): number;
@@ -0,0 +1,9 @@
1
+ import type { WCAG } from "./types";
2
+ /**
3
+ * Calculates the contrast ratio between two colours in CSSRGB format.
4
+ * @param colour1 The first colour in CSSRGB format
5
+ * @param colour2 The second colour in CSSRGB 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 getContrastRatioFromCSSRGB(colour1: string, colour2: string, standard: WCAG): number;
@@ -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;
@@ -0,0 +1,11 @@
1
+ import type { NamedCSSColour, WCAG } from "./types";
2
+ /**
3
+ * Calculates the contrast ratio between two named CSS colours in.
4
+ *
5
+ * See list here https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
6
+ * @param colour1 The first named CSS colour
7
+ * @param colour2 The second named CSS colour
8
+ * @param standard The standard to evaluate the contrast ratio against, defaults to WCAG2.1
9
+ * @returns The contrast ratio between the two colours truncated to 3 decimal places
10
+ */
11
+ export declare function getContrastRatioFromNamedCSSColour(colour1: NamedCSSColour, colour2: NamedCSSColour, 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 L1 = colour1.L;
72
- const a1 = colour1.a;
73
- const b1 = colour1.b;
74
- const L2 = colour2.L;
75
- const a2 = colour2.a;
76
- const b2 = colour2.b;
77
- const kL = 1;
78
- const kC = 1;
79
- const kH = 1;
80
- const C1 = Math.sqrt(a1 ** 2 + b1 ** 2);
81
- const C2 = Math.sqrt(a2 ** 2 + b2 ** 2);
82
- const ΔL_d = L2 - L1;
83
- const = (C1 + C2) / 2;
84
- const G = 0.5 * (1 - bigSquare());
85
- const a1_d = a1 * (1 + G);
86
- const a2_d = a2 * (1 + G);
87
- const C1_d = Math.sqrt(a1_d ** 2 + b1 ** 2);
88
- const C2_d = Math.sqrt(a2_d ** 2 + b2 ** 2);
89
- const C̅_d = (C1_d + C2_d) / 2;
90
- const ΔC̅_d = C2_d - C1_d;
91
- const h1_d = hue_d(b1, a1_d);
92
- const h2_d = hue_d(b2, a2_d);
93
- const Δh_d = deltaHue_d({ C1, C2, h1_d, h2_d });
94
- const ΔH_d = 2 * Math.sqrt(C1_d * C2_d) * Math.sin(toRadians(Δh_d) / 2);
95
- const H̅_d = meanHue_d({ C1, C2, h1_d, h2_d });
96
- const L̅ = (L1 + L2) / 2;
97
- const T = 1 - 0.17 * Math.cos(toRadians(H̅_d - 30)) + 0.24 * Math.cos(toRadians(2 * H̅_d)) + 0.32 * Math.cos(toRadians(3 * H̅_d + 6)) - 0.2 * Math.cos(toRadians(4 * H̅_d - 63));
98
- const SL = 1 + 0.015 * (L̅ - 50) ** 2 / Math.sqrt(20 + (L̅ - 50) ** 2);
99
- const SC = 0.045 * C̅_d + 1;
100
- const SH = 1 + 0.015 * C̅_d * T;
101
- const rotation = 30 * Math.exp(-(((H̅_d - 275) / 25) ** 2));
102
- const RT = -2 * bigSquare(C̅_d) * Math.sin(toRadians(rotation * 2));
103
- const ΔE = Math.sqrt(
104
- (ΔL_d / (kL * SL)) ** 2 + (ΔC̅_d / (kC * SC)) ** 2 + (ΔH_d / (kH * SH)) ** 2 + RT * (ΔC̅_d / (kC * SC)) * (ΔH_d / (kH * SH))
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 ΔE;
116
+ return deltaE;
107
117
  }
108
118
  function convertRGBtoXYZ(colour) {
109
119
  const { R, G, B } = colour;
@@ -508,6 +518,69 @@ function getSRGBLuminanceFromHex(colour, standard) {
508
518
  const rgbColor = convertHextoRGB(colour);
509
519
  return getSRGBLuminanceFromRGB(rgbColor, standard);
510
520
  }
521
+ function calculateContrastRatio(luminance1, luminance2) {
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
+ }
527
+ function getContrastRatioFromHex(colour1, colour2, standard) {
528
+ const luminance1 = getSRGBLuminanceFromHex(colour1, standard);
529
+ const luminance2 = getSRGBLuminanceFromHex(colour2, standard);
530
+ return calculateContrastRatio(luminance1, luminance2);
531
+ }
532
+ function getContrastRatio(colour1, colour2, standard) {
533
+ let hexColour1;
534
+ let hexColour2;
535
+ if (colour1.startsWith("#")) {
536
+ hexColour1 = colour1;
537
+ } else if (colour1.startsWith("rgb")) {
538
+ hexColour1 = convertCSSRGBtoHex(colour1);
539
+ } else {
540
+ const _hexColour1 = convertNamedCSSColourtoHex(colour1);
541
+ if (_hexColour1 === void 0) {
542
+ throw new Error(
543
+ `getContrastRatio expects valid CSS named colours.
544
+ ${colour1} is not a valid CSS named colour.
545
+ See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color`
546
+ );
547
+ }
548
+ hexColour1 = _hexColour1;
549
+ }
550
+ if (colour2.startsWith("#")) {
551
+ hexColour2 = colour2;
552
+ } else if (colour2.startsWith("rgb")) {
553
+ hexColour2 = convertCSSRGBtoHex(colour2);
554
+ } else {
555
+ const _hexColour2 = convertNamedCSSColourtoHex(colour2);
556
+ if (_hexColour2 === void 0) {
557
+ throw new Error(`getContrastRatio expects valid CSS named colours.
558
+ ${colour2} is not a valid CSS named colour.
559
+ See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color`);
560
+ }
561
+ hexColour2 = _hexColour2;
562
+ }
563
+ return getContrastRatioFromHex(hexColour1, hexColour2, standard);
564
+ }
565
+ function getContrastRatioFromCSSRGB(colour1, colour2, standard) {
566
+ const rgbColour1 = convertCSSRGBtoRGB(colour1);
567
+ const rgbColour2 = convertCSSRGBtoRGB(colour2);
568
+ const luminance1 = getSRGBLuminanceFromRGB(rgbColour1, standard);
569
+ const luminance2 = getSRGBLuminanceFromRGB(rgbColour2, standard);
570
+ return calculateContrastRatio(luminance1, luminance2);
571
+ }
572
+ function getContrastRatioFromNamedCSSColour(colour1, colour2, standard) {
573
+ const hexColour1 = convertNamedCSSColourtoHex(colour1);
574
+ const hexColour2 = convertNamedCSSColourtoHex(colour2);
575
+ if (hexColour1 === void 0 || hexColour2 === void 0) {
576
+ throw new Error(
577
+ `getContrastRatioFromNamedCSSColour expects valid CSS named colours.
578
+ ${colour1} or ${colour2} are not valid CSS named colours.
579
+ See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color`
580
+ );
581
+ }
582
+ return getContrastRatioFromHex(hexColour1, hexColour2, standard);
583
+ }
511
584
  function isHexDarkColour(colour, standard) {
512
585
  const colourLuminance = getSRGBLuminanceFromHex(colour, standard) + 0.05;
513
586
  const whiteContrast = 1.05 / colourLuminance;
@@ -587,6 +660,10 @@ exports.findNearestColour = findNearestColour;
587
660
  exports.findNearestHexColour = findNearestHexColour;
588
661
  exports.findNearestNamedCSSColour = findNearestNamedCSSColour;
589
662
  exports.findNearestRGBColour = findNearestRGBColour;
663
+ exports.getContrastRatio = getContrastRatio;
664
+ exports.getContrastRatioFromCSSRGB = getContrastRatioFromCSSRGB;
665
+ exports.getContrastRatioFromHex = getContrastRatioFromHex;
666
+ exports.getContrastRatioFromNamedCSSColour = getContrastRatioFromNamedCSSColour;
590
667
  exports.getSRGBLuminanceFromHex = getSRGBLuminanceFromHex;
591
668
  exports.getSRGBLuminanceFromRGB = getSRGBLuminanceFromRGB;
592
669
  exports.isCSSNamedDarkColour = isCSSNamedDarkColour;
package/dist/index.d.ts CHANGED
@@ -17,6 +17,10 @@ export { findNearestColour } from "./findNearestColour";
17
17
  export { findNearestHexColour } from "./findNearestHexColour";
18
18
  export { findNearestNamedCSSColour } from "./findNearestNamedCSSColour";
19
19
  export { findNearestRGBColour } from "./findNearestRGBColour";
20
+ export { getContrastRatio } from "./getContrastRatio";
21
+ export { getContrastRatioFromCSSRGB } from "./getContrastRatioFromCSSRGB";
22
+ export { getContrastRatioFromHex } from "./getContrastRatioFromHex";
23
+ export { getContrastRatioFromNamedCSSColour } from "./getContrastRatioFromNamedCSSColour";
20
24
  export { getSRGBLuminanceFromHex } from "./getSRGBLuminanceFromHex";
21
25
  export { getSRGBLuminanceFromRGB } from "./getSRGBLuminanceFromRGB";
22
26
  export { isCSSNamedDarkColour } from "./isCSSNameDarkColour";
package/dist/index.min.js CHANGED
@@ -1 +1,7 @@
1
- "use strict";function x(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 me({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 d=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 Ce(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,f=t.a,s=t.b,c=1,l=1,G=1,m=Math.sqrt(r**2+o**2),g=Math.sqrt(f**2+s**2),se=a-n,ce=(m+g)/2,A=.5*(1-z(ce)),E=r*(1+A),F=f*(1+A),k=Math.sqrt(E**2+o**2),B=Math.sqrt(F**2+s**2),$=(k+B)/2,X=B-k,Y=x(o,E),Z=x(s,F),ie=me({C1:m,C2:g,h1_d:Y,h2_d:Z}),D=2*Math.sqrt(k*B)*Math.sin(d(ie)/2),C=ge({C1:m,C2:g,h1_d:Y,h2_d:Z}),W=(n+a)/2,ue=1-.17*Math.cos(d(C-30))+.24*Math.cos(d(2*C))+.32*Math.cos(d(3*C+6))-.2*Math.cos(d(4*C-63)),le=1+.015*(W-50)**2/Math.sqrt(20+(W-50)**2),T=.045*$+1,P=1+.015*$*ue,de=30*Math.exp(-(((C-275)/25)**2)),be=-2*z($)*Math.sin(d(de*2));return Math.sqrt((se/(c*le))**2+(X/(l*T))**2+(D/(G*P))**2+be*(X/(l*T))*(D/(G*P)))}function O(e){const{R:t,G:n,B:r}=e,o=b(t)*100,a=b(n)*100,f=b(r)*100,s=o*.4124+a*.3576+f*.1805,c=o*.2126+a*.7152+f*.0722,l=o*.0193+a*.1192+f*.9505;return{X:s,Y:c,Z:l}}function U(e){const{X:t,Y:n,Z:r}=e,o=t/95.047,a=n/100,f=r/108.883,s=v(o),c=v(a),l=v(f),G=116*c-16,m=500*(s-c),g=200*(c-l);return{L:G,a:m,b:g}}function y(e){const t=O(e);return U(t)}const V=(e,t)=>{const n=y(e),r=y(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=>Ce(a,0,255).toString(16).padStart(2,"0");return`#${o(e)}${o(t)}${o(n)}${r?o(Math.round(r*255)):""}`}function pe(e){const t=i(e);return p(t)}const w=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 w.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 N(e){return w.get(e)}function h(e){const t=N(e);if(t)return u(t)}function M({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 R(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 he(e,t){if(!t||t.length<2)return e;const n=i(e),r=t.map(a=>i(a)),o=R(n,r);return M(o)}function L(e){return!!e.match(ee)}function ne(e){return!!e.match(J)||!!e.match(K)||!!e.match(_)||!!e.match(Q)}function re(e){return w.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"),L(e)&&(n=i(e),r="cssRGB"),re(e)&&(n=h(e),r="namedCSS"),!n)return;for(const f of t)ne(f)&&o.push(u(f)),L(f)&&o.push(i(f)),re(f)&&o.push(h(f));if(o.length<2)return e;const a=R(n,o);if(r==="hex")return p(a);if(r==="cssRGB")return M(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=R(n,r);return p(o)}function Ge(e,t){if(!t||t.length<2)return e;const n=h(e),o=t.map(f=>h(f)).filter(f=>f!==void 0);if(!n||o.length<2)return e;const a=R(n,o);return H(a)}function q({R:e,G:t,B:n},r){const o=r==="WCAG2.1",a=b(e,o),f=b(t,o),s=b(n,o);return .2126*a+.7152*f+.0722*s}function S(e,t){const n=u(e);return q(n,t)}function I(e,t){const n=S(e,t)+.05,r=1.05/n,o=n/.05;return r>o}function oe(e,t){const n=N(e);if(n)return I(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=q(n,t),o=1.05/r,a=r/.05;return o>a}function ke(e,t){try{return e.startsWith("#")?I(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 fe=(e,t)=>e>t?e/t:t/e,Be=({backgroundColour:e,optionOneColour:t,optionTwoColour:n},r)=>{const o=S(e,r)+.05,a=S(t,r)+.05,f=S(n,r)+.05,s=fe(a,o),c=fe(f,o);return s>c?t:n};export{V as RGBdistance,j as ciede2000,pe as convertCSSRGBtoHex,i as convertCSSRGBtoRGB,te as convertHextoNamedCSSColour,u as convertHextoRGB,N as convertNamedCSSColourtoHex,h as convertNamedCSSColourtoRGB,M as convertRGBtoCSSRGB,p as convertRGBtoHex,y as convertRGBtoLab,H as convertRGBtoNamedCSSColour,O as convertRGBtoXYZ,U as convertXYZtoLab,he as findNearestCSSRGBColour,Re as findNearestColour,Se as findNearestHexColour,Ge as findNearestNamedCSSColour,R as findNearestRGBColour,S as getSRGBLuminanceFromHex,q as getSRGBLuminanceFromRGB,oe as isCSSNamedDarkColour,L as isCSSRGBColour,ae as isCSSRGBDarkColour,ke as isDarkColour,I as isHexDarkColour,Be as pickHexColourContrast};
1
+ "use strict";function U(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 he({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 Se({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 b=e=>e*(Math.PI/180),j=e=>Math.sqrt(e**7/(e**7+25**7));function g(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 y(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 pe(e,t,n){return Math.min(Math.max(e,t),n)}function O(e,t){const n=e.L,r=e.a,o=e.b,a=t.L,s=t.a,f=t.b,c=1,m=1,$=1,C=Math.sqrt(r**2+o**2),h=Math.sqrt(s**2+f**2),ue=a-n,le=(C+h)/2,P=.5*(1-j(le)),F=r*(1+P),A=s*(1+P),B=Math.sqrt(F**2+o**2),k=Math.sqrt(A**2+f**2),w=(B+k)/2,E=k-B,Y=U(o,F),X=U(f,A),de=he({C1:C,C2:h,h1_d:Y,h2_d:X}),Z=2*Math.sqrt(B*k)*Math.sin(b(de)/2),S=Se({C1:C,C2:h,h1_d:Y,h2_d:X}),D=(n+a)/2,me=1-.17*Math.cos(b(S-30))+.24*Math.cos(b(2*S))+.32*Math.cos(b(3*S+6))-.2*Math.cos(b(4*S-63)),be=1+.015*(D-50)**2/Math.sqrt(20+(D-50)**2),z=.045*w+1,T=1+.015*w*me,ge=30*Math.exp(-(((S-275)/25)**2)),Ce=-2*j(w)*Math.sin(b(ge*2));return Math.sqrt((ue/(c*be))**2+(E/(m*z))**2+(Z/($*T))**2+Ce*(E/(m*z))*(Z/($*T)))}function V(e){const{R:t,G:n,B:r}=e,o=g(t)*100,a=g(n)*100,s=g(r)*100,f=o*.4124+a*.3576+s*.1805,c=o*.2126+a*.7152+s*.0722,m=o*.0193+a*.1192+s*.9505;return{X:f,Y:c,Z:m}}function J(e){const{X:t,Y:n,Z:r}=e,o=t/95.047,a=n/100,s=r/108.883,f=y(o),c=y(a),m=y(s),$=116*c-16,C=500*(f-c),h=200*(c-m);return{L:$,a:C,b:h}}function N(e){const t=V(e);return J(t)}const K=(e,t)=>{const n=N(e),r=N(t);return O(n,r)},Q=/^#[a-fA-F0-9]{6}$/,_=/^#[a-fA-F0-9]{8}$/,ee=/^#[a-fA-F0-9]{3}$/,te=/^#[a-fA-F0-9]{4}$/,ne=/^rgba*\(\s*([-+]?\d+)\s*(?:,)?\s*([-+]?\d+)\s*(?:,)?\s*([-+]?\d+)\s*(?:,*|\/*)\s*([-+]?\d*[.]?\d+[%]?)*\)$/i;function i(e){const t=e.match(ne);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=>pe(a,0,255).toString(16).padStart(2,"0");return`#${o(e)}${o(t)}${o(n)}${r?o(Math.round(r*255)):""}`}function M(e){const t=i(e);return p(t)}const H=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 re(e){for(const[t,n]of H.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(Q))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(ee))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(_))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(te))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 l(e){return H.get(e)}function R(e){const t=l(e);if(t)return u(t)}function q({R:e,G:t,B:n,A:r}){return`rgb(${e} ${t} ${n}${r?` / ${r}`:""})`}function I(e){const t=p(e);return re(t)}function v(e,t){if(!t||t.length<2)return e;const n=[];for(const o of t){const a=K(e,o);n.push([o,a])}return n.sort((o,a)=>o[1]-a[1])[0][0]}function Re(e,t){if(!t||t.length<2)return e;const n=i(e),r=t.map(a=>i(a)),o=v(n,r);return q(o)}function L(e){return!!e.match(ne)}function oe(e){return!!e.match(Q)||!!e.match(_)||!!e.match(te)||!!e.match(ee)}function ae(e){return H.has(e)}function ve(e,t){if(!t||t.length<2)return e;let n,r;const o=[];if(oe(e)&&(n=u(e),r="hex"),L(e)&&(n=i(e),r="cssRGB"),ae(e)&&(n=R(e),r="namedCSS"),!n)return;for(const s of t)oe(s)&&o.push(u(s)),L(s)&&o.push(i(s)),ae(s)&&o.push(R(s));if(o.length<2)return e;const a=v(n,o);if(r==="hex")return p(a);if(r==="cssRGB")return q(a);if(r==="namedCSS")return I(a)}function Ge(e,t){if(!t||t.length<2)return e;const n=u(e),r=t.map(a=>u(a)),o=v(n,r);return p(o)}function $e(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=v(n,o);return I(a)}function G({R:e,G:t,B:n},r){const o=r==="WCAG2.1",a=g(e,o),s=g(t,o),f=g(n,o);return .2126*a+.7152*s+.0722*f}function d(e,t){const n=u(e);return G(n,t)}function se(e,t){const n=Math.max(e,t),r=Math.min(e,t),o=(n+.05)/(r+.05);return Math.floor(o*1e3)/1e3}function W(e,t,n){const r=d(e,n),o=d(t,n);return se(r,o)}function Be(e,t,n){let r,o;if(e.startsWith("#"))r=e;else if(e.startsWith("rgb"))r=M(e);else{const a=l(e);if(a===void 0)throw new Error(`getContrastRatio expects valid CSS named colours.
2
+ ${e} is not a valid CSS named colour.
3
+ See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color`);r=a}if(t.startsWith("#"))o=t;else if(t.startsWith("rgb"))o=M(t);else{const a=l(t);if(a===void 0)throw new Error(`getContrastRatio expects valid CSS named colours.
4
+ ${t} is not a valid CSS named colour.
5
+ See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color`);o=a}return W(r,o,n)}function ke(e,t,n){const r=i(e),o=i(t),a=G(r,n),s=G(o,n);return se(a,s)}function we(e,t,n){const r=l(e),o=l(t);if(r===void 0||o===void 0)throw new Error(`getContrastRatioFromNamedCSSColour expects valid CSS named colours.
6
+ ${e} or ${t} are not valid CSS named colours.
7
+ See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color`);return W(r,o,n)}function x(e,t){const n=d(e,t)+.05,r=1.05/n,o=n/.05;return r>o}function fe(e,t){const n=l(e);if(n)return x(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 ce(e,t){const n=i(e),r=G(n,t),o=1.05/r,a=r/.05;return o>a}function ye(e,t){try{return e.startsWith("#")?x(e,t):e.startsWith("rgb")?ce(e,t):fe(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 ie=(e,t)=>e>t?e/t:t/e,Ne=({backgroundColour:e,optionOneColour:t,optionTwoColour:n},r)=>{const o=d(e,r)+.05,a=d(t,r)+.05,s=d(n,r)+.05,f=ie(a,o),c=ie(s,o);return f>c?t:n};export{K as RGBdistance,O as ciede2000,M as convertCSSRGBtoHex,i as convertCSSRGBtoRGB,re as convertHextoNamedCSSColour,u as convertHextoRGB,l as convertNamedCSSColourtoHex,R as convertNamedCSSColourtoRGB,q as convertRGBtoCSSRGB,p as convertRGBtoHex,N as convertRGBtoLab,I as convertRGBtoNamedCSSColour,V as convertRGBtoXYZ,J as convertXYZtoLab,Re as findNearestCSSRGBColour,ve as findNearestColour,Ge as findNearestHexColour,$e as findNearestNamedCSSColour,v as findNearestRGBColour,Be as getContrastRatio,ke as getContrastRatioFromCSSRGB,W as getContrastRatioFromHex,we as getContrastRatioFromNamedCSSColour,d as getSRGBLuminanceFromHex,G as getSRGBLuminanceFromRGB,fe as isCSSNamedDarkColour,L as isCSSRGBColour,ce as isCSSRGBDarkColour,ye as isDarkColour,x as isHexDarkColour,Ne 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 L1 = colour1.L;
70
- const a1 = colour1.a;
71
- const b1 = colour1.b;
72
- const L2 = colour2.L;
73
- const a2 = colour2.a;
74
- const b2 = colour2.b;
75
- const kL = 1;
76
- const kC = 1;
77
- const kH = 1;
78
- const C1 = Math.sqrt(a1 ** 2 + b1 ** 2);
79
- const C2 = Math.sqrt(a2 ** 2 + b2 ** 2);
80
- const ΔL_d = L2 - L1;
81
- const = (C1 + C2) / 2;
82
- const G = 0.5 * (1 - bigSquare());
83
- const a1_d = a1 * (1 + G);
84
- const a2_d = a2 * (1 + G);
85
- const C1_d = Math.sqrt(a1_d ** 2 + b1 ** 2);
86
- const C2_d = Math.sqrt(a2_d ** 2 + b2 ** 2);
87
- const C̅_d = (C1_d + C2_d) / 2;
88
- const ΔC̅_d = C2_d - C1_d;
89
- const h1_d = hue_d(b1, a1_d);
90
- const h2_d = hue_d(b2, a2_d);
91
- const Δh_d = deltaHue_d({ C1, C2, h1_d, h2_d });
92
- const ΔH_d = 2 * Math.sqrt(C1_d * C2_d) * Math.sin(toRadians(Δh_d) / 2);
93
- const H̅_d = meanHue_d({ C1, C2, h1_d, h2_d });
94
- const L̅ = (L1 + L2) / 2;
95
- const T = 1 - 0.17 * Math.cos(toRadians(H̅_d - 30)) + 0.24 * Math.cos(toRadians(2 * H̅_d)) + 0.32 * Math.cos(toRadians(3 * H̅_d + 6)) - 0.2 * Math.cos(toRadians(4 * H̅_d - 63));
96
- const SL = 1 + 0.015 * (L̅ - 50) ** 2 / Math.sqrt(20 + (L̅ - 50) ** 2);
97
- const SC = 0.045 * C̅_d + 1;
98
- const SH = 1 + 0.015 * C̅_d * T;
99
- const rotation = 30 * Math.exp(-(((H̅_d - 275) / 25) ** 2));
100
- const RT = -2 * bigSquare(C̅_d) * Math.sin(toRadians(rotation * 2));
101
- const ΔE = Math.sqrt(
102
- (ΔL_d / (kL * SL)) ** 2 + (ΔC̅_d / (kC * SC)) ** 2 + (ΔH_d / (kH * SH)) ** 2 + RT * (ΔC̅_d / (kC * SC)) * (ΔH_d / (kH * SH))
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 ΔE;
114
+ return deltaE;
105
115
  }
106
116
  function convertRGBtoXYZ(colour) {
107
117
  const { R, G, B } = colour;
@@ -506,6 +516,69 @@ function getSRGBLuminanceFromHex(colour, standard) {
506
516
  const rgbColor = convertHextoRGB(colour);
507
517
  return getSRGBLuminanceFromRGB(rgbColor, standard);
508
518
  }
519
+ function calculateContrastRatio(luminance1, luminance2) {
520
+ const lighter = Math.max(luminance1, luminance2);
521
+ const darker = Math.min(luminance1, luminance2);
522
+ const ratio = (lighter + 0.05) / (darker + 0.05);
523
+ return Math.floor(ratio * 1e3) / 1e3;
524
+ }
525
+ function getContrastRatioFromHex(colour1, colour2, standard) {
526
+ const luminance1 = getSRGBLuminanceFromHex(colour1, standard);
527
+ const luminance2 = getSRGBLuminanceFromHex(colour2, standard);
528
+ return calculateContrastRatio(luminance1, luminance2);
529
+ }
530
+ function getContrastRatio(colour1, colour2, standard) {
531
+ let hexColour1;
532
+ let hexColour2;
533
+ if (colour1.startsWith("#")) {
534
+ hexColour1 = colour1;
535
+ } else if (colour1.startsWith("rgb")) {
536
+ hexColour1 = convertCSSRGBtoHex(colour1);
537
+ } else {
538
+ const _hexColour1 = convertNamedCSSColourtoHex(colour1);
539
+ if (_hexColour1 === void 0) {
540
+ throw new Error(
541
+ `getContrastRatio expects valid CSS named colours.
542
+ ${colour1} is not a valid CSS named colour.
543
+ See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color`
544
+ );
545
+ }
546
+ hexColour1 = _hexColour1;
547
+ }
548
+ if (colour2.startsWith("#")) {
549
+ hexColour2 = colour2;
550
+ } else if (colour2.startsWith("rgb")) {
551
+ hexColour2 = convertCSSRGBtoHex(colour2);
552
+ } else {
553
+ const _hexColour2 = convertNamedCSSColourtoHex(colour2);
554
+ if (_hexColour2 === void 0) {
555
+ throw new Error(`getContrastRatio expects valid CSS named colours.
556
+ ${colour2} is not a valid CSS named colour.
557
+ See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color`);
558
+ }
559
+ hexColour2 = _hexColour2;
560
+ }
561
+ return getContrastRatioFromHex(hexColour1, hexColour2, standard);
562
+ }
563
+ function getContrastRatioFromCSSRGB(colour1, colour2, standard) {
564
+ const rgbColour1 = convertCSSRGBtoRGB(colour1);
565
+ const rgbColour2 = convertCSSRGBtoRGB(colour2);
566
+ const luminance1 = getSRGBLuminanceFromRGB(rgbColour1, standard);
567
+ const luminance2 = getSRGBLuminanceFromRGB(rgbColour2, standard);
568
+ return calculateContrastRatio(luminance1, luminance2);
569
+ }
570
+ function getContrastRatioFromNamedCSSColour(colour1, colour2, standard) {
571
+ const hexColour1 = convertNamedCSSColourtoHex(colour1);
572
+ const hexColour2 = convertNamedCSSColourtoHex(colour2);
573
+ if (hexColour1 === void 0 || hexColour2 === void 0) {
574
+ throw new Error(
575
+ `getContrastRatioFromNamedCSSColour expects valid CSS named colours.
576
+ ${colour1} or ${colour2} are not valid CSS named colours.
577
+ See https://developer.mozilla.org/en-US/docs/Web/CSS/named-color`
578
+ );
579
+ }
580
+ return getContrastRatioFromHex(hexColour1, hexColour2, standard);
581
+ }
509
582
  function isHexDarkColour(colour, standard) {
510
583
  const colourLuminance = getSRGBLuminanceFromHex(colour, standard) + 0.05;
511
584
  const whiteContrast = 1.05 / colourLuminance;
@@ -586,6 +659,10 @@ export {
586
659
  findNearestHexColour,
587
660
  findNearestNamedCSSColour,
588
661
  findNearestRGBColour,
662
+ getContrastRatio,
663
+ getContrastRatioFromCSSRGB,
664
+ getContrastRatioFromHex,
665
+ getContrastRatioFromNamedCSSColour,
589
666
  getSRGBLuminanceFromHex,
590
667
  getSRGBLuminanceFromRGB,
591
668
  isCSSNamedDarkColour,
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Calculate the contrast ratio between two colours
3
+ * @param luminance1 The luminance of the first colour
4
+ * @param luminance2 The luminance of the second colour
5
+ * @returns The contrast ratio between the two colours truncated to 3 decimal places
6
+ */
7
+ export declare function calculateContrastRatio(luminance1: number, luminance2: number): number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sardine/colour",
3
- "version": "2.1.1",
3
+ "version": "2.2.1",
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.8.3",
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
- "husky": "^9.0.11",
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
  }