chromametry 0.1.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/README.md +156 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +145 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.global.js +1 -0
- package/dist/index.js +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Chromametry
|
|
2
|
+
> A metrics framework for evaluating web-accessible sequential color palettes.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
*Figure 1. Web-accessible Palette Ranking*
|
|
7
|
+
|
|
8
|
+
## Metric Definitions
|
|
9
|
+
|
|
10
|
+
1. **Contrast Efficiency:** Measures how efficiently contrast space is used to achieve WCAG 4.5:1 contrast.
|
|
11
|
+
2. **Lightness Linearity:** Evaluates the linearity of lightness (with Helmholtz–Kohlrausch correction).
|
|
12
|
+
3. **Chroma Smoothness:** Checks for artifacts and kinks in the saturation curve using Monotone Cubic Splines.
|
|
13
|
+
4. **Hue Stability:** Quantifies hue shift/drift across the lightness ramp.
|
|
14
|
+
5. **Spacing Uniformity:** Measures the consistency of color spacing (DeltaE 2000).
|
|
15
|
+
>[Read full Methodology](https://github.com/chromametry/chromametry.git)
|
|
16
|
+
|
|
17
|
+
> All lightness, chroma, hue, and DeltaE2000 computations are performed in the CIELAB color space.
|
|
18
|
+
|
|
19
|
+
## Benchmark Rankings
|
|
20
|
+
|
|
21
|
+
Comparison of popular design systems based on Chromametry metrics.
|
|
22
|
+
|
|
23
|
+
| Rank | Design System | Steps | Span (K) | Contrast Efficiency | Lightness Linearity | Chroma Smoothness | Hue Stability | Spacing Uniformity | **SCORE** |
|
|
24
|
+
| :---: | :------------------------- | :---: | :--------: | :-----------------: | :-----------------: | :---------------: | :-----------: | :----------------: | :-------: |
|
|
25
|
+
| 🥇 | **IBM Carbon** | 12 | 6 | 0.906 | 0.932 | 0.864 | 0.930 | 0.794 | **88.36** |
|
|
26
|
+
| 🥈 | **Adobe Spectrum** | 18 | 9 | 0.929 | 0.935 | 0.871 | 0.917 | 0.775 | **88.33** |
|
|
27
|
+
| 🥉 | **U.S. Web Design System** | 12 | 6 | 0.906 | 0.936 | 0.805 | 0.942 | 0.801 | **87.58** |
|
|
28
|
+
| 4 | Salesforce Lightning 2 | 14 | 7 | 0.916 | 0.921 | 0.839 | 0.938 | 0.712 | **86.06** |
|
|
29
|
+
| 5 | GitHub Primer Brand | 12 | 6 | 0.906 | 0.926 | 0.838 | 0.946 | 0.685 | **85.43** |
|
|
30
|
+
| 6 | Atlassian | 14 | 8 | 0.785 | 0.897 | 0.914 | 0.950 | 0.714 | **84.72** |
|
|
31
|
+
| 7 | Tailwind CSS | 13 | 8 | 0.774 | 0.873 | 0.849 | 0.918 | 0.677 | **81.37** |
|
|
32
|
+
| 8 | Ant Design | 12 | 9 | 0.698 | 0.860 | 0.861 | 0.913 | 0.656 | **79.08** |
|
|
33
|
+
| 9 | Material UI | 12 | 11 | 0.554 | 0.797 | 0.773 | 0.931 | 0.550 | **70.56** |
|
|
34
|
+
| 10 | Radix UI | 13 | 10 | 0.533 | 0.800 | 0.745 | 0.947 | 0.520 | **68.98** |
|
|
35
|
+
| 11 | Shopify Polaris | 17 | 15 | 0.349 | 0.730 | 0.642 | 0.920 | 0.467 | **58.78** |
|
|
36
|
+
|
|
37
|
+
*Table 1. Benchmark ranking of design systems evaluated using Chromametry metrics.*
|
|
38
|
+
|
|
39
|
+
> **Note:** Design systems like Bootstrap,Google Material 3, Apple Human Interface or Fluent UI are excluded as they define discrete semantic tokens rather than algorithmic sequential ramps.
|
|
40
|
+
|
|
41
|
+
> **Overall Score** is computed as the weighted mean of the five normalized metrics, using equal weights by default.
|
|
42
|
+
|
|
43
|
+
### Example: A Typical Report
|
|
44
|
+
|
|
45
|
+

|
|
46
|
+
*Figure 2. IBM Carbon Color Palette.*
|
|
47
|
+
|
|
48
|
+

|
|
49
|
+
*Figure 3. IBM Carbon Palette Metrics.*
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+

|
|
53
|
+
*Figure 4. IBM Carbon Palette Charts.*
|
|
54
|
+
|
|
55
|
+
## Benchmark result page
|
|
56
|
+
|
|
57
|
+
- Online Report : [Benchmark page](https://github.com/chromametry/benchmarks/monochromatic)
|
|
58
|
+
- Local `/benchmarks/monochromatic/output/index.html` (double click)
|
|
59
|
+
|
|
60
|
+
## Analyze Palettes
|
|
61
|
+
### Installation
|
|
62
|
+
**NPM Package:**
|
|
63
|
+
```bash
|
|
64
|
+
npm install chromametry
|
|
65
|
+
```
|
|
66
|
+
**CDN (Browser) ESM:**
|
|
67
|
+
```js
|
|
68
|
+
<script type="module">
|
|
69
|
+
import { analyzeMonochromaticPalette } from 'https://esm.sh/chromametry';
|
|
70
|
+
</script>
|
|
71
|
+
const result = Chromametry.analyzeMonochromaticPalette({ ... });
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**CDN (Browser) Global:**
|
|
77
|
+
```js
|
|
78
|
+
<script src="https://unpkg.com/chromametry/dist/index.global.js"></script>
|
|
79
|
+
<script>
|
|
80
|
+
const result = Chromametry.analyzeMonochromaticPalette({ ... });
|
|
81
|
+
</script>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Usage
|
|
85
|
+
```ts
|
|
86
|
+
import { analyzeMonochromaticPalette } from 'chromametry';
|
|
87
|
+
|
|
88
|
+
// 1. Define your colors (must include white/black anchors for accurate scoring)
|
|
89
|
+
const colors = {
|
|
90
|
+
yellow: ["#ffffff","#fcf4d6","#fddc69","#f1c21b","#d2a106","#b28600","#8e6a00","#684e00","#483700","#302400","#1c1500","#000000"],
|
|
91
|
+
orange: ["#ffffff","#fff2e8","#ffd9be","#ffb784","#ff832b","#eb6200","#ba4e00","#8a3800","#5e2900","#3e1a00","#231000","#000000"]
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// 2. Define step names corresponding to the array length
|
|
95
|
+
const stepNames = [0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950,1000];
|
|
96
|
+
|
|
97
|
+
// 3. Run analysis
|
|
98
|
+
const result = analyzeMonochromaticPalette({
|
|
99
|
+
name: "My Custom Brand",
|
|
100
|
+
stepNames: stepNames,
|
|
101
|
+
colors: colors
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
console.log(result.metrics);
|
|
105
|
+
console.log(`System Score: ${result.score}`);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Reproducing Benchmarks
|
|
109
|
+
To run the benchmark generator locally:
|
|
110
|
+
```bash
|
|
111
|
+
git clone https://github.com/chromametry/chromametry.git
|
|
112
|
+
cd chromametry
|
|
113
|
+
npm install
|
|
114
|
+
npm run generate
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Adding Custom Palettes to Benchmark
|
|
118
|
+
Create a new .ts file in benchmarks/monochromatic/input/ (e.g., my-palette.ts).
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { MonochromaticPaletteData } from "../../../src/index.js";
|
|
122
|
+
import { red, volcano, gold } from '@ant-design/colors';
|
|
123
|
+
|
|
124
|
+
// Define colors (imported or inline object)
|
|
125
|
+
let colors: Record<string, string[]> = { red, volcano, gold };
|
|
126
|
+
|
|
127
|
+
// Ensure white/black anchors exist if your ramp misses them
|
|
128
|
+
for (let name in colors) {
|
|
129
|
+
if (colors[name][0] !== "#ffffff") colors[name].unshift("#ffffff");
|
|
130
|
+
if (colors[name][colors[name].length - 1] !== "#000000") colors[name].push("#000000");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const stepNames = Object.keys(Object.values(colors)[0]);
|
|
134
|
+
|
|
135
|
+
const palette: MonochromaticPaletteData = {
|
|
136
|
+
name: "Ant Design",
|
|
137
|
+
stepNames, // string[]
|
|
138
|
+
colors // Record<string, string[]>
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export default palette;
|
|
142
|
+
```
|
|
143
|
+
Then regenerate the report:
|
|
144
|
+
```bash
|
|
145
|
+
npm run generate
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Input Requirements
|
|
149
|
+
- Equal Steps: All color ramps must have the same number of steps.
|
|
150
|
+
|
|
151
|
+
- Format: Colors must be Hex strings.
|
|
152
|
+
|
|
153
|
+
- Monotonicity: Lightness must strictly increase or decrease (sorted).
|
|
154
|
+
|
|
155
|
+
- Anchors: Start/End colors should ideally be Black and White.
|
|
156
|
+
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var P=Object.defineProperty;var ot=Object.getOwnPropertyDescriptor;var at=Object.getOwnPropertyNames;var st=Object.prototype.hasOwnProperty;var ct=(r,t)=>{for(var n in t)P(r,n,{get:t[n],enumerable:!0})},mt=(r,t,n,e)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of at(t))!st.call(r,o)&&o!==n&&P(r,o,{get:()=>t[o],enumerable:!(e=ot(t,o))||e.enumerable});return r};var ht=r=>mt(P({},"__esModule",{value:!0}),r);var dt={};ct(dt,{analyzeMonochromatic:()=>F,analyzeMonochromaticPalette:()=>xt,calcChromaSmoothness:()=>J,calcContrastEfficiency:()=>V,calcDeltaE2000:()=>k,calcHueStability:()=>K,calcLightnessLinearity:()=>W,calcScore:()=>I,calcSpacingUniformity:()=>Q,calcStatistics:()=>ft,contrastList:()=>H,createMonotone:()=>B,cssRgbToRgb:()=>gt,findMaxChromaHex:()=>v,fromLightnessEAL:()=>ut,getApcaContrast:()=>L,getMonochromaticContrasts:()=>N,getPaletteContrasts:()=>z,getRelativeLuminance:()=>D,getWcagContrast:()=>R,hexToRgb:()=>f,labToLch:()=>q,labToRgb:()=>lt,lchToLab:()=>bt,lrgbToSrgb:()=>X,rgbToHex:()=>it,rgbToLab:()=>x,rootMeanSquare:()=>y,simulateDeuteranopia:()=>Mt,simulateProtanopia:()=>pt,srgbToLrgb:()=>Y,toLightnessEAL:()=>T,unwrapHue:()=>A});module.exports=ht(dt);var D=r=>{let[t,n,e]=r;return .2126*t+.7152*n+.0722*e},X=r=>{let t=n=>{let e=Math.max(0,Math.min(1,n)),o=e<=.0031308?12.92*e:1.055*Math.pow(e,1/2.4)-.055;return Math.max(0,Math.min(255,Math.round(o*255)))};return r.map(t)},Y=r=>{let t=n=>n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92;return r.map(t)},it=r=>{let[t,n,e]=X(r);return t=t.toString(16).padStart(2,"0"),n=n.toString(16).padStart(2,"0"),e=e.toString(16).padStart(2,"0"),`#${t}${n}${e}`},f=r=>{let t=parseInt(r.slice(1,3),16)/255,n=parseInt(r.slice(3,5),16)/255,e=parseInt(r.slice(5,7),16)/255;return Y([t,n,e])},T=r=>{let[t,n,e]=r,o=Math.sqrt(n*n+e*e),c=(Math.atan2(e,n)*180/Math.PI+360)%360,i=.1644,b=.0603,a=.1307,u=.006,h=i*Math.abs(Math.sin((c-90)/2*(Math.PI/180)))+b,m=0;return(c<=90||c>=270)&&(m=a*Math.abs(Math.cos(c*(Math.PI/180)))+u),t+(h+m)*o},ut=(r,t)=>{let[,n,e]=t,o=Math.sqrt(n*n+e*e),c=(Math.atan2(e,n)*180/Math.PI+360)%360,i=.1644,b=.0603,a=.1307,u=.006,h=i*Math.abs(Math.sin((c-90)/2*(Math.PI/180)))+b,m=0;return(c<=90||c>=270)&&(m=a*Math.abs(Math.cos(c*(Math.PI/180)))+u),Math.max(0,r-(h+m)*o)},bt=r=>{let[t,n,e]=r,o=e*Math.PI/180;return[t,n*Math.cos(o),n*Math.sin(o)]},x=r=>{let[t,n,e]=r,o=[[1.0479298,.0229469,-.0501922],[.0296278,.9904345,-.0170738],[.0296278,.9904345,-.0170738]],s=.4124564*t+.3575761*n+.1804375*e,c=.2126729*t+.7151522*n+.072175*e,i=.0193339*t+.119192*n+.9503041*e,b=1.0479298*s+.0229469*c-.0501922*i,a=.0296278*s+.9904345*c-.0170738*i,u=-.009243*s+.0150552*c+.7518743*i,h=M=>M>.008856?Math.cbrt(M):7.787*M+16/116,m=h(b/.96422),l=h(a/1),p=h(u/.82521);return[116*l-16,500*(m-l),200*(l-p)]},q=r=>{let[t,n,e]=r,o=Math.sqrt(n*n+e*e);if(o<1e-4)return[t,0,0];let c=(Math.atan2(e,n)*180/Math.PI+360)%360;return c>=359.9999&&(c=0),[t,o,c]},lt=r=>{let[t,n,e]=r,o=(t+16)/116,s=n/500+o,c=o-e/200,i=p=>p>.2068965?p**3:(p-16/116)/7.787,b=i(s)*.96422,a=i(o)*1,u=i(c)*.82521,h=.9555766*b-.0230393*a+.0631636*u,m=-.0282895*b+1.0099416*a+.0210077*u,l=.0122982*b-.020483*a+1.3299098*u;return[3.2404542*h-1.5371385*m-.4985314*l,-.969266*h+1.8760108*m+.041556*l,.0556434*h-.2040259*m+1.0572252*l]},k=(r,t)=>{let[n,e,o]=r,[s,c,i]=t,b=(n+s)/2,a=Math.sqrt(e*e+o*o),u=Math.sqrt(c*c+i*i),h=(a+u)/2,m=.5*(1-Math.sqrt(Math.pow(h,7)/(Math.pow(h,7)+Math.pow(25,7)))),l=e*(1+m),p=c*(1+m),M=Math.sqrt(l*l+o*o),d=Math.sqrt(p*p+i*i),S=(M+d)/2,C=Math.atan2(o,l)*180/Math.PI+(Math.atan2(o,l)<0?360:0),g=Math.atan2(i,p)*180/Math.PI+(Math.atan2(i,p)<0?360:0),E=g-C;Math.abs(E)>180&&(E+=g<=C?360:-360);let w=Math.abs(C-g)>180?(C+g+360)/2:(C+g)/2,Z=1-.17*Math.cos((w-30)*Math.PI/180)+.24*Math.cos(2*w*Math.PI/180)+.32*Math.cos((3*w+6)*Math.PI/180)-.2*Math.cos((4*w-63)*Math.PI/180),tt=s-n,U=d-M,$=2*Math.sqrt(M*d)*Math.sin(E/2*Math.PI/180),nt=1+.015*Math.pow(b-50,2)/Math.sqrt(20+Math.pow(b-50,2)),G=1+.045*S,O=1+.015*S*Z,rt=30*Math.exp(-Math.pow((w-275)/25,2)),et=-(2*Math.sqrt(Math.pow(S,7)/(Math.pow(S,7)+Math.pow(25,7))))*Math.sin(2*rt*Math.PI/180);return Math.sqrt(Math.pow(tt/nt,2)+Math.pow(U/G,2)+Math.pow($/O,2)+et*(U/G)*($/O))},v=r=>{let t="",n=-1/0;for(let e of r){let o=x(f(e)),[,s]=q(o);s>n&&(n=s,t=e)}return t};function A(r){let t=[r[0]];for(let n=1;n<r.length;n++){let e=r[n]-r[n-1];e>180?e-=360:e<-180&&(e+=360),t.push(t[n-1]+e)}return t}function pt(r){let[t,n,e]=r;return[.152286*t+1.052583*n-.204868*e,.114503*t+.786281*n+.099216*e,-.003882*t-.048116*n+1.051998*e]}function Mt(r){let[t,n,e]=r;return[.367322*t+.860646*n-.227968*e,.280085*t+.672501*n+.047413*e,-.01182*t+.04294*n+.968881*e]}var gt=r=>{let t=r.match(/\d+(\.\d+)?/g);if(!t||t.length<3)throw new Error("Invalid CSS rgb()");let n=e=>{let o=e/255;return o<=.04045?o/12.92:Math.pow((o+.055)/1.055,2.4)};return[n(Number(t[0])),n(Number(t[1])),n(Number(t[2]))]};var R=(r,t)=>(Math.max(r,t)+.05)/(Math.min(r,t)+.05),L=(r,t)=>{let n=c=>c>5e-4?c:c+Math.pow(5e-4-c,.8),e=n(r),o=n(t),s=(Math.pow(e,.56)-Math.pow(o,.56))*100;return Math.abs(s)<.1?0:(s=s>0?s<1?0:s-.25:s>-1?0:s+.25,Math.round(s))};function _(r){let t=r.match(/^(wcag|apca)(\d+)$/);if(!t)throw new Error(`Invalid contrast: ${r}`);let n=t[1]==="wcag"?Number(t[2])/10:Number(t[2]);return{system:t[1],target:n}}var N=r=>{let t={},n=r.length,e=n-1;return H.forEach(o=>{let{system:s,target:c}=_(o),i=e,b=0;for(let a=1;a<n;a++){let u=1/0;for(let h=0;h<n-a;h++){let m=s==="wcag"?R(r[h].luminance,r[h+a].luminance):Math.max(Math.abs(L(r[h+a].luminance,r[h].luminance)),Math.abs(L(r[h].luminance,r[h+a].luminance)));m<u&&(u=m)}if(u>=c){i=a,b=u;break}a===e&&(b=u)}t[o]={system:s,efficiency:i/e,target:c,span:i,value:b,name:o}}),t},H=["wcag30","wcag45","wcag70","apca45","apca60","apca75"],z=r=>{let t={};return H.forEach(n=>{var a;let{target:e,system:o}=_(n),s=r.map(u=>u.contrasts[n]),c=((a=r[0])==null?void 0:a.shades.length)||0,i=Math.max(...s.map(u=>(u==null?void 0:u.span)||0)),b=s.reduce((u,h)=>u+((h==null?void 0:h.value)||0),0);t[n]={system:o,target:e,span:i,value:b/(r.length||1),name:n,efficiency:i/(c-1||1)}}),t};var j=r=>{let t=f(r),n=x(t),e=D(t),o=T(n),s=q(n);return{hex:r,rgb:t,lab:n,lch:s,lightness:o,chroma:s[1],hue:s[2],luminance:e,parameter:0,cumDeltaE00:0,cumProtDeltaE00:0,cumDeutDeltaE00:0,wcag:R(e,1),apca:L(e,1)}};var B=r=>{if(r.length<1)return a=>0;let t=[...r].sort((a,u)=>a[0]-u[0]),n=[];for(let a=0;a<t.length;a++)(a===0||t[a][0]!==t[a-1][0])&&n.push(t[a]);let e=n.length;if(e===1)return a=>n[0][1];let o=n.map(a=>a[0]),s=n.map(a=>a[1]),c=[],i=[];for(let a=0;a<e-1;a++)c[a]=o[a+1]-o[a],i[a]=(s[a+1]-s[a])/c[a];let b=new Array(e);b[0]=i[0],b[e-1]=i[e-2];for(let a=1;a<e-1;a++){let u=i[a-1],h=i[a];if(u*h<=0)b[a]=0;else{let m=(1+c[a]/(c[a-1]+c[a]))/3;b[a]=u*h/((1-m)*u+m*h)}}return a=>{if(a<=o[0])return s[0];if(a>=o[e-1])return s[e-1];let u=0,h=e-2,m=0;for(;u<=h;){let g=Math.floor((u+h)/2);if(a>=o[g]&&a<=o[g+1]){m=g;break}a<o[g]?h=g-1:u=g+1}let l=c[m],p=(a-o[m])/l,M=p*p,d=M*p,S=b[m]*l,C=b[m+1]*l;return(2*d-3*M+1)*s[m]+(d-2*M+p)*S+(-2*d+3*M)*s[m+1]+(d-M)*C}};function y(r){let t=r.length;if(t===0)return 0;let n=0;for(let e=0;e<t;e++)n+=r[e]*r[e];return Math.sqrt(n/t)}var ft=r=>{let t=r.length;if(t===0)return{min:0,max:0,avg:0};let n=r[0],e=r[0],o=0;for(let s=0;s<t;s++){let c=r[s];c<n&&(n=c),c>e&&(e=c),o+=c}return{min:n,max:e,avg:o/t}},I=r=>{let t=r.length;if(t===0)return 0;let n=1e-6,e=r.reduce((c,i)=>c*(i+n),1),o=Math.pow(e,1/t),s=Math.max(0,Math.min(1,o));return parseFloat((s*100).toFixed(2))};var F=(r,t,n)=>{t||(t="brand"),n||(n=[...Array(r.length).keys()]);let e=x(f(r[0]))[0],o=x(f(r[r.length-1]))[0];e>o&&r.reverse();let s=r.map(m=>j(m));for(let m=1;m<s.length;m++){let l=k(s[m-1].lab,s[m].lab);s[m].cumDeltaE00=s[m-1].cumDeltaE00+l}let c=v(r.slice(2,-2))||r[Math.floor(r.length/2)],i=r.findIndex(m=>m.toLowerCase()===(c==null?void 0:c.toLowerCase())),b=A(s.map(m=>m.hue).slice(1,-1));for(let m=1;m<s.length-1;m++)s[m].hue=b[m-1];let a=K(s.map(m=>m.hue).slice(1,-1),s[i].hue),u=N(s),h={lightnessLinearity:W(s.map(m=>m.lightness)),chromaSmoothness:J(s.map(m=>m.chroma)),spacingUniformity:Q(s.map(m=>m.cumDeltaE00)),contrastEfficiency:V(u.wcag45.span,n.length),hueStability:a};return{name:t,colors:r,baseIndex:i,baseColor:c,shades:s,contrasts:u,metrics:h,score:I(Object.values(h))}},xt=r=>{let{stepNames:t,name:n,colors:e}=r,o=Object.values(e)[0],s=x(f(o[0]))[0],c=x(f(o[o.length-1]))[0],i=s>c?"darken":"lighten",b=[],a={};for(let h in e){let m=F(e[h],h,t);b.push(m),a[h]=m.baseColor}let u={contrastEfficiency:y(b.map(h=>h.metrics.contrastEfficiency)),lightnessLinearity:y(b.map(h=>h.metrics.lightnessLinearity)),chromaSmoothness:y(b.map(h=>h.metrics.chromaSmoothness)),hueStability:y(b.map(h=>h.metrics.hueStability)),spacingUniformity:y(b.map(h=>h.metrics.spacingUniformity))};return{name:n,baseColors:a,stepNames:t,direction:i,steps:t.length,colors:e,contrasts:z(b),scales:b,metrics:u,score:I(Object.values(u))}};function W(r){let t=r.length;if(t<2)return 1;let n=0,e=0,o=0,s=0;for(let m=0;m<t;m++)n+=m,e+=r[m],o+=m*r[m],s+=m*m;let c=t*s-n*n;if(Math.abs(c)<1e-10)return 1;let i=(t*o-n*e)/c,b=(e-i*n)/t;if(Math.abs(i*(t-1))<.001)return 1;let u=0,h=0;for(let m=0;m<t;m++){let l=i*m+b,p=r[m]-l;u+=p*p;let M=Math.max(l-Math.min(b,i*(t-1)+b),Math.max(b,i*(t-1)+b)-l);h+=M*M}return Math.max(0,Math.min(1,1-Math.sqrt(u/t)/Math.sqrt(h/t)))}function K(r,t){let n=r.length;if(n<2)return 1;let e=0,o=0;for(let s=0;s<n;s++){let c=Math.abs(r[s]-t)%360;c>180&&(c=360-c),e+=c*c;let i=s/(n-1)*180;o+=i*i}return Math.max(0,Math.min(1,1-Math.sqrt(e/n)/(Math.sqrt(o/n)||1)))}var J=r=>{let t=r.length;if(t<3)return 1;let n=r.map(a=>a*133.8),e=Math.min(...n),o=Math.max(...n),s=n.findIndex(a=>a===o),c=B([[0,n[0]],[s,o],[t-1,n[t-1]]]),i=0,b=0;for(let a=0;a<t;a++){let u=c(a),h=n[a]-u;i+=h*h,b+=Math.pow(Math.max(u-e,o-u),2)}return Math.max(0,Math.min(1,1-Math.sqrt(i/t)/Math.sqrt(b/t)))},Q=r=>{let t=r.length;if(t<2)return 1;let n=[];for(let c=1;c<t;c++){let i=r[c]-r[c-1];if(i<0)return 0;n.push(i)}let e=n.reduce((c,i)=>c+i,0)/n.length;if(e<=1e-6)return 0;let o=0;for(let c of n)o+=Math.pow(c-e,2);let s=Math.sqrt(o/n.length)/e;return Math.max(0,Math.min(1,1/(1+s)))},V=(r,t)=>{if(t<=1)return 1;let n=.489,e=r/t,o=n*((t-1)/t);return e<=o?1:e>=1?0:(1-e)/(1-o)};0&&(module.exports={analyzeMonochromatic,analyzeMonochromaticPalette,calcChromaSmoothness,calcContrastEfficiency,calcDeltaE2000,calcHueStability,calcLightnessLinearity,calcScore,calcSpacingUniformity,calcStatistics,contrastList,createMonotone,cssRgbToRgb,findMaxChromaHex,fromLightnessEAL,getApcaContrast,getMonochromaticContrasts,getPaletteContrasts,getRelativeLuminance,getWcagContrast,hexToRgb,labToLch,labToRgb,lchToLab,lrgbToSrgb,rgbToHex,rgbToLab,rootMeanSquare,simulateDeuteranopia,simulateProtanopia,srgbToLrgb,toLightnessEAL,unwrapHue});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/** Calculate relative luminance from linear RGB (0-1). */
|
|
2
|
+
declare const getRelativeLuminance: (rgb: number[]) => number;
|
|
3
|
+
declare const lrgbToSrgb: (rgb: number[]) => number[];
|
|
4
|
+
declare const srgbToLrgb: (rgb: number[]) => number[];
|
|
5
|
+
/** Convert linear RGB to sRGB Hex string. */
|
|
6
|
+
declare const rgbToHex: (rgb: number[]) => string;
|
|
7
|
+
/** Convert sRGB Hex string to linear RGB. */
|
|
8
|
+
declare const hexToRgb: (hex: string) => number[];
|
|
9
|
+
/** Calculate Equivalent Achromatic Lightness (L_EAL) using High et al. (2023). */
|
|
10
|
+
declare const toLightnessEAL: (lab: number[]) => number;
|
|
11
|
+
/** Reverse L_EAL to get CIELAB Lightness (L). */
|
|
12
|
+
declare const fromLightnessEAL: (brightness: number, lab: number[]) => number;
|
|
13
|
+
/** Convert LCH to CIELAB coordinates. */
|
|
14
|
+
declare const lchToLab: (lch: number[]) => number[];
|
|
15
|
+
/** Convert linear RGB to CIELAB (D50). */
|
|
16
|
+
declare const rgbToLab: (rgb: number[]) => number[];
|
|
17
|
+
/** Convert CIELAB to LCH coordinates. */
|
|
18
|
+
declare const labToLch: (lab: number[]) => number[];
|
|
19
|
+
/** Convert CIELAB (D50) to linear RGB. */
|
|
20
|
+
declare const labToRgb: (lab: number[]) => number[];
|
|
21
|
+
/** Calculate color difference using CIEDE2000 formula. */
|
|
22
|
+
declare const calcDeltaE2000: (lab1: number[], lab2: number[]) => number;
|
|
23
|
+
/** Find the Hex color with the highest Chroma in a list. */
|
|
24
|
+
declare const findMaxChromaHex: (scale: string[]) => string;
|
|
25
|
+
/** Normalize hue values to prevent large jumps during interpolation. */
|
|
26
|
+
declare function unwrapHue(hues: number[]): number[];
|
|
27
|
+
/** Simulate Protanopia (red-blindness) on linear RGB. */
|
|
28
|
+
declare function simulateProtanopia(rgb: number[]): number[];
|
|
29
|
+
/** Simulate Deuteranopia (green-blindness) on linear RGB. */
|
|
30
|
+
declare function simulateDeuteranopia(rgb: number[]): number[];
|
|
31
|
+
/** Convert CSS rgb() string to linear RGB. */
|
|
32
|
+
declare const cssRgbToRgb: (css: string) => number[];
|
|
33
|
+
|
|
34
|
+
type ColorShade = {
|
|
35
|
+
hex: string;
|
|
36
|
+
rgb: number[];
|
|
37
|
+
lab: number[];
|
|
38
|
+
lch: number[];
|
|
39
|
+
parameter: number;
|
|
40
|
+
lightness: number;
|
|
41
|
+
chroma: number;
|
|
42
|
+
hue: number;
|
|
43
|
+
luminance: number;
|
|
44
|
+
wcag: number;
|
|
45
|
+
apca: number;
|
|
46
|
+
cumDeltaE00: number;
|
|
47
|
+
cumProtDeltaE00: number;
|
|
48
|
+
cumDeutDeltaE00: number;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type ContrastName = 'wcag20' | 'wcag30' | 'wcag45' | 'wcag70' | 'apca45' | 'apca60' | 'apca75' | 'apca90';
|
|
52
|
+
type Contrast = {
|
|
53
|
+
system: string;
|
|
54
|
+
target: number;
|
|
55
|
+
span: number;
|
|
56
|
+
value: number;
|
|
57
|
+
efficiency: number;
|
|
58
|
+
name: ContrastName;
|
|
59
|
+
};
|
|
60
|
+
/** Calculate WCAG 2.x contrast ratio. */
|
|
61
|
+
declare const getWcagContrast: (l1: number, l2: number) => number;
|
|
62
|
+
/** Calculate APCA Lc (Lightness Contrast) value. */
|
|
63
|
+
declare const getApcaContrast: (yText: number, yBg: number) => number;
|
|
64
|
+
/** Analyze contrast metrics for a single Monochromatic scale. */
|
|
65
|
+
declare const getMonochromaticContrasts: (metrics: ColorShade[]) => Record<string, Contrast>;
|
|
66
|
+
declare const contrastList: ContrastName[];
|
|
67
|
+
/** Aggregate contrast metrics across multiple color scales. */
|
|
68
|
+
declare const getPaletteContrasts: (colorScales: any[]) => Record<ContrastName, Contrast>;
|
|
69
|
+
|
|
70
|
+
type MonochromaticAnalysis = {
|
|
71
|
+
name: string;
|
|
72
|
+
colors: string[];
|
|
73
|
+
baseIndex: number;
|
|
74
|
+
baseColor: string;
|
|
75
|
+
shades: ColorShade[];
|
|
76
|
+
metrics: MonochromaticMetrics;
|
|
77
|
+
contrasts: Record<ContrastName, Contrast>;
|
|
78
|
+
score: number;
|
|
79
|
+
};
|
|
80
|
+
type MonochromaticMetrics = {
|
|
81
|
+
lightnessLinearity: number;
|
|
82
|
+
chromaSmoothness: number;
|
|
83
|
+
spacingUniformity: number;
|
|
84
|
+
hueStability: number;
|
|
85
|
+
contrastEfficiency: number;
|
|
86
|
+
};
|
|
87
|
+
/** Analyze a single monochromatic color scale. */
|
|
88
|
+
declare const analyzeMonochromatic: (colors: string[], name?: string, stepNames?: Array<number | string>) => MonochromaticAnalysis;
|
|
89
|
+
type MonochromaticPaletteData = {
|
|
90
|
+
name: string;
|
|
91
|
+
stepNames: Array<number | string>;
|
|
92
|
+
colors: Record<string, string[]>;
|
|
93
|
+
};
|
|
94
|
+
/** Analyze a full palette containing multiple monochromatic scales. */
|
|
95
|
+
declare const analyzeMonochromaticPalette: (paletteData: MonochromaticPaletteData) => {
|
|
96
|
+
name: string;
|
|
97
|
+
baseColors: Record<string, string>;
|
|
98
|
+
stepNames: (string | number)[];
|
|
99
|
+
direction: string;
|
|
100
|
+
steps: number;
|
|
101
|
+
colors: Record<string, string[]>;
|
|
102
|
+
contrasts: Record<ContrastName, Contrast>;
|
|
103
|
+
scales: MonochromaticAnalysis[];
|
|
104
|
+
metrics: {
|
|
105
|
+
contrastEfficiency: number;
|
|
106
|
+
lightnessLinearity: number;
|
|
107
|
+
chromaSmoothness: number;
|
|
108
|
+
hueStability: number;
|
|
109
|
+
spacingUniformity: number;
|
|
110
|
+
};
|
|
111
|
+
score: number;
|
|
112
|
+
};
|
|
113
|
+
/** Measure how close lightness values follow a linear trend. */
|
|
114
|
+
declare function calcLightnessLinearity(values: number[]): number;
|
|
115
|
+
/** Measure hue deviation from a reference color. */
|
|
116
|
+
declare function calcHueStability(values: number[], ref: number): number;
|
|
117
|
+
/** Measure the smoothness of chroma transitions using a spline peak. */
|
|
118
|
+
declare const calcChromaSmoothness: (C: number[]) => number;
|
|
119
|
+
/** Measure visual spacing uniformity using DeltaE2000. */
|
|
120
|
+
declare const calcSpacingUniformity: (cumulativeDeltaE00: number[]) => number;
|
|
121
|
+
/**
|
|
122
|
+
* Evaluate Contrast Span Efficiency (η) using Continuous Linear Scaling.
|
|
123
|
+
* This scientific approach removes quantization bias (no ceil/floor),
|
|
124
|
+
* ensuring fairness across both odd and even step counts.
|
|
125
|
+
*/
|
|
126
|
+
declare const calcContrastEfficiency: (span: number, steps: number) => number;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Create a Monotone Cubic Hermite Interpolator.
|
|
130
|
+
* Ensures monotonicity is preserved between points.
|
|
131
|
+
* Fritsch, F. N., & Carlson, R. E. (1980). Monotone piecewise cubic interpolation. *SIAM Journal on Numerical Analysis*, 17(2), 238–246.
|
|
132
|
+
*/
|
|
133
|
+
declare const createMonotone: (points: number[][]) => (_t: number) => number;
|
|
134
|
+
/** Calculate Root Mean Square (RMS) of an array. */
|
|
135
|
+
declare function rootMeanSquare(values: number[]): number;
|
|
136
|
+
/** Calculate min, max, and average of an array. */
|
|
137
|
+
declare const calcStatistics: (array: number[]) => {
|
|
138
|
+
min: number;
|
|
139
|
+
max: number;
|
|
140
|
+
avg: number;
|
|
141
|
+
};
|
|
142
|
+
/** Calculate geometric mean score (0-100) from metrics. */
|
|
143
|
+
declare const calcScore: (metrics: number[]) => number;
|
|
144
|
+
|
|
145
|
+
export { type Contrast, type ContrastName, type MonochromaticAnalysis, type MonochromaticMetrics, type MonochromaticPaletteData, analyzeMonochromatic, analyzeMonochromaticPalette, calcChromaSmoothness, calcContrastEfficiency, calcDeltaE2000, calcHueStability, calcLightnessLinearity, calcScore, calcSpacingUniformity, calcStatistics, contrastList, createMonotone, cssRgbToRgb, findMaxChromaHex, fromLightnessEAL, getApcaContrast, getMonochromaticContrasts, getPaletteContrasts, getRelativeLuminance, getWcagContrast, hexToRgb, labToLch, labToRgb, lchToLab, lrgbToSrgb, rgbToHex, rgbToLab, rootMeanSquare, simulateDeuteranopia, simulateProtanopia, srgbToLrgb, toLightnessEAL, unwrapHue };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/** Calculate relative luminance from linear RGB (0-1). */
|
|
2
|
+
declare const getRelativeLuminance: (rgb: number[]) => number;
|
|
3
|
+
declare const lrgbToSrgb: (rgb: number[]) => number[];
|
|
4
|
+
declare const srgbToLrgb: (rgb: number[]) => number[];
|
|
5
|
+
/** Convert linear RGB to sRGB Hex string. */
|
|
6
|
+
declare const rgbToHex: (rgb: number[]) => string;
|
|
7
|
+
/** Convert sRGB Hex string to linear RGB. */
|
|
8
|
+
declare const hexToRgb: (hex: string) => number[];
|
|
9
|
+
/** Calculate Equivalent Achromatic Lightness (L_EAL) using High et al. (2023). */
|
|
10
|
+
declare const toLightnessEAL: (lab: number[]) => number;
|
|
11
|
+
/** Reverse L_EAL to get CIELAB Lightness (L). */
|
|
12
|
+
declare const fromLightnessEAL: (brightness: number, lab: number[]) => number;
|
|
13
|
+
/** Convert LCH to CIELAB coordinates. */
|
|
14
|
+
declare const lchToLab: (lch: number[]) => number[];
|
|
15
|
+
/** Convert linear RGB to CIELAB (D50). */
|
|
16
|
+
declare const rgbToLab: (rgb: number[]) => number[];
|
|
17
|
+
/** Convert CIELAB to LCH coordinates. */
|
|
18
|
+
declare const labToLch: (lab: number[]) => number[];
|
|
19
|
+
/** Convert CIELAB (D50) to linear RGB. */
|
|
20
|
+
declare const labToRgb: (lab: number[]) => number[];
|
|
21
|
+
/** Calculate color difference using CIEDE2000 formula. */
|
|
22
|
+
declare const calcDeltaE2000: (lab1: number[], lab2: number[]) => number;
|
|
23
|
+
/** Find the Hex color with the highest Chroma in a list. */
|
|
24
|
+
declare const findMaxChromaHex: (scale: string[]) => string;
|
|
25
|
+
/** Normalize hue values to prevent large jumps during interpolation. */
|
|
26
|
+
declare function unwrapHue(hues: number[]): number[];
|
|
27
|
+
/** Simulate Protanopia (red-blindness) on linear RGB. */
|
|
28
|
+
declare function simulateProtanopia(rgb: number[]): number[];
|
|
29
|
+
/** Simulate Deuteranopia (green-blindness) on linear RGB. */
|
|
30
|
+
declare function simulateDeuteranopia(rgb: number[]): number[];
|
|
31
|
+
/** Convert CSS rgb() string to linear RGB. */
|
|
32
|
+
declare const cssRgbToRgb: (css: string) => number[];
|
|
33
|
+
|
|
34
|
+
type ColorShade = {
|
|
35
|
+
hex: string;
|
|
36
|
+
rgb: number[];
|
|
37
|
+
lab: number[];
|
|
38
|
+
lch: number[];
|
|
39
|
+
parameter: number;
|
|
40
|
+
lightness: number;
|
|
41
|
+
chroma: number;
|
|
42
|
+
hue: number;
|
|
43
|
+
luminance: number;
|
|
44
|
+
wcag: number;
|
|
45
|
+
apca: number;
|
|
46
|
+
cumDeltaE00: number;
|
|
47
|
+
cumProtDeltaE00: number;
|
|
48
|
+
cumDeutDeltaE00: number;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type ContrastName = 'wcag20' | 'wcag30' | 'wcag45' | 'wcag70' | 'apca45' | 'apca60' | 'apca75' | 'apca90';
|
|
52
|
+
type Contrast = {
|
|
53
|
+
system: string;
|
|
54
|
+
target: number;
|
|
55
|
+
span: number;
|
|
56
|
+
value: number;
|
|
57
|
+
efficiency: number;
|
|
58
|
+
name: ContrastName;
|
|
59
|
+
};
|
|
60
|
+
/** Calculate WCAG 2.x contrast ratio. */
|
|
61
|
+
declare const getWcagContrast: (l1: number, l2: number) => number;
|
|
62
|
+
/** Calculate APCA Lc (Lightness Contrast) value. */
|
|
63
|
+
declare const getApcaContrast: (yText: number, yBg: number) => number;
|
|
64
|
+
/** Analyze contrast metrics for a single Monochromatic scale. */
|
|
65
|
+
declare const getMonochromaticContrasts: (metrics: ColorShade[]) => Record<string, Contrast>;
|
|
66
|
+
declare const contrastList: ContrastName[];
|
|
67
|
+
/** Aggregate contrast metrics across multiple color scales. */
|
|
68
|
+
declare const getPaletteContrasts: (colorScales: any[]) => Record<ContrastName, Contrast>;
|
|
69
|
+
|
|
70
|
+
type MonochromaticAnalysis = {
|
|
71
|
+
name: string;
|
|
72
|
+
colors: string[];
|
|
73
|
+
baseIndex: number;
|
|
74
|
+
baseColor: string;
|
|
75
|
+
shades: ColorShade[];
|
|
76
|
+
metrics: MonochromaticMetrics;
|
|
77
|
+
contrasts: Record<ContrastName, Contrast>;
|
|
78
|
+
score: number;
|
|
79
|
+
};
|
|
80
|
+
type MonochromaticMetrics = {
|
|
81
|
+
lightnessLinearity: number;
|
|
82
|
+
chromaSmoothness: number;
|
|
83
|
+
spacingUniformity: number;
|
|
84
|
+
hueStability: number;
|
|
85
|
+
contrastEfficiency: number;
|
|
86
|
+
};
|
|
87
|
+
/** Analyze a single monochromatic color scale. */
|
|
88
|
+
declare const analyzeMonochromatic: (colors: string[], name?: string, stepNames?: Array<number | string>) => MonochromaticAnalysis;
|
|
89
|
+
type MonochromaticPaletteData = {
|
|
90
|
+
name: string;
|
|
91
|
+
stepNames: Array<number | string>;
|
|
92
|
+
colors: Record<string, string[]>;
|
|
93
|
+
};
|
|
94
|
+
/** Analyze a full palette containing multiple monochromatic scales. */
|
|
95
|
+
declare const analyzeMonochromaticPalette: (paletteData: MonochromaticPaletteData) => {
|
|
96
|
+
name: string;
|
|
97
|
+
baseColors: Record<string, string>;
|
|
98
|
+
stepNames: (string | number)[];
|
|
99
|
+
direction: string;
|
|
100
|
+
steps: number;
|
|
101
|
+
colors: Record<string, string[]>;
|
|
102
|
+
contrasts: Record<ContrastName, Contrast>;
|
|
103
|
+
scales: MonochromaticAnalysis[];
|
|
104
|
+
metrics: {
|
|
105
|
+
contrastEfficiency: number;
|
|
106
|
+
lightnessLinearity: number;
|
|
107
|
+
chromaSmoothness: number;
|
|
108
|
+
hueStability: number;
|
|
109
|
+
spacingUniformity: number;
|
|
110
|
+
};
|
|
111
|
+
score: number;
|
|
112
|
+
};
|
|
113
|
+
/** Measure how close lightness values follow a linear trend. */
|
|
114
|
+
declare function calcLightnessLinearity(values: number[]): number;
|
|
115
|
+
/** Measure hue deviation from a reference color. */
|
|
116
|
+
declare function calcHueStability(values: number[], ref: number): number;
|
|
117
|
+
/** Measure the smoothness of chroma transitions using a spline peak. */
|
|
118
|
+
declare const calcChromaSmoothness: (C: number[]) => number;
|
|
119
|
+
/** Measure visual spacing uniformity using DeltaE2000. */
|
|
120
|
+
declare const calcSpacingUniformity: (cumulativeDeltaE00: number[]) => number;
|
|
121
|
+
/**
|
|
122
|
+
* Evaluate Contrast Span Efficiency (η) using Continuous Linear Scaling.
|
|
123
|
+
* This scientific approach removes quantization bias (no ceil/floor),
|
|
124
|
+
* ensuring fairness across both odd and even step counts.
|
|
125
|
+
*/
|
|
126
|
+
declare const calcContrastEfficiency: (span: number, steps: number) => number;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Create a Monotone Cubic Hermite Interpolator.
|
|
130
|
+
* Ensures monotonicity is preserved between points.
|
|
131
|
+
* Fritsch, F. N., & Carlson, R. E. (1980). Monotone piecewise cubic interpolation. *SIAM Journal on Numerical Analysis*, 17(2), 238–246.
|
|
132
|
+
*/
|
|
133
|
+
declare const createMonotone: (points: number[][]) => (_t: number) => number;
|
|
134
|
+
/** Calculate Root Mean Square (RMS) of an array. */
|
|
135
|
+
declare function rootMeanSquare(values: number[]): number;
|
|
136
|
+
/** Calculate min, max, and average of an array. */
|
|
137
|
+
declare const calcStatistics: (array: number[]) => {
|
|
138
|
+
min: number;
|
|
139
|
+
max: number;
|
|
140
|
+
avg: number;
|
|
141
|
+
};
|
|
142
|
+
/** Calculate geometric mean score (0-100) from metrics. */
|
|
143
|
+
declare const calcScore: (metrics: number[]) => number;
|
|
144
|
+
|
|
145
|
+
export { type Contrast, type ContrastName, type MonochromaticAnalysis, type MonochromaticMetrics, type MonochromaticPaletteData, analyzeMonochromatic, analyzeMonochromaticPalette, calcChromaSmoothness, calcContrastEfficiency, calcDeltaE2000, calcHueStability, calcLightnessLinearity, calcScore, calcSpacingUniformity, calcStatistics, contrastList, createMonotone, cssRgbToRgb, findMaxChromaHex, fromLightnessEAL, getApcaContrast, getMonochromaticContrasts, getPaletteContrasts, getRelativeLuminance, getWcagContrast, hexToRgb, labToLch, labToRgb, lchToLab, lrgbToSrgb, rgbToHex, rgbToLab, rootMeanSquare, simulateDeuteranopia, simulateProtanopia, srgbToLrgb, toLightnessEAL, unwrapHue };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var Chromametry=(()=>{var P=Object.defineProperty;var ot=Object.getOwnPropertyDescriptor;var at=Object.getOwnPropertyNames;var st=Object.prototype.hasOwnProperty;var ct=(r,t)=>{for(var n in t)P(r,n,{get:t[n],enumerable:!0})},mt=(r,t,n,e)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of at(t))!st.call(r,o)&&o!==n&&P(r,o,{get:()=>t[o],enumerable:!(e=ot(t,o))||e.enumerable});return r};var ht=r=>mt(P({},"__esModule",{value:!0}),r);var dt={};ct(dt,{analyzeMonochromatic:()=>F,analyzeMonochromaticPalette:()=>xt,calcChromaSmoothness:()=>J,calcContrastEfficiency:()=>V,calcDeltaE2000:()=>k,calcHueStability:()=>K,calcLightnessLinearity:()=>W,calcScore:()=>I,calcSpacingUniformity:()=>Q,calcStatistics:()=>ft,contrastList:()=>H,createMonotone:()=>B,cssRgbToRgb:()=>gt,findMaxChromaHex:()=>v,fromLightnessEAL:()=>ut,getApcaContrast:()=>L,getMonochromaticContrasts:()=>N,getPaletteContrasts:()=>z,getRelativeLuminance:()=>D,getWcagContrast:()=>R,hexToRgb:()=>f,labToLch:()=>q,labToRgb:()=>lt,lchToLab:()=>bt,lrgbToSrgb:()=>X,rgbToHex:()=>it,rgbToLab:()=>x,rootMeanSquare:()=>y,simulateDeuteranopia:()=>Mt,simulateProtanopia:()=>pt,srgbToLrgb:()=>Y,toLightnessEAL:()=>T,unwrapHue:()=>A});var D=r=>{let[t,n,e]=r;return .2126*t+.7152*n+.0722*e},X=r=>{let t=n=>{let e=Math.max(0,Math.min(1,n)),o=e<=.0031308?12.92*e:1.055*Math.pow(e,1/2.4)-.055;return Math.max(0,Math.min(255,Math.round(o*255)))};return r.map(t)},Y=r=>{let t=n=>n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92;return r.map(t)},it=r=>{let[t,n,e]=X(r);return t=t.toString(16).padStart(2,"0"),n=n.toString(16).padStart(2,"0"),e=e.toString(16).padStart(2,"0"),`#${t}${n}${e}`},f=r=>{let t=parseInt(r.slice(1,3),16)/255,n=parseInt(r.slice(3,5),16)/255,e=parseInt(r.slice(5,7),16)/255;return Y([t,n,e])},T=r=>{let[t,n,e]=r,o=Math.sqrt(n*n+e*e),c=(Math.atan2(e,n)*180/Math.PI+360)%360,i=.1644,b=.0603,a=.1307,u=.006,h=i*Math.abs(Math.sin((c-90)/2*(Math.PI/180)))+b,m=0;return(c<=90||c>=270)&&(m=a*Math.abs(Math.cos(c*(Math.PI/180)))+u),t+(h+m)*o},ut=(r,t)=>{let[,n,e]=t,o=Math.sqrt(n*n+e*e),c=(Math.atan2(e,n)*180/Math.PI+360)%360,i=.1644,b=.0603,a=.1307,u=.006,h=i*Math.abs(Math.sin((c-90)/2*(Math.PI/180)))+b,m=0;return(c<=90||c>=270)&&(m=a*Math.abs(Math.cos(c*(Math.PI/180)))+u),Math.max(0,r-(h+m)*o)},bt=r=>{let[t,n,e]=r,o=e*Math.PI/180;return[t,n*Math.cos(o),n*Math.sin(o)]},x=r=>{let[t,n,e]=r,o=[[1.0479298,.0229469,-.0501922],[.0296278,.9904345,-.0170738],[.0296278,.9904345,-.0170738]],s=.4124564*t+.3575761*n+.1804375*e,c=.2126729*t+.7151522*n+.072175*e,i=.0193339*t+.119192*n+.9503041*e,b=1.0479298*s+.0229469*c-.0501922*i,a=.0296278*s+.9904345*c-.0170738*i,u=-.009243*s+.0150552*c+.7518743*i,h=M=>M>.008856?Math.cbrt(M):7.787*M+16/116,m=h(b/.96422),l=h(a/1),p=h(u/.82521);return[116*l-16,500*(m-l),200*(l-p)]},q=r=>{let[t,n,e]=r,o=Math.sqrt(n*n+e*e);if(o<1e-4)return[t,0,0];let c=(Math.atan2(e,n)*180/Math.PI+360)%360;return c>=359.9999&&(c=0),[t,o,c]},lt=r=>{let[t,n,e]=r,o=(t+16)/116,s=n/500+o,c=o-e/200,i=p=>p>.2068965?p**3:(p-16/116)/7.787,b=i(s)*.96422,a=i(o)*1,u=i(c)*.82521,h=.9555766*b-.0230393*a+.0631636*u,m=-.0282895*b+1.0099416*a+.0210077*u,l=.0122982*b-.020483*a+1.3299098*u;return[3.2404542*h-1.5371385*m-.4985314*l,-.969266*h+1.8760108*m+.041556*l,.0556434*h-.2040259*m+1.0572252*l]},k=(r,t)=>{let[n,e,o]=r,[s,c,i]=t,b=(n+s)/2,a=Math.sqrt(e*e+o*o),u=Math.sqrt(c*c+i*i),h=(a+u)/2,m=.5*(1-Math.sqrt(Math.pow(h,7)/(Math.pow(h,7)+Math.pow(25,7)))),l=e*(1+m),p=c*(1+m),M=Math.sqrt(l*l+o*o),d=Math.sqrt(p*p+i*i),S=(M+d)/2,C=Math.atan2(o,l)*180/Math.PI+(Math.atan2(o,l)<0?360:0),g=Math.atan2(i,p)*180/Math.PI+(Math.atan2(i,p)<0?360:0),E=g-C;Math.abs(E)>180&&(E+=g<=C?360:-360);let w=Math.abs(C-g)>180?(C+g+360)/2:(C+g)/2,Z=1-.17*Math.cos((w-30)*Math.PI/180)+.24*Math.cos(2*w*Math.PI/180)+.32*Math.cos((3*w+6)*Math.PI/180)-.2*Math.cos((4*w-63)*Math.PI/180),tt=s-n,U=d-M,$=2*Math.sqrt(M*d)*Math.sin(E/2*Math.PI/180),nt=1+.015*Math.pow(b-50,2)/Math.sqrt(20+Math.pow(b-50,2)),G=1+.045*S,O=1+.015*S*Z,rt=30*Math.exp(-Math.pow((w-275)/25,2)),et=-(2*Math.sqrt(Math.pow(S,7)/(Math.pow(S,7)+Math.pow(25,7))))*Math.sin(2*rt*Math.PI/180);return Math.sqrt(Math.pow(tt/nt,2)+Math.pow(U/G,2)+Math.pow($/O,2)+et*(U/G)*($/O))},v=r=>{let t="",n=-1/0;for(let e of r){let o=x(f(e)),[,s]=q(o);s>n&&(n=s,t=e)}return t};function A(r){let t=[r[0]];for(let n=1;n<r.length;n++){let e=r[n]-r[n-1];e>180?e-=360:e<-180&&(e+=360),t.push(t[n-1]+e)}return t}function pt(r){let[t,n,e]=r;return[.152286*t+1.052583*n-.204868*e,.114503*t+.786281*n+.099216*e,-.003882*t-.048116*n+1.051998*e]}function Mt(r){let[t,n,e]=r;return[.367322*t+.860646*n-.227968*e,.280085*t+.672501*n+.047413*e,-.01182*t+.04294*n+.968881*e]}var gt=r=>{let t=r.match(/\d+(\.\d+)?/g);if(!t||t.length<3)throw new Error("Invalid CSS rgb()");let n=e=>{let o=e/255;return o<=.04045?o/12.92:Math.pow((o+.055)/1.055,2.4)};return[n(Number(t[0])),n(Number(t[1])),n(Number(t[2]))]};var R=(r,t)=>(Math.max(r,t)+.05)/(Math.min(r,t)+.05),L=(r,t)=>{let n=c=>c>5e-4?c:c+Math.pow(5e-4-c,.8),e=n(r),o=n(t),s=(Math.pow(e,.56)-Math.pow(o,.56))*100;return Math.abs(s)<.1?0:(s=s>0?s<1?0:s-.25:s>-1?0:s+.25,Math.round(s))};function _(r){let t=r.match(/^(wcag|apca)(\d+)$/);if(!t)throw new Error(`Invalid contrast: ${r}`);let n=t[1]==="wcag"?Number(t[2])/10:Number(t[2]);return{system:t[1],target:n}}var N=r=>{let t={},n=r.length,e=n-1;return H.forEach(o=>{let{system:s,target:c}=_(o),i=e,b=0;for(let a=1;a<n;a++){let u=1/0;for(let h=0;h<n-a;h++){let m=s==="wcag"?R(r[h].luminance,r[h+a].luminance):Math.max(Math.abs(L(r[h+a].luminance,r[h].luminance)),Math.abs(L(r[h].luminance,r[h+a].luminance)));m<u&&(u=m)}if(u>=c){i=a,b=u;break}a===e&&(b=u)}t[o]={system:s,efficiency:i/e,target:c,span:i,value:b,name:o}}),t},H=["wcag30","wcag45","wcag70","apca45","apca60","apca75"],z=r=>{let t={};return H.forEach(n=>{var a;let{target:e,system:o}=_(n),s=r.map(u=>u.contrasts[n]),c=((a=r[0])==null?void 0:a.shades.length)||0,i=Math.max(...s.map(u=>(u==null?void 0:u.span)||0)),b=s.reduce((u,h)=>u+((h==null?void 0:h.value)||0),0);t[n]={system:o,target:e,span:i,value:b/(r.length||1),name:n,efficiency:i/(c-1||1)}}),t};var j=r=>{let t=f(r),n=x(t),e=D(t),o=T(n),s=q(n);return{hex:r,rgb:t,lab:n,lch:s,lightness:o,chroma:s[1],hue:s[2],luminance:e,parameter:0,cumDeltaE00:0,cumProtDeltaE00:0,cumDeutDeltaE00:0,wcag:R(e,1),apca:L(e,1)}};var B=r=>{if(r.length<1)return a=>0;let t=[...r].sort((a,u)=>a[0]-u[0]),n=[];for(let a=0;a<t.length;a++)(a===0||t[a][0]!==t[a-1][0])&&n.push(t[a]);let e=n.length;if(e===1)return a=>n[0][1];let o=n.map(a=>a[0]),s=n.map(a=>a[1]),c=[],i=[];for(let a=0;a<e-1;a++)c[a]=o[a+1]-o[a],i[a]=(s[a+1]-s[a])/c[a];let b=new Array(e);b[0]=i[0],b[e-1]=i[e-2];for(let a=1;a<e-1;a++){let u=i[a-1],h=i[a];if(u*h<=0)b[a]=0;else{let m=(1+c[a]/(c[a-1]+c[a]))/3;b[a]=u*h/((1-m)*u+m*h)}}return a=>{if(a<=o[0])return s[0];if(a>=o[e-1])return s[e-1];let u=0,h=e-2,m=0;for(;u<=h;){let g=Math.floor((u+h)/2);if(a>=o[g]&&a<=o[g+1]){m=g;break}a<o[g]?h=g-1:u=g+1}let l=c[m],p=(a-o[m])/l,M=p*p,d=M*p,S=b[m]*l,C=b[m+1]*l;return(2*d-3*M+1)*s[m]+(d-2*M+p)*S+(-2*d+3*M)*s[m+1]+(d-M)*C}};function y(r){let t=r.length;if(t===0)return 0;let n=0;for(let e=0;e<t;e++)n+=r[e]*r[e];return Math.sqrt(n/t)}var ft=r=>{let t=r.length;if(t===0)return{min:0,max:0,avg:0};let n=r[0],e=r[0],o=0;for(let s=0;s<t;s++){let c=r[s];c<n&&(n=c),c>e&&(e=c),o+=c}return{min:n,max:e,avg:o/t}},I=r=>{let t=r.length;if(t===0)return 0;let n=1e-6,e=r.reduce((c,i)=>c*(i+n),1),o=Math.pow(e,1/t),s=Math.max(0,Math.min(1,o));return parseFloat((s*100).toFixed(2))};var F=(r,t,n)=>{t||(t="brand"),n||(n=[...Array(r.length).keys()]);let e=x(f(r[0]))[0],o=x(f(r[r.length-1]))[0];e>o&&r.reverse();let s=r.map(m=>j(m));for(let m=1;m<s.length;m++){let l=k(s[m-1].lab,s[m].lab);s[m].cumDeltaE00=s[m-1].cumDeltaE00+l}let c=v(r.slice(2,-2))||r[Math.floor(r.length/2)],i=r.findIndex(m=>m.toLowerCase()===(c==null?void 0:c.toLowerCase())),b=A(s.map(m=>m.hue).slice(1,-1));for(let m=1;m<s.length-1;m++)s[m].hue=b[m-1];let a=K(s.map(m=>m.hue).slice(1,-1),s[i].hue),u=N(s),h={lightnessLinearity:W(s.map(m=>m.lightness)),chromaSmoothness:J(s.map(m=>m.chroma)),spacingUniformity:Q(s.map(m=>m.cumDeltaE00)),contrastEfficiency:V(u.wcag45.span,n.length),hueStability:a};return{name:t,colors:r,baseIndex:i,baseColor:c,shades:s,contrasts:u,metrics:h,score:I(Object.values(h))}},xt=r=>{let{stepNames:t,name:n,colors:e}=r,o=Object.values(e)[0],s=x(f(o[0]))[0],c=x(f(o[o.length-1]))[0],i=s>c?"darken":"lighten",b=[],a={};for(let h in e){let m=F(e[h],h,t);b.push(m),a[h]=m.baseColor}let u={contrastEfficiency:y(b.map(h=>h.metrics.contrastEfficiency)),lightnessLinearity:y(b.map(h=>h.metrics.lightnessLinearity)),chromaSmoothness:y(b.map(h=>h.metrics.chromaSmoothness)),hueStability:y(b.map(h=>h.metrics.hueStability)),spacingUniformity:y(b.map(h=>h.metrics.spacingUniformity))};return{name:n,baseColors:a,stepNames:t,direction:i,steps:t.length,colors:e,contrasts:z(b),scales:b,metrics:u,score:I(Object.values(u))}};function W(r){let t=r.length;if(t<2)return 1;let n=0,e=0,o=0,s=0;for(let m=0;m<t;m++)n+=m,e+=r[m],o+=m*r[m],s+=m*m;let c=t*s-n*n;if(Math.abs(c)<1e-10)return 1;let i=(t*o-n*e)/c,b=(e-i*n)/t;if(Math.abs(i*(t-1))<.001)return 1;let u=0,h=0;for(let m=0;m<t;m++){let l=i*m+b,p=r[m]-l;u+=p*p;let M=Math.max(l-Math.min(b,i*(t-1)+b),Math.max(b,i*(t-1)+b)-l);h+=M*M}return Math.max(0,Math.min(1,1-Math.sqrt(u/t)/Math.sqrt(h/t)))}function K(r,t){let n=r.length;if(n<2)return 1;let e=0,o=0;for(let s=0;s<n;s++){let c=Math.abs(r[s]-t)%360;c>180&&(c=360-c),e+=c*c;let i=s/(n-1)*180;o+=i*i}return Math.max(0,Math.min(1,1-Math.sqrt(e/n)/(Math.sqrt(o/n)||1)))}var J=r=>{let t=r.length;if(t<3)return 1;let n=r.map(a=>a*133.8),e=Math.min(...n),o=Math.max(...n),s=n.findIndex(a=>a===o),c=B([[0,n[0]],[s,o],[t-1,n[t-1]]]),i=0,b=0;for(let a=0;a<t;a++){let u=c(a),h=n[a]-u;i+=h*h,b+=Math.pow(Math.max(u-e,o-u),2)}return Math.max(0,Math.min(1,1-Math.sqrt(i/t)/Math.sqrt(b/t)))},Q=r=>{let t=r.length;if(t<2)return 1;let n=[];for(let c=1;c<t;c++){let i=r[c]-r[c-1];if(i<0)return 0;n.push(i)}let e=n.reduce((c,i)=>c+i,0)/n.length;if(e<=1e-6)return 0;let o=0;for(let c of n)o+=Math.pow(c-e,2);let s=Math.sqrt(o/n.length)/e;return Math.max(0,Math.min(1,1/(1+s)))},V=(r,t)=>{if(t<=1)return 1;let n=.489,e=r/t,o=n*((t-1)/t);return e<=o?1:e>=1?0:(1-e)/(1-o)};return ht(dt);})();
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var v=e=>{let[n,t,r]=e;return .2126*n+.7152*t+.0722*r},K=e=>{let n=t=>{let r=Math.max(0,Math.min(1,t)),s=r<=.0031308?12.92*r:1.055*Math.pow(r,1/2.4)-.055;return Math.max(0,Math.min(255,Math.round(s*255)))};return e.map(n)},J=e=>{let n=t=>t>.04045?Math.pow((t+.055)/1.055,2.4):t/12.92;return e.map(n)},ot=e=>{let[n,t,r]=K(e);return n=n.toString(16).padStart(2,"0"),t=t.toString(16).padStart(2,"0"),r=r.toString(16).padStart(2,"0"),`#${n}${t}${r}`},x=e=>{let n=parseInt(e.slice(1,3),16)/255,t=parseInt(e.slice(3,5),16)/255,r=parseInt(e.slice(5,7),16)/255;return J([n,t,r])},A=e=>{let[n,t,r]=e,s=Math.sqrt(t*t+r*r),c=(Math.atan2(r,t)*180/Math.PI+360)%360,i=.1644,b=.0603,o=.1307,u=.006,h=i*Math.abs(Math.sin((c-90)/2*(Math.PI/180)))+b,m=0;return(c<=90||c>=270)&&(m=o*Math.abs(Math.cos(c*(Math.PI/180)))+u),n+(h+m)*s},at=(e,n)=>{let[,t,r]=n,s=Math.sqrt(t*t+r*r),c=(Math.atan2(r,t)*180/Math.PI+360)%360,i=.1644,b=.0603,o=.1307,u=.006,h=i*Math.abs(Math.sin((c-90)/2*(Math.PI/180)))+b,m=0;return(c<=90||c>=270)&&(m=o*Math.abs(Math.cos(c*(Math.PI/180)))+u),Math.max(0,e-(h+m)*s)},st=e=>{let[n,t,r]=e,s=r*Math.PI/180;return[n,t*Math.cos(s),t*Math.sin(s)]},d=e=>{let[n,t,r]=e,s=[[1.0479298,.0229469,-.0501922],[.0296278,.9904345,-.0170738],[.0296278,.9904345,-.0170738]],a=.4124564*n+.3575761*t+.1804375*r,c=.2126729*n+.7151522*t+.072175*r,i=.0193339*n+.119192*t+.9503041*r,b=1.0479298*a+.0229469*c-.0501922*i,o=.0296278*a+.9904345*c-.0170738*i,u=-.009243*a+.0150552*c+.7518743*i,h=M=>M>.008856?Math.cbrt(M):7.787*M+16/116,m=h(b/.96422),l=h(o/1),p=h(u/.82521);return[116*l-16,500*(m-l),200*(l-p)]},R=e=>{let[n,t,r]=e,s=Math.sqrt(t*t+r*r);if(s<1e-4)return[n,0,0];let c=(Math.atan2(r,t)*180/Math.PI+360)%360;return c>=359.9999&&(c=0),[n,s,c]},ct=e=>{let[n,t,r]=e,s=(n+16)/116,a=t/500+s,c=s-r/200,i=p=>p>.2068965?p**3:(p-16/116)/7.787,b=i(a)*.96422,o=i(s)*1,u=i(c)*.82521,h=.9555766*b-.0230393*o+.0631636*u,m=-.0282895*b+1.0099416*o+.0210077*u,l=.0122982*b-.020483*o+1.3299098*u;return[3.2404542*h-1.5371385*m-.4985314*l,-.969266*h+1.8760108*m+.041556*l,.0556434*h-.2040259*m+1.0572252*l]},N=(e,n)=>{let[t,r,s]=e,[a,c,i]=n,b=(t+a)/2,o=Math.sqrt(r*r+s*s),u=Math.sqrt(c*c+i*i),h=(o+u)/2,m=.5*(1-Math.sqrt(Math.pow(h,7)/(Math.pow(h,7)+Math.pow(25,7)))),l=r*(1+m),p=c*(1+m),M=Math.sqrt(l*l+s*s),f=Math.sqrt(p*p+i*i),y=(M+f)/2,C=Math.atan2(s,l)*180/Math.PI+(Math.atan2(s,l)<0?360:0),g=Math.atan2(i,p)*180/Math.PI+(Math.atan2(i,p)<0?360:0),q=g-C;Math.abs(q)>180&&(q+=g<=C?360:-360);let w=Math.abs(C-g)>180?(C+g+360)/2:(C+g)/2,Y=1-.17*Math.cos((w-30)*Math.PI/180)+.24*Math.cos(2*w*Math.PI/180)+.32*Math.cos((3*w+6)*Math.PI/180)-.2*Math.cos((4*w-63)*Math.PI/180),_=a-t,P=f-M,D=2*Math.sqrt(M*f)*Math.sin(q/2*Math.PI/180),j=1+.015*Math.pow(b-50,2)/Math.sqrt(20+Math.pow(b-50,2)),T=1+.045*y,k=1+.015*y*Y,F=30*Math.exp(-Math.pow((w-275)/25,2)),W=-(2*Math.sqrt(Math.pow(y,7)/(Math.pow(y,7)+Math.pow(25,7))))*Math.sin(2*F*Math.PI/180);return Math.sqrt(Math.pow(_/j,2)+Math.pow(P/T,2)+Math.pow(D/k,2)+W*(P/T)*(D/k))},H=e=>{let n="",t=-1/0;for(let r of e){let s=d(x(r)),[,a]=R(s);a>t&&(t=a,n=r)}return n};function z(e){let n=[e[0]];for(let t=1;t<e.length;t++){let r=e[t]-e[t-1];r>180?r-=360:r<-180&&(r+=360),n.push(n[t-1]+r)}return n}function mt(e){let[n,t,r]=e;return[.152286*n+1.052583*t-.204868*r,.114503*n+.786281*t+.099216*r,-.003882*n-.048116*t+1.051998*r]}function ht(e){let[n,t,r]=e;return[.367322*n+.860646*t-.227968*r,.280085*n+.672501*t+.047413*r,-.01182*n+.04294*t+.968881*r]}var it=e=>{let n=e.match(/\d+(\.\d+)?/g);if(!n||n.length<3)throw new Error("Invalid CSS rgb()");let t=r=>{let s=r/255;return s<=.04045?s/12.92:Math.pow((s+.055)/1.055,2.4)};return[t(Number(n[0])),t(Number(n[1])),t(Number(n[2]))]};var I=(e,n)=>(Math.max(e,n)+.05)/(Math.min(e,n)+.05),L=(e,n)=>{let t=c=>c>5e-4?c:c+Math.pow(5e-4-c,.8),r=t(e),s=t(n),a=(Math.pow(r,.56)-Math.pow(s,.56))*100;return Math.abs(a)<.1?0:(a=a>0?a<1?0:a-.25:a>-1?0:a+.25,Math.round(a))};function B(e){let n=e.match(/^(wcag|apca)(\d+)$/);if(!n)throw new Error(`Invalid contrast: ${e}`);let t=n[1]==="wcag"?Number(n[2])/10:Number(n[2]);return{system:n[1],target:t}}var U=e=>{let n={},t=e.length,r=t-1;return $.forEach(s=>{let{system:a,target:c}=B(s),i=r,b=0;for(let o=1;o<t;o++){let u=1/0;for(let h=0;h<t-o;h++){let m=a==="wcag"?I(e[h].luminance,e[h+o].luminance):Math.max(Math.abs(L(e[h+o].luminance,e[h].luminance)),Math.abs(L(e[h].luminance,e[h+o].luminance)));m<u&&(u=m)}if(u>=c){i=o,b=u;break}o===r&&(b=u)}n[s]={system:a,efficiency:i/r,target:c,span:i,value:b,name:s}}),n},$=["wcag30","wcag45","wcag70","apca45","apca60","apca75"],G=e=>{let n={};return $.forEach(t=>{var o;let{target:r,system:s}=B(t),a=e.map(u=>u.contrasts[t]),c=((o=e[0])==null?void 0:o.shades.length)||0,i=Math.max(...a.map(u=>(u==null?void 0:u.span)||0)),b=a.reduce((u,h)=>u+((h==null?void 0:h.value)||0),0);n[t]={system:s,target:r,span:i,value:b/(e.length||1),name:t,efficiency:i/(c-1||1)}}),n};var O=e=>{let n=x(e),t=d(n),r=v(n),s=A(t),a=R(t);return{hex:e,rgb:n,lab:t,lch:a,lightness:s,chroma:a[1],hue:a[2],luminance:r,parameter:0,cumDeltaE00:0,cumProtDeltaE00:0,cumDeutDeltaE00:0,wcag:I(r,1),apca:L(r,1)}};var X=e=>{if(e.length<1)return o=>0;let n=[...e].sort((o,u)=>o[0]-u[0]),t=[];for(let o=0;o<n.length;o++)(o===0||n[o][0]!==n[o-1][0])&&t.push(n[o]);let r=t.length;if(r===1)return o=>t[0][1];let s=t.map(o=>o[0]),a=t.map(o=>o[1]),c=[],i=[];for(let o=0;o<r-1;o++)c[o]=s[o+1]-s[o],i[o]=(a[o+1]-a[o])/c[o];let b=new Array(r);b[0]=i[0],b[r-1]=i[r-2];for(let o=1;o<r-1;o++){let u=i[o-1],h=i[o];if(u*h<=0)b[o]=0;else{let m=(1+c[o]/(c[o-1]+c[o]))/3;b[o]=u*h/((1-m)*u+m*h)}}return o=>{if(o<=s[0])return a[0];if(o>=s[r-1])return a[r-1];let u=0,h=r-2,m=0;for(;u<=h;){let g=Math.floor((u+h)/2);if(o>=s[g]&&o<=s[g+1]){m=g;break}o<s[g]?h=g-1:u=g+1}let l=c[m],p=(o-s[m])/l,M=p*p,f=M*p,y=b[m]*l,C=b[m+1]*l;return(2*f-3*M+1)*a[m]+(f-2*M+p)*y+(-2*f+3*M)*a[m+1]+(f-M)*C}};function S(e){let n=e.length;if(n===0)return 0;let t=0;for(let r=0;r<n;r++)t+=e[r]*e[r];return Math.sqrt(t/n)}var gt=e=>{let n=e.length;if(n===0)return{min:0,max:0,avg:0};let t=e[0],r=e[0],s=0;for(let a=0;a<n;a++){let c=e[a];c<t&&(t=c),c>r&&(r=c),s+=c}return{min:t,max:r,avg:s/n}},E=e=>{let n=e.length;if(n===0)return 0;let t=1e-6,r=e.reduce((c,i)=>c*(i+t),1),s=Math.pow(r,1/n),a=Math.max(0,Math.min(1,s));return parseFloat((a*100).toFixed(2))};var Q=(e,n,t)=>{n||(n="brand"),t||(t=[...Array(e.length).keys()]);let r=d(x(e[0]))[0],s=d(x(e[e.length-1]))[0];r>s&&e.reverse();let a=e.map(m=>O(m));for(let m=1;m<a.length;m++){let l=N(a[m-1].lab,a[m].lab);a[m].cumDeltaE00=a[m-1].cumDeltaE00+l}let c=H(e.slice(2,-2))||e[Math.floor(e.length/2)],i=e.findIndex(m=>m.toLowerCase()===(c==null?void 0:c.toLowerCase())),b=z(a.map(m=>m.hue).slice(1,-1));for(let m=1;m<a.length-1;m++)a[m].hue=b[m-1];let o=Z(a.map(m=>m.hue).slice(1,-1),a[i].hue),u=U(a),h={lightnessLinearity:V(a.map(m=>m.lightness)),chromaSmoothness:tt(a.map(m=>m.chroma)),spacingUniformity:nt(a.map(m=>m.cumDeltaE00)),contrastEfficiency:rt(u.wcag45.span,t.length),hueStability:o};return{name:n,colors:e,baseIndex:i,baseColor:c,shades:a,contrasts:u,metrics:h,score:E(Object.values(h))}},Et=e=>{let{stepNames:n,name:t,colors:r}=e,s=Object.values(r)[0],a=d(x(s[0]))[0],c=d(x(s[s.length-1]))[0],i=a>c?"darken":"lighten",b=[],o={};for(let h in r){let m=Q(r[h],h,n);b.push(m),o[h]=m.baseColor}let u={contrastEfficiency:S(b.map(h=>h.metrics.contrastEfficiency)),lightnessLinearity:S(b.map(h=>h.metrics.lightnessLinearity)),chromaSmoothness:S(b.map(h=>h.metrics.chromaSmoothness)),hueStability:S(b.map(h=>h.metrics.hueStability)),spacingUniformity:S(b.map(h=>h.metrics.spacingUniformity))};return{name:t,baseColors:o,stepNames:n,direction:i,steps:n.length,colors:r,contrasts:G(b),scales:b,metrics:u,score:E(Object.values(u))}};function V(e){let n=e.length;if(n<2)return 1;let t=0,r=0,s=0,a=0;for(let m=0;m<n;m++)t+=m,r+=e[m],s+=m*e[m],a+=m*m;let c=n*a-t*t;if(Math.abs(c)<1e-10)return 1;let i=(n*s-t*r)/c,b=(r-i*t)/n;if(Math.abs(i*(n-1))<.001)return 1;let u=0,h=0;for(let m=0;m<n;m++){let l=i*m+b,p=e[m]-l;u+=p*p;let M=Math.max(l-Math.min(b,i*(n-1)+b),Math.max(b,i*(n-1)+b)-l);h+=M*M}return Math.max(0,Math.min(1,1-Math.sqrt(u/n)/Math.sqrt(h/n)))}function Z(e,n){let t=e.length;if(t<2)return 1;let r=0,s=0;for(let a=0;a<t;a++){let c=Math.abs(e[a]-n)%360;c>180&&(c=360-c),r+=c*c;let i=a/(t-1)*180;s+=i*i}return Math.max(0,Math.min(1,1-Math.sqrt(r/t)/(Math.sqrt(s/t)||1)))}var tt=e=>{let n=e.length;if(n<3)return 1;let t=e.map(o=>o*133.8),r=Math.min(...t),s=Math.max(...t),a=t.findIndex(o=>o===s),c=X([[0,t[0]],[a,s],[n-1,t[n-1]]]),i=0,b=0;for(let o=0;o<n;o++){let u=c(o),h=t[o]-u;i+=h*h,b+=Math.pow(Math.max(u-r,s-u),2)}return Math.max(0,Math.min(1,1-Math.sqrt(i/n)/Math.sqrt(b/n)))},nt=e=>{let n=e.length;if(n<2)return 1;let t=[];for(let c=1;c<n;c++){let i=e[c]-e[c-1];if(i<0)return 0;t.push(i)}let r=t.reduce((c,i)=>c+i,0)/t.length;if(r<=1e-6)return 0;let s=0;for(let c of t)s+=Math.pow(c-r,2);let a=Math.sqrt(s/t.length)/r;return Math.max(0,Math.min(1,1/(1+a)))},rt=(e,n)=>{if(n<=1)return 1;let t=.489,r=e/n,s=t*((n-1)/n);return r<=s?1:r>=1?0:(1-r)/(1-s)};export{Q as analyzeMonochromatic,Et as analyzeMonochromaticPalette,tt as calcChromaSmoothness,rt as calcContrastEfficiency,N as calcDeltaE2000,Z as calcHueStability,V as calcLightnessLinearity,E as calcScore,nt as calcSpacingUniformity,gt as calcStatistics,$ as contrastList,X as createMonotone,it as cssRgbToRgb,H as findMaxChromaHex,at as fromLightnessEAL,L as getApcaContrast,U as getMonochromaticContrasts,G as getPaletteContrasts,v as getRelativeLuminance,I as getWcagContrast,x as hexToRgb,R as labToLch,ct as labToRgb,st as lchToLab,K as lrgbToSrgb,ot as rgbToHex,d as rgbToLab,S as rootMeanSquare,ht as simulateDeuteranopia,mt as simulateProtanopia,J as srgbToLrgb,A as toLightnessEAL,z as unwrapHue};
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "chromametry",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Web-accessible Color Palette Metrics",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "vitest",
|
|
11
|
+
"generate": "tsx benchmarks/generate.ts",
|
|
12
|
+
"build": "tsup",
|
|
13
|
+
"prepublishOnly": "npm run build && npm run test"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"color",
|
|
17
|
+
"color-palette",
|
|
18
|
+
"color-metrics",
|
|
19
|
+
"palette",
|
|
20
|
+
"monochromatic",
|
|
21
|
+
"accessibility",
|
|
22
|
+
"wcag",
|
|
23
|
+
"design-system"
|
|
24
|
+
],
|
|
25
|
+
"author": "Huu Khanh Nguyen",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@ant-design/colors": "^8.0.1",
|
|
29
|
+
"@atlaskit/tokens": "^10.0.1",
|
|
30
|
+
"@material/material-color-utilities": "^0.3.0",
|
|
31
|
+
"@types/node": "^25.2.0",
|
|
32
|
+
"sharp": "^0.34.5",
|
|
33
|
+
"tsup": "^8.5.0",
|
|
34
|
+
"tsx": "^4.21.0",
|
|
35
|
+
"typescript": "^5.8.3",
|
|
36
|
+
"vitest": "^4.0.18"
|
|
37
|
+
},
|
|
38
|
+
"unpkg": "dist/index.global.js",
|
|
39
|
+
"jsdelivr": "dist/index.global.js",
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"README.md"
|
|
43
|
+
]
|
|
44
|
+
}
|