Skip to content

Commit ee178d1

Browse files
committed
chore: add rgbToOklch script
pin cypress/code-coverage pkg version
1 parent 72695d1 commit ee178d1

File tree

4 files changed

+345
-3
lines changed

4 files changed

+345
-3
lines changed

apps/test/react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"react-dom": "^18.3.1"
2626
},
2727
"devDependencies": {
28-
"@cypress/code-coverage": "^3.12.29",
28+
"@cypress/code-coverage": "3.13.4",
2929
"@frsource/cypress-plugin-visual-regression-diff": "^3.3.10",
3030
"@frsource/frs-replace": "^4.1.1",
3131
"@storefront-ui/eslint-config": "workspace:*",

scripts/rgbColors.txt

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
--white: #FFFFFFFF;
2+
--white-opacity-50: #FFFFFF80;
3+
--black: #000000FF;
4+
--blank: #FFFFFF00;
5+
--black-opacity-50: #00000080;
6+
--Brand-200: #00E16DFF;
7+
--Neutral-900: #151A16FF;
8+
--Neutral-800: #252B27FF;
9+
--Neutral-700: #38413BFF;
10+
--Neutral-600: #4D564FFF;
11+
--Neutral-500: #646F68FF;
12+
--Neutral-400: #818C85FF;
13+
--Neutral-300: #BBC6BEFF;
14+
--Neutral-200: #D9E2DCFF;
15+
--Neutral-100: #EFF4F1FF;
16+
--Neutral-50: #F9FBFAFF;
17+
--Primary-900: #1F2924FF;
18+
--Primary-800: #32433BFF;
19+
--Primary-700: #486054FF;
20+
--Primary-600: #557263FF;
21+
--Primary-500: #709482FF;
22+
--Primary-400: #88B29DFF;
23+
--Primary-300: #9BCCB4FF;
24+
--Primary-200: #BEE4D2FF;
25+
--Primary-100: #DDF3E7FF;
26+
--Primary-50: #EEFDF3FF;
27+
--Secondary-900: #0F321EFF;
28+
--Secondary-800: #10562EFF;
29+
--Secondary-700: #0D7F3FFF;
30+
--Secondary-600: #07A14FFF;
31+
--Secondary-500: #13C360FF;
32+
--Secondary-400: #3CE078FF;
33+
--Secondary-300: #82EA9EFF;
34+
--Secondary-200: #ABF1C0FF;
35+
--Secondary-100: #D9FDE4FF;
36+
--Secondary-50: #EBFFF2FF;
37+
--Positive-900: #0F321EFF;
38+
--Positive-800: #10562EFF;
39+
--Positive-700: #0D7F3FFF;
40+
--Positive-600: #07A14FFF;
41+
--Positive-500: #13C360FF;
42+
--Positive-400: #3CE078FF;
43+
--Positive-300: #82EA9EFF;
44+
--Positive-200: #ABF1C0FF;
45+
--Positive-100: #D9FDE4FF;
46+
--Positive-50: #EBFFF2FF;
47+
--Negative-900: #4C0F17FF;
48+
--Negative-800: #8D0821FF;
49+
--Negative-700: #D00D30FF;
50+
--Negative-600: #F03B5BFF;
51+
--Negative-500: #FF7F8FFF;
52+
--Negative-400: #FFA3AFFF;
53+
--Negative-300: #FDC1CAFF;
54+
--Negative-200: #FED3DBFF;
55+
--Negative-100: #FFE8EDFF;
56+
--Negative-50: #FFF5F7FF;
57+
--Warning-900: #3E230AFF;
58+
--Warning-800: #6D3F09FF;
59+
--Warning-700: #9D5D03FF;
60+
--Warning-600: #BF7911FF;
61+
--Warning-500: #ED990EFF;
62+
--Warning-400: #FFB54DFF;
63+
--Warning-300: #FECA84FF;
64+
--Warning-200: #FEDCA5FF;
65+
--Warning-100: #FFF0D7FF;
66+
--Warning-50: #FEF7ECFF;
67+
--Disabled-900: #151A1680;
68+
--Disabled-800: #252B2780;
69+
--Disabled-700: #38413B80;
70+
--Disabled-600: #4D564F80;
71+
--Disabled-500: #646F6880;
72+
--Disabled-400: #818C8580;
73+
--Disabled-300: #BBC6BE80;
74+
--Disabled-200: #D9E2DC80;
75+
--Disabled-100: #EFF4F180;
76+
--Disabled-50: #F9FBFA80;`

scripts/rgbToOklch.js

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
// usage: node ./scripts/rgbToOklch.js > output-colors.json
2+
// taken from https://github.com/ChromeDevTools/devtools-frontend/tree/main/front_end
3+
4+
const { readFileSync } = require('fs');
5+
const path = require('path');
6+
7+
class Vector3 {
8+
values = [0, 0, 0];
9+
constructor(values) {
10+
if (values) {
11+
this.values = values;
12+
}
13+
}
14+
}
15+
class Matrix3x3 {
16+
values = [
17+
[0, 0, 0],
18+
[0, 0, 0],
19+
[0, 0, 0],
20+
];
21+
22+
constructor(values) {
23+
if (values) {
24+
this.values = values;
25+
}
26+
}
27+
28+
multiply(other) {
29+
const dst = new Vector3();
30+
for (let row = 0; row < 3; ++row) {
31+
dst.values[row] = this.values[row][0] * other.values[0] + this.values[row][1] * other.values[1] +
32+
this.values[row][2] * other.values[2];
33+
}
34+
return dst;
35+
}
36+
}
37+
class TransferFunction {
38+
g;
39+
a;
40+
b;
41+
c;
42+
d;
43+
e;
44+
f;
45+
46+
constructor(g, a, b = 0, c = 0, d = 0, e = 0, f = 0) {
47+
this.g = g;
48+
this.a = a;
49+
this.b = b;
50+
this.c = c;
51+
this.d = d;
52+
this.e = e;
53+
this.f = f;
54+
}
55+
56+
eval(val) {
57+
const sign = val < 0 ? -1.0 : 1.0;
58+
const abs = val * sign;
59+
60+
// 0 <= |encoded| < d path
61+
if (abs < this.d) {
62+
return sign * (this.c * abs + this.f);
63+
}
64+
65+
// d <= |encoded| path
66+
return sign * (Math.pow(this.a * abs + this.b, this.g) + this.e);
67+
}
68+
}
69+
function applyTransferFns(fn, r, g, b) {
70+
return [fn.eval(r), fn.eval(g), fn.eval(b)];
71+
}
72+
73+
const EPSILON = 0.01;
74+
const GAMUT_sRGB = new Matrix3x3([
75+
[0.436065674, 0.385147095, 0.143066406],
76+
[0.222488403, 0.716873169, 0.060607910],
77+
[0.013916016, 0.097076416, 0.714096069],
78+
]);
79+
const LMS_TO_OKLAB_MATRIX = new Matrix3x3([
80+
[0.2104542553, 0.7936177849999999, -0.0040720468],
81+
[1.9779984951000003, -2.4285922049999997, 0.4505937099000001],
82+
[0.025904037099999982, 0.7827717662, -0.8086757660000001],
83+
]);
84+
const XYZ_TO_LMS_MATRIX = new Matrix3x3([
85+
[0.8190224432164319, 0.3619062562801221, -0.12887378261216414],
86+
[0.0329836671980271, 0.9292868468965546, 0.03614466816999844],
87+
[0.048177199566046255, 0.26423952494422764, 0.6335478258136937],
88+
]);
89+
const XYZD50_TO_XYZD65_MATRIX = new Matrix3x3([
90+
[0.9555366447632887, -0.02306009252137888, 0.06321844147263304],
91+
[-0.028315378228764922, 1.009951351591575, 0.021026001591792402],
92+
[0.012308773293784308, -0.02050053471777469, 1.3301947294775631],
93+
]);
94+
const LMS_TO_XYZ_MATRIX = new Matrix3x3([
95+
[1.226879873374156, -0.5578149965554814, 0.2813910501772159],
96+
[-0.040575762624313734, 1.1122868293970596, -0.07171106666151703],
97+
[-0.07637294974672144, -0.4214933239627915, 1.586924024427242],
98+
]);
99+
const OKLAB_TO_LMS_MATRIX = new Matrix3x3([
100+
[0.99999999845051981432, 0.39633779217376785678, 0.21580375806075880339],
101+
[1.0000000088817607767, -0.1055613423236563494, -0.063854174771705903402],
102+
[1.0000000546724109177, -0.089484182094965759684, -1.2914855378640917399],
103+
]);
104+
const XYZD65_TO_XYZD50_MATRIX = new Matrix3x3([
105+
[1.0478573189120088, 0.022907374491829943, -0.050162247377152525],
106+
[0.029570500050499514, 0.9904755577034089, -0.017061518194840468],
107+
[-0.00924047197558879, 0.015052921526981566, 0.7519708530777581],
108+
]);
109+
const TRANSFER_sRGB = new TransferFunction(2.4, (1 / 1.055), (0.055 / 1.055), (1 / 12.92), 0.04045, 0.0, 0.0)
110+
111+
function normalizeHue(hue) {
112+
// Even though it is highly unlikely, hue can be
113+
// very negative like -400. The initial modulo
114+
// operation makes sure that the if the number is
115+
// negative, it is between [-360, 0].
116+
return ((hue % 360) + 360) % 360;
117+
}
118+
function equals(a, b, accuracy = EPSILON) {
119+
if (Array.isArray(a) && Array.isArray(b)) {
120+
if (a.length !== b.length) {
121+
return false;
122+
}
123+
for (const i in a) {
124+
if (!equals(a[i], b[i])) {
125+
return false;
126+
}
127+
}
128+
return true;
129+
}
130+
if (Array.isArray(a) || Array.isArray(b)) {
131+
return false;
132+
}
133+
if (a === null || b === null) {
134+
return a === b;
135+
}
136+
return Math.abs(a - b) < accuracy;
137+
}
138+
139+
function radToDeg(rad) {
140+
return rad * (180 / Math.PI);
141+
}
142+
function degToRad(deg) {
143+
return deg * (Math.PI / 180);
144+
}
145+
function round(v) {
146+
return Math.round(v * 1000) / 1000;
147+
}
148+
function clamp(value, { min, max }) {
149+
if (value === null) {
150+
return value;
151+
}
152+
if (min !== undefined) {
153+
value = Math.max(value, min);
154+
}
155+
if (max !== undefined) {
156+
value = Math.min(value, max);
157+
}
158+
return value;
159+
}
160+
161+
function hexToRgb(hex) {
162+
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
163+
return result ? {
164+
r: parseInt(result[1], 16),
165+
g: parseInt(result[2], 16),
166+
b: parseInt(result[3], 16)
167+
} : hex;
168+
}
169+
170+
function create(r, g, b) {
171+
let [l, c, h] = xyzd50ToOklch(...toXyzd50(r, g, b))
172+
173+
l = clamp(l, { min: 0, max: 1 });
174+
c = equals(l, 0) || equals(l, 1) ? 0 : c;
175+
c = clamp(c, { min: 0 });
176+
h = equals(c, 0) ? 0 : h;
177+
h = normalizeHue(h);
178+
179+
return [l, c, h]
180+
}
181+
182+
function srgbToXyzd50(r, g, b) {
183+
const [mappedR, mappedG, mappedB] = applyTransferFns(TRANSFER_sRGB, r, g, b);
184+
const rgbInput = new Vector3([mappedR, mappedG, mappedB]);
185+
const xyzOutput = GAMUT_sRGB.multiply(rgbInput);
186+
return xyzOutput.values;
187+
}
188+
function toXyzd50(r, g, b) {
189+
return srgbToXyzd50(r, g, b);
190+
}
191+
function labToLch(l, a, b) {
192+
return [l, Math.sqrt(a * a + b * b), radToDeg(Math.atan2(b, a))];
193+
}
194+
function xyzd65ToOklab(x, y, z) {
195+
const xyzInput = new Vector3([x, y, z]);
196+
const lmsIntermediate = XYZ_TO_LMS_MATRIX.multiply(xyzInput);
197+
198+
lmsIntermediate.values[0] = Math.pow(lmsIntermediate.values[0], 1.0 / 3.0);
199+
lmsIntermediate.values[1] = Math.pow(lmsIntermediate.values[1], 1.0 / 3.0);
200+
lmsIntermediate.values[2] = Math.pow(lmsIntermediate.values[2], 1.0 / 3.0);
201+
202+
const labOutput = LMS_TO_OKLAB_MATRIX.multiply(lmsIntermediate);
203+
return [labOutput.values[0], labOutput.values[1], labOutput.values[2]];
204+
}
205+
function xyzd50ToD65(x, y, z) {
206+
const xyzInput = new Vector3([x, y, z]);
207+
const xyzOutput = XYZD50_TO_XYZD65_MATRIX.multiply(xyzInput);
208+
return xyzOutput.values;
209+
}
210+
function xyzd50ToOklch(x, y, z) {
211+
const [x65, y65, z65] = xyzd50ToD65(x, y, z);
212+
const [l, a, b] = xyzd65ToOklab(x65, y65, z65);
213+
return labToLch(l, a, b);
214+
}
215+
function toXyzd50(l, c, h) {
216+
return oklchToXyzd50(l, c, h);
217+
}
218+
function oklchToXyzd50(lInput, c, h) {
219+
const [l, a, b] = lchToLab(lInput, c, h);
220+
const [x65, y65, z65] = oklabToXyzd65(l, a, b);
221+
return xyzd65ToD50(x65, y65, z65);
222+
}
223+
function xyzd65ToD50(x, y, z) {
224+
const xyzInput = new Vector3([x, y, z]);
225+
const xyzOutput = XYZD65_TO_XYZD50_MATRIX.multiply(xyzInput);
226+
return xyzOutput.values;
227+
}
228+
function oklabToXyzd65(l, a, b) {
229+
const labInput = new Vector3([l, a, b]);
230+
const lmsIntermediate = OKLAB_TO_LMS_MATRIX.multiply(labInput);
231+
lmsIntermediate.values[0] = lmsIntermediate.values[0] * lmsIntermediate.values[0] * lmsIntermediate.values[0];
232+
lmsIntermediate.values[1] = lmsIntermediate.values[1] * lmsIntermediate.values[1] * lmsIntermediate.values[1];
233+
lmsIntermediate.values[2] = lmsIntermediate.values[2] * lmsIntermediate.values[2] * lmsIntermediate.values[2];
234+
const xyzOutput = LMS_TO_XYZ_MATRIX.multiply(lmsIntermediate);
235+
return xyzOutput.values;
236+
}
237+
238+
function lchToLab(l, c, h) {
239+
if (h === undefined) {
240+
return [l, 0, 0];
241+
}
242+
243+
return [l, c * Math.cos(degToRad(h)), c * Math.sin(degToRad(h))];
244+
}
245+
246+
///
247+
248+
const input = readFileSync(path.join(__dirname, 'rgbColors.txt'), 'utf8');
249+
const result = input.split('\n').map(v => v.trim().split(':')).map(([variable, value]) => ({ name: variable.substring(2).toLowerCase(), value: value.trim().slice(0, -1) })).map(({ name, value }) => ({ name, value: value.slice(-2) === 'FF' ? hexToRgb(value.slice(0, -2)) : value })).reduce((p, v) => {
250+
const [name, value] = v.name.split('-');
251+
if (parseInt(value) + '' !== value) return p;
252+
if (typeof v.value === 'string') {
253+
return p;
254+
}
255+
const { r, g, b } = v.value;
256+
const [l, c, h] = create(r / 255, g / 255, b / 255);
257+
p[name] ??= {};
258+
if (isNaN(l)) {
259+
p[name][value] = v.value;
260+
return p;
261+
}
262+
p[name][value] = [round(l), round(c), round(h)].join(' ');
263+
return p;
264+
}, {});
265+
266+
console.log(JSON.stringify(result, null, 2));

yarn.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,7 +1189,7 @@ __metadata:
11891189
languageName: node
11901190
linkType: hard
11911191

1192-
"@cypress/code-coverage@npm:^3.12.29":
1192+
"@cypress/code-coverage@npm:3.13.4, @cypress/code-coverage@npm:^3.12.29":
11931193
version: 3.13.4
11941194
resolution: "@cypress/code-coverage@npm:3.13.4"
11951195
dependencies:
@@ -4394,7 +4394,7 @@ __metadata:
43944394
version: 0.0.0-use.local
43954395
resolution: "@storefront-ui/react-test@workspace:apps/test/react"
43964396
dependencies:
4397-
"@cypress/code-coverage": ^3.12.29
4397+
"@cypress/code-coverage": 3.13.4
43984398
"@frsource/cypress-plugin-visual-regression-diff": ^3.3.10
43994399
"@frsource/frs-replace": ^4.1.1
44004400
"@storefront-ui/eslint-config": "workspace:*"

0 commit comments

Comments
 (0)