├── .gitignore
├── LICENSE.md
├── README.md
├── manifest.json
├── package-lock.json
├── package.json
└── src
├── code.tsx
├── config.ts
├── lib
└── color.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Node
2 | *.log
3 | *.log.*
4 | node_modules
5 |
6 | out/
7 | dist/
8 | code.js
9 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Zach Inglis
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Constrast Check
2 |
3 | A simple contrast checker widget to put in to your styleguides.
4 |
5 | [Figma Plugin]:(https://www.figma.com/community/widget/1123669584321839030/Contrast-Checker---Easy-testing)
6 |
7 | Click the widget to input the foreground color. The background color is inferred by the parent frame. There are options to test against: AA, AAA, Large Text, Normal Text.
8 |
9 | (This was a quick and dirty plugin built in a day. So please excuse anything that's not perfect and feel free to make PRs.) It definitely needs a refactor and cleanup.
10 |
11 | ## Building
12 |
13 | To build run `npm run build`
14 |
15 | ## Future Plans & Wants
16 | - Recalculates when moved to a new frame
17 | - Close Pass / Close Fail
18 |
19 | ## Authors
20 | - Zach Inglis - [Twitter](https://twitter.com/zachinglis)
21 |
22 | ## With Thanks
23 | - https://remixicon.com/
24 | - https://css-tricks.com/converting-color-spaces-in-javascript/
25 | - https://blog.cristiana.tech/calculating-color-contrast-in-typescript-using-web-content-accessibility-guidelines-wcag
26 | - https://dev.to/alvaromontoro/building-your-own-color-contrast-checker-4j7o
27 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Contrast Check",
3 | "id": "1123669584321839030",
4 | "api": "1.0.0",
5 | "main": "dist/code.js",
6 | "editorType": ["figma"],
7 | "containsWidget": true,
8 | "widgetApi": "1.0.0"
9 | }
10 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "figma_widget_contrast_check",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "figma_widget_contrast_check",
9 | "version": "1.0.0",
10 | "license": "MIT",
11 | "dependencies": {
12 | "concurrently": "^7.2.2",
13 | "esbuild": "^0.14.48"
14 | },
15 | "devDependencies": {
16 | "@figma/plugin-typings": "*",
17 | "@figma/widget-typings": "*",
18 | "typescript": "^4.7.4"
19 | }
20 | },
21 | "node_modules/@figma/plugin-typings": {
22 | "version": "1.49.0",
23 | "resolved": "https://registry.npmjs.org/@figma/plugin-typings/-/plugin-typings-1.49.0.tgz",
24 | "integrity": "sha512-72tKC61RPkrBZ+uzj+TtL3bVAmxahZKv/TfElbrY9sBoZvr4/+4gw7hnJYiln5xBJHiJ4KTcjUy+6D1qUuWlTA==",
25 | "dev": true
26 | },
27 | "node_modules/@figma/widget-typings": {
28 | "version": "1.4.0",
29 | "resolved": "https://registry.npmjs.org/@figma/widget-typings/-/widget-typings-1.4.0.tgz",
30 | "integrity": "sha512-1wvCA0mFTzvsNUkdVhV6kT2sp3B9r0ex2vSEq59jysVZrWJxjljc9uvGM4C+va11ktcOVqgjzuCHNhWDtvugXQ==",
31 | "dev": true,
32 | "peerDependencies": {
33 | "@figma/plugin-typings": "1.x"
34 | }
35 | },
36 | "node_modules/ansi-regex": {
37 | "version": "5.0.1",
38 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
39 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
40 | "engines": {
41 | "node": ">=8"
42 | }
43 | },
44 | "node_modules/ansi-styles": {
45 | "version": "4.3.0",
46 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
47 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
48 | "dependencies": {
49 | "color-convert": "^2.0.1"
50 | },
51 | "engines": {
52 | "node": ">=8"
53 | },
54 | "funding": {
55 | "url": "https://github.com/chalk/ansi-styles?sponsor=1"
56 | }
57 | },
58 | "node_modules/chalk": {
59 | "version": "4.1.2",
60 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
61 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
62 | "dependencies": {
63 | "ansi-styles": "^4.1.0",
64 | "supports-color": "^7.1.0"
65 | },
66 | "engines": {
67 | "node": ">=10"
68 | },
69 | "funding": {
70 | "url": "https://github.com/chalk/chalk?sponsor=1"
71 | }
72 | },
73 | "node_modules/chalk/node_modules/supports-color": {
74 | "version": "7.2.0",
75 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
76 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
77 | "dependencies": {
78 | "has-flag": "^4.0.0"
79 | },
80 | "engines": {
81 | "node": ">=8"
82 | }
83 | },
84 | "node_modules/cliui": {
85 | "version": "7.0.4",
86 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
87 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
88 | "dependencies": {
89 | "string-width": "^4.2.0",
90 | "strip-ansi": "^6.0.0",
91 | "wrap-ansi": "^7.0.0"
92 | }
93 | },
94 | "node_modules/color-convert": {
95 | "version": "2.0.1",
96 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
97 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
98 | "dependencies": {
99 | "color-name": "~1.1.4"
100 | },
101 | "engines": {
102 | "node": ">=7.0.0"
103 | }
104 | },
105 | "node_modules/color-name": {
106 | "version": "1.1.4",
107 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
108 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
109 | },
110 | "node_modules/concurrently": {
111 | "version": "7.2.2",
112 | "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.2.2.tgz",
113 | "integrity": "sha512-DcQkI0ruil5BA/g7Xy3EWySGrFJovF5RYAYxwGvv9Jf9q9B1v3jPFP2tl6axExNf1qgF30kjoNYrangZ0ey4Aw==",
114 | "dependencies": {
115 | "chalk": "^4.1.0",
116 | "date-fns": "^2.16.1",
117 | "lodash": "^4.17.21",
118 | "rxjs": "^7.0.0",
119 | "shell-quote": "^1.7.3",
120 | "spawn-command": "^0.0.2-1",
121 | "supports-color": "^8.1.0",
122 | "tree-kill": "^1.2.2",
123 | "yargs": "^17.3.1"
124 | },
125 | "bin": {
126 | "concurrently": "dist/bin/concurrently.js"
127 | },
128 | "engines": {
129 | "node": "^12.20.0 || ^14.13.0 || >=16.0.0"
130 | }
131 | },
132 | "node_modules/date-fns": {
133 | "version": "2.28.0",
134 | "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
135 | "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==",
136 | "engines": {
137 | "node": ">=0.11"
138 | },
139 | "funding": {
140 | "type": "opencollective",
141 | "url": "https://opencollective.com/date-fns"
142 | }
143 | },
144 | "node_modules/emoji-regex": {
145 | "version": "8.0.0",
146 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
147 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
148 | },
149 | "node_modules/esbuild": {
150 | "version": "0.14.48",
151 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.48.tgz",
152 | "integrity": "sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==",
153 | "hasInstallScript": true,
154 | "bin": {
155 | "esbuild": "bin/esbuild"
156 | },
157 | "engines": {
158 | "node": ">=12"
159 | },
160 | "optionalDependencies": {
161 | "esbuild-android-64": "0.14.48",
162 | "esbuild-android-arm64": "0.14.48",
163 | "esbuild-darwin-64": "0.14.48",
164 | "esbuild-darwin-arm64": "0.14.48",
165 | "esbuild-freebsd-64": "0.14.48",
166 | "esbuild-freebsd-arm64": "0.14.48",
167 | "esbuild-linux-32": "0.14.48",
168 | "esbuild-linux-64": "0.14.48",
169 | "esbuild-linux-arm": "0.14.48",
170 | "esbuild-linux-arm64": "0.14.48",
171 | "esbuild-linux-mips64le": "0.14.48",
172 | "esbuild-linux-ppc64le": "0.14.48",
173 | "esbuild-linux-riscv64": "0.14.48",
174 | "esbuild-linux-s390x": "0.14.48",
175 | "esbuild-netbsd-64": "0.14.48",
176 | "esbuild-openbsd-64": "0.14.48",
177 | "esbuild-sunos-64": "0.14.48",
178 | "esbuild-windows-32": "0.14.48",
179 | "esbuild-windows-64": "0.14.48",
180 | "esbuild-windows-arm64": "0.14.48"
181 | }
182 | },
183 | "node_modules/esbuild-android-64": {
184 | "version": "0.14.48",
185 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.48.tgz",
186 | "integrity": "sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg==",
187 | "cpu": [
188 | "x64"
189 | ],
190 | "optional": true,
191 | "os": [
192 | "android"
193 | ],
194 | "engines": {
195 | "node": ">=12"
196 | }
197 | },
198 | "node_modules/esbuild-android-arm64": {
199 | "version": "0.14.48",
200 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.48.tgz",
201 | "integrity": "sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g==",
202 | "cpu": [
203 | "arm64"
204 | ],
205 | "optional": true,
206 | "os": [
207 | "android"
208 | ],
209 | "engines": {
210 | "node": ">=12"
211 | }
212 | },
213 | "node_modules/esbuild-darwin-64": {
214 | "version": "0.14.48",
215 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.48.tgz",
216 | "integrity": "sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA==",
217 | "cpu": [
218 | "x64"
219 | ],
220 | "optional": true,
221 | "os": [
222 | "darwin"
223 | ],
224 | "engines": {
225 | "node": ">=12"
226 | }
227 | },
228 | "node_modules/esbuild-darwin-arm64": {
229 | "version": "0.14.48",
230 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz",
231 | "integrity": "sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==",
232 | "cpu": [
233 | "arm64"
234 | ],
235 | "optional": true,
236 | "os": [
237 | "darwin"
238 | ],
239 | "engines": {
240 | "node": ">=12"
241 | }
242 | },
243 | "node_modules/esbuild-freebsd-64": {
244 | "version": "0.14.48",
245 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.48.tgz",
246 | "integrity": "sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA==",
247 | "cpu": [
248 | "x64"
249 | ],
250 | "optional": true,
251 | "os": [
252 | "freebsd"
253 | ],
254 | "engines": {
255 | "node": ">=12"
256 | }
257 | },
258 | "node_modules/esbuild-freebsd-arm64": {
259 | "version": "0.14.48",
260 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.48.tgz",
261 | "integrity": "sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw==",
262 | "cpu": [
263 | "arm64"
264 | ],
265 | "optional": true,
266 | "os": [
267 | "freebsd"
268 | ],
269 | "engines": {
270 | "node": ">=12"
271 | }
272 | },
273 | "node_modules/esbuild-linux-32": {
274 | "version": "0.14.48",
275 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.48.tgz",
276 | "integrity": "sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ==",
277 | "cpu": [
278 | "ia32"
279 | ],
280 | "optional": true,
281 | "os": [
282 | "linux"
283 | ],
284 | "engines": {
285 | "node": ">=12"
286 | }
287 | },
288 | "node_modules/esbuild-linux-64": {
289 | "version": "0.14.48",
290 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.48.tgz",
291 | "integrity": "sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug==",
292 | "cpu": [
293 | "x64"
294 | ],
295 | "optional": true,
296 | "os": [
297 | "linux"
298 | ],
299 | "engines": {
300 | "node": ">=12"
301 | }
302 | },
303 | "node_modules/esbuild-linux-arm": {
304 | "version": "0.14.48",
305 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.48.tgz",
306 | "integrity": "sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw==",
307 | "cpu": [
308 | "arm"
309 | ],
310 | "optional": true,
311 | "os": [
312 | "linux"
313 | ],
314 | "engines": {
315 | "node": ">=12"
316 | }
317 | },
318 | "node_modules/esbuild-linux-arm64": {
319 | "version": "0.14.48",
320 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.48.tgz",
321 | "integrity": "sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw==",
322 | "cpu": [
323 | "arm64"
324 | ],
325 | "optional": true,
326 | "os": [
327 | "linux"
328 | ],
329 | "engines": {
330 | "node": ">=12"
331 | }
332 | },
333 | "node_modules/esbuild-linux-mips64le": {
334 | "version": "0.14.48",
335 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.48.tgz",
336 | "integrity": "sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA==",
337 | "cpu": [
338 | "mips64el"
339 | ],
340 | "optional": true,
341 | "os": [
342 | "linux"
343 | ],
344 | "engines": {
345 | "node": ">=12"
346 | }
347 | },
348 | "node_modules/esbuild-linux-ppc64le": {
349 | "version": "0.14.48",
350 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.48.tgz",
351 | "integrity": "sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ==",
352 | "cpu": [
353 | "ppc64"
354 | ],
355 | "optional": true,
356 | "os": [
357 | "linux"
358 | ],
359 | "engines": {
360 | "node": ">=12"
361 | }
362 | },
363 | "node_modules/esbuild-linux-riscv64": {
364 | "version": "0.14.48",
365 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.48.tgz",
366 | "integrity": "sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA==",
367 | "cpu": [
368 | "riscv64"
369 | ],
370 | "optional": true,
371 | "os": [
372 | "linux"
373 | ],
374 | "engines": {
375 | "node": ">=12"
376 | }
377 | },
378 | "node_modules/esbuild-linux-s390x": {
379 | "version": "0.14.48",
380 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.48.tgz",
381 | "integrity": "sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g==",
382 | "cpu": [
383 | "s390x"
384 | ],
385 | "optional": true,
386 | "os": [
387 | "linux"
388 | ],
389 | "engines": {
390 | "node": ">=12"
391 | }
392 | },
393 | "node_modules/esbuild-netbsd-64": {
394 | "version": "0.14.48",
395 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.48.tgz",
396 | "integrity": "sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw==",
397 | "cpu": [
398 | "x64"
399 | ],
400 | "optional": true,
401 | "os": [
402 | "netbsd"
403 | ],
404 | "engines": {
405 | "node": ">=12"
406 | }
407 | },
408 | "node_modules/esbuild-openbsd-64": {
409 | "version": "0.14.48",
410 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.48.tgz",
411 | "integrity": "sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA==",
412 | "cpu": [
413 | "x64"
414 | ],
415 | "optional": true,
416 | "os": [
417 | "openbsd"
418 | ],
419 | "engines": {
420 | "node": ">=12"
421 | }
422 | },
423 | "node_modules/esbuild-sunos-64": {
424 | "version": "0.14.48",
425 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.48.tgz",
426 | "integrity": "sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA==",
427 | "cpu": [
428 | "x64"
429 | ],
430 | "optional": true,
431 | "os": [
432 | "sunos"
433 | ],
434 | "engines": {
435 | "node": ">=12"
436 | }
437 | },
438 | "node_modules/esbuild-windows-32": {
439 | "version": "0.14.48",
440 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.48.tgz",
441 | "integrity": "sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg==",
442 | "cpu": [
443 | "ia32"
444 | ],
445 | "optional": true,
446 | "os": [
447 | "win32"
448 | ],
449 | "engines": {
450 | "node": ">=12"
451 | }
452 | },
453 | "node_modules/esbuild-windows-64": {
454 | "version": "0.14.48",
455 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.48.tgz",
456 | "integrity": "sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA==",
457 | "cpu": [
458 | "x64"
459 | ],
460 | "optional": true,
461 | "os": [
462 | "win32"
463 | ],
464 | "engines": {
465 | "node": ">=12"
466 | }
467 | },
468 | "node_modules/esbuild-windows-arm64": {
469 | "version": "0.14.48",
470 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.48.tgz",
471 | "integrity": "sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g==",
472 | "cpu": [
473 | "arm64"
474 | ],
475 | "optional": true,
476 | "os": [
477 | "win32"
478 | ],
479 | "engines": {
480 | "node": ">=12"
481 | }
482 | },
483 | "node_modules/escalade": {
484 | "version": "3.1.1",
485 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
486 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
487 | "engines": {
488 | "node": ">=6"
489 | }
490 | },
491 | "node_modules/get-caller-file": {
492 | "version": "2.0.5",
493 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
494 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
495 | "engines": {
496 | "node": "6.* || 8.* || >= 10.*"
497 | }
498 | },
499 | "node_modules/has-flag": {
500 | "version": "4.0.0",
501 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
502 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
503 | "engines": {
504 | "node": ">=8"
505 | }
506 | },
507 | "node_modules/is-fullwidth-code-point": {
508 | "version": "3.0.0",
509 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
510 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
511 | "engines": {
512 | "node": ">=8"
513 | }
514 | },
515 | "node_modules/lodash": {
516 | "version": "4.17.21",
517 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
518 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
519 | },
520 | "node_modules/require-directory": {
521 | "version": "2.1.1",
522 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
523 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
524 | "engines": {
525 | "node": ">=0.10.0"
526 | }
527 | },
528 | "node_modules/rxjs": {
529 | "version": "7.5.5",
530 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz",
531 | "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==",
532 | "dependencies": {
533 | "tslib": "^2.1.0"
534 | }
535 | },
536 | "node_modules/shell-quote": {
537 | "version": "1.7.3",
538 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz",
539 | "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw=="
540 | },
541 | "node_modules/spawn-command": {
542 | "version": "0.0.2-1",
543 | "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
544 | "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg=="
545 | },
546 | "node_modules/string-width": {
547 | "version": "4.2.3",
548 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
549 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
550 | "dependencies": {
551 | "emoji-regex": "^8.0.0",
552 | "is-fullwidth-code-point": "^3.0.0",
553 | "strip-ansi": "^6.0.1"
554 | },
555 | "engines": {
556 | "node": ">=8"
557 | }
558 | },
559 | "node_modules/strip-ansi": {
560 | "version": "6.0.1",
561 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
562 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
563 | "dependencies": {
564 | "ansi-regex": "^5.0.1"
565 | },
566 | "engines": {
567 | "node": ">=8"
568 | }
569 | },
570 | "node_modules/supports-color": {
571 | "version": "8.1.1",
572 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
573 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
574 | "dependencies": {
575 | "has-flag": "^4.0.0"
576 | },
577 | "engines": {
578 | "node": ">=10"
579 | },
580 | "funding": {
581 | "url": "https://github.com/chalk/supports-color?sponsor=1"
582 | }
583 | },
584 | "node_modules/tree-kill": {
585 | "version": "1.2.2",
586 | "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
587 | "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
588 | "bin": {
589 | "tree-kill": "cli.js"
590 | }
591 | },
592 | "node_modules/tslib": {
593 | "version": "2.4.0",
594 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
595 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
596 | },
597 | "node_modules/typescript": {
598 | "version": "4.7.4",
599 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
600 | "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
601 | "dev": true,
602 | "bin": {
603 | "tsc": "bin/tsc",
604 | "tsserver": "bin/tsserver"
605 | },
606 | "engines": {
607 | "node": ">=4.2.0"
608 | }
609 | },
610 | "node_modules/wrap-ansi": {
611 | "version": "7.0.0",
612 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
613 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
614 | "dependencies": {
615 | "ansi-styles": "^4.0.0",
616 | "string-width": "^4.1.0",
617 | "strip-ansi": "^6.0.0"
618 | },
619 | "engines": {
620 | "node": ">=10"
621 | },
622 | "funding": {
623 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
624 | }
625 | },
626 | "node_modules/y18n": {
627 | "version": "5.0.8",
628 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
629 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
630 | "engines": {
631 | "node": ">=10"
632 | }
633 | },
634 | "node_modules/yargs": {
635 | "version": "17.5.1",
636 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
637 | "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==",
638 | "dependencies": {
639 | "cliui": "^7.0.2",
640 | "escalade": "^3.1.1",
641 | "get-caller-file": "^2.0.5",
642 | "require-directory": "^2.1.1",
643 | "string-width": "^4.2.3",
644 | "y18n": "^5.0.5",
645 | "yargs-parser": "^21.0.0"
646 | },
647 | "engines": {
648 | "node": ">=12"
649 | }
650 | },
651 | "node_modules/yargs-parser": {
652 | "version": "21.0.1",
653 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
654 | "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==",
655 | "engines": {
656 | "node": ">=12"
657 | }
658 | }
659 | },
660 | "dependencies": {
661 | "@figma/plugin-typings": {
662 | "version": "1.49.0",
663 | "resolved": "https://registry.npmjs.org/@figma/plugin-typings/-/plugin-typings-1.49.0.tgz",
664 | "integrity": "sha512-72tKC61RPkrBZ+uzj+TtL3bVAmxahZKv/TfElbrY9sBoZvr4/+4gw7hnJYiln5xBJHiJ4KTcjUy+6D1qUuWlTA==",
665 | "dev": true
666 | },
667 | "@figma/widget-typings": {
668 | "version": "1.4.0",
669 | "resolved": "https://registry.npmjs.org/@figma/widget-typings/-/widget-typings-1.4.0.tgz",
670 | "integrity": "sha512-1wvCA0mFTzvsNUkdVhV6kT2sp3B9r0ex2vSEq59jysVZrWJxjljc9uvGM4C+va11ktcOVqgjzuCHNhWDtvugXQ==",
671 | "dev": true,
672 | "requires": {}
673 | },
674 | "ansi-regex": {
675 | "version": "5.0.1",
676 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
677 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
678 | },
679 | "ansi-styles": {
680 | "version": "4.3.0",
681 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
682 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
683 | "requires": {
684 | "color-convert": "^2.0.1"
685 | }
686 | },
687 | "chalk": {
688 | "version": "4.1.2",
689 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
690 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
691 | "requires": {
692 | "ansi-styles": "^4.1.0",
693 | "supports-color": "^7.1.0"
694 | },
695 | "dependencies": {
696 | "supports-color": {
697 | "version": "7.2.0",
698 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
699 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
700 | "requires": {
701 | "has-flag": "^4.0.0"
702 | }
703 | }
704 | }
705 | },
706 | "cliui": {
707 | "version": "7.0.4",
708 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
709 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
710 | "requires": {
711 | "string-width": "^4.2.0",
712 | "strip-ansi": "^6.0.0",
713 | "wrap-ansi": "^7.0.0"
714 | }
715 | },
716 | "color-convert": {
717 | "version": "2.0.1",
718 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
719 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
720 | "requires": {
721 | "color-name": "~1.1.4"
722 | }
723 | },
724 | "color-name": {
725 | "version": "1.1.4",
726 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
727 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
728 | },
729 | "concurrently": {
730 | "version": "7.2.2",
731 | "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.2.2.tgz",
732 | "integrity": "sha512-DcQkI0ruil5BA/g7Xy3EWySGrFJovF5RYAYxwGvv9Jf9q9B1v3jPFP2tl6axExNf1qgF30kjoNYrangZ0ey4Aw==",
733 | "requires": {
734 | "chalk": "^4.1.0",
735 | "date-fns": "^2.16.1",
736 | "lodash": "^4.17.21",
737 | "rxjs": "^7.0.0",
738 | "shell-quote": "^1.7.3",
739 | "spawn-command": "^0.0.2-1",
740 | "supports-color": "^8.1.0",
741 | "tree-kill": "^1.2.2",
742 | "yargs": "^17.3.1"
743 | }
744 | },
745 | "date-fns": {
746 | "version": "2.28.0",
747 | "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
748 | "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw=="
749 | },
750 | "emoji-regex": {
751 | "version": "8.0.0",
752 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
753 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
754 | },
755 | "esbuild": {
756 | "version": "0.14.48",
757 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.48.tgz",
758 | "integrity": "sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==",
759 | "requires": {
760 | "esbuild-android-64": "0.14.48",
761 | "esbuild-android-arm64": "0.14.48",
762 | "esbuild-darwin-64": "0.14.48",
763 | "esbuild-darwin-arm64": "0.14.48",
764 | "esbuild-freebsd-64": "0.14.48",
765 | "esbuild-freebsd-arm64": "0.14.48",
766 | "esbuild-linux-32": "0.14.48",
767 | "esbuild-linux-64": "0.14.48",
768 | "esbuild-linux-arm": "0.14.48",
769 | "esbuild-linux-arm64": "0.14.48",
770 | "esbuild-linux-mips64le": "0.14.48",
771 | "esbuild-linux-ppc64le": "0.14.48",
772 | "esbuild-linux-riscv64": "0.14.48",
773 | "esbuild-linux-s390x": "0.14.48",
774 | "esbuild-netbsd-64": "0.14.48",
775 | "esbuild-openbsd-64": "0.14.48",
776 | "esbuild-sunos-64": "0.14.48",
777 | "esbuild-windows-32": "0.14.48",
778 | "esbuild-windows-64": "0.14.48",
779 | "esbuild-windows-arm64": "0.14.48"
780 | }
781 | },
782 | "esbuild-android-64": {
783 | "version": "0.14.48",
784 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.48.tgz",
785 | "integrity": "sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg==",
786 | "optional": true
787 | },
788 | "esbuild-android-arm64": {
789 | "version": "0.14.48",
790 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.48.tgz",
791 | "integrity": "sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g==",
792 | "optional": true
793 | },
794 | "esbuild-darwin-64": {
795 | "version": "0.14.48",
796 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.48.tgz",
797 | "integrity": "sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA==",
798 | "optional": true
799 | },
800 | "esbuild-darwin-arm64": {
801 | "version": "0.14.48",
802 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz",
803 | "integrity": "sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==",
804 | "optional": true
805 | },
806 | "esbuild-freebsd-64": {
807 | "version": "0.14.48",
808 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.48.tgz",
809 | "integrity": "sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA==",
810 | "optional": true
811 | },
812 | "esbuild-freebsd-arm64": {
813 | "version": "0.14.48",
814 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.48.tgz",
815 | "integrity": "sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw==",
816 | "optional": true
817 | },
818 | "esbuild-linux-32": {
819 | "version": "0.14.48",
820 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.48.tgz",
821 | "integrity": "sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ==",
822 | "optional": true
823 | },
824 | "esbuild-linux-64": {
825 | "version": "0.14.48",
826 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.48.tgz",
827 | "integrity": "sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug==",
828 | "optional": true
829 | },
830 | "esbuild-linux-arm": {
831 | "version": "0.14.48",
832 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.48.tgz",
833 | "integrity": "sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw==",
834 | "optional": true
835 | },
836 | "esbuild-linux-arm64": {
837 | "version": "0.14.48",
838 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.48.tgz",
839 | "integrity": "sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw==",
840 | "optional": true
841 | },
842 | "esbuild-linux-mips64le": {
843 | "version": "0.14.48",
844 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.48.tgz",
845 | "integrity": "sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA==",
846 | "optional": true
847 | },
848 | "esbuild-linux-ppc64le": {
849 | "version": "0.14.48",
850 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.48.tgz",
851 | "integrity": "sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ==",
852 | "optional": true
853 | },
854 | "esbuild-linux-riscv64": {
855 | "version": "0.14.48",
856 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.48.tgz",
857 | "integrity": "sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA==",
858 | "optional": true
859 | },
860 | "esbuild-linux-s390x": {
861 | "version": "0.14.48",
862 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.48.tgz",
863 | "integrity": "sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g==",
864 | "optional": true
865 | },
866 | "esbuild-netbsd-64": {
867 | "version": "0.14.48",
868 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.48.tgz",
869 | "integrity": "sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw==",
870 | "optional": true
871 | },
872 | "esbuild-openbsd-64": {
873 | "version": "0.14.48",
874 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.48.tgz",
875 | "integrity": "sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA==",
876 | "optional": true
877 | },
878 | "esbuild-sunos-64": {
879 | "version": "0.14.48",
880 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.48.tgz",
881 | "integrity": "sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA==",
882 | "optional": true
883 | },
884 | "esbuild-windows-32": {
885 | "version": "0.14.48",
886 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.48.tgz",
887 | "integrity": "sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg==",
888 | "optional": true
889 | },
890 | "esbuild-windows-64": {
891 | "version": "0.14.48",
892 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.48.tgz",
893 | "integrity": "sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA==",
894 | "optional": true
895 | },
896 | "esbuild-windows-arm64": {
897 | "version": "0.14.48",
898 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.48.tgz",
899 | "integrity": "sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g==",
900 | "optional": true
901 | },
902 | "escalade": {
903 | "version": "3.1.1",
904 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
905 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
906 | },
907 | "get-caller-file": {
908 | "version": "2.0.5",
909 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
910 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
911 | },
912 | "has-flag": {
913 | "version": "4.0.0",
914 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
915 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
916 | },
917 | "is-fullwidth-code-point": {
918 | "version": "3.0.0",
919 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
920 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
921 | },
922 | "lodash": {
923 | "version": "4.17.21",
924 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
925 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
926 | },
927 | "require-directory": {
928 | "version": "2.1.1",
929 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
930 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
931 | },
932 | "rxjs": {
933 | "version": "7.5.5",
934 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz",
935 | "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==",
936 | "requires": {
937 | "tslib": "^2.1.0"
938 | }
939 | },
940 | "shell-quote": {
941 | "version": "1.7.3",
942 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz",
943 | "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw=="
944 | },
945 | "spawn-command": {
946 | "version": "0.0.2-1",
947 | "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
948 | "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg=="
949 | },
950 | "string-width": {
951 | "version": "4.2.3",
952 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
953 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
954 | "requires": {
955 | "emoji-regex": "^8.0.0",
956 | "is-fullwidth-code-point": "^3.0.0",
957 | "strip-ansi": "^6.0.1"
958 | }
959 | },
960 | "strip-ansi": {
961 | "version": "6.0.1",
962 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
963 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
964 | "requires": {
965 | "ansi-regex": "^5.0.1"
966 | }
967 | },
968 | "supports-color": {
969 | "version": "8.1.1",
970 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
971 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
972 | "requires": {
973 | "has-flag": "^4.0.0"
974 | }
975 | },
976 | "tree-kill": {
977 | "version": "1.2.2",
978 | "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
979 | "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="
980 | },
981 | "tslib": {
982 | "version": "2.4.0",
983 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
984 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
985 | },
986 | "typescript": {
987 | "version": "4.7.4",
988 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
989 | "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
990 | "dev": true
991 | },
992 | "wrap-ansi": {
993 | "version": "7.0.0",
994 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
995 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
996 | "requires": {
997 | "ansi-styles": "^4.0.0",
998 | "string-width": "^4.1.0",
999 | "strip-ansi": "^6.0.0"
1000 | }
1001 | },
1002 | "y18n": {
1003 | "version": "5.0.8",
1004 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
1005 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
1006 | },
1007 | "yargs": {
1008 | "version": "17.5.1",
1009 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
1010 | "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==",
1011 | "requires": {
1012 | "cliui": "^7.0.2",
1013 | "escalade": "^3.1.1",
1014 | "get-caller-file": "^2.0.5",
1015 | "require-directory": "^2.1.1",
1016 | "string-width": "^4.2.3",
1017 | "y18n": "^5.0.5",
1018 | "yargs-parser": "^21.0.0"
1019 | }
1020 | },
1021 | "yargs-parser": {
1022 | "version": "21.0.1",
1023 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
1024 | "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg=="
1025 | }
1026 | }
1027 | }
1028 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "figma_widget_contrast_check",
3 | "version": "1.1.0",
4 | "description": "Contrast Check figma widget",
5 | "main": "code.js",
6 | "scripts": {
7 | "test": "tsc -p src --noEmit",
8 | "build": "esbuild src/code.tsx --bundle --outfile=dist/code.js",
9 | "dev": "concurrently -n tsc,build 'npm run test -- --preserveWatchOutput --watch' 'npm run build -- --watch'"
10 | },
11 | "author": "Zach Inglis",
12 | "license": "MIT License",
13 | "devDependencies": {
14 | "@figma/plugin-typings": "*",
15 | "@figma/widget-typings": "*",
16 | "concurrently": "^7.2.2",
17 | "esbuild": "^0.14.48"
18 | },
19 | "dependencies": {
20 | "typescript": "^4.7.4"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/code.tsx:
--------------------------------------------------------------------------------
1 | interface checkContrastArgs {
2 | foregroundColor?: string
3 | ratioLevel?: string
4 | fontSize?: string
5 | }
6 |
7 | import { RGBToHex, getCurrentRatio, doesPass, toPrettyRatio } from './lib/color'
8 |
9 | const { widget, notify } = figma
10 | const { useSyncedState, usePropertyMenu, AutoLayout, Rectangle, SVG, Text, Input, useWidgetId } = widget
11 |
12 | function Widget() {
13 | //#section Initial State
14 | const [foregroundColor, setForegroundColor] = useSyncedState('foregroundColor', '#000000')
15 | const [backgroundColor, setBackgroundColor] = useSyncedState('parentColor', '#ffffff')
16 | const [ratio, setRatio] = useSyncedState('ratio', '0')
17 | const [prettyRatio, setPrettyRatio] = useSyncedState('prettyRatio', '21:1')
18 | const [passes, setPasses] = useSyncedState('passes', 'false')
19 |
20 | const [optionRatioLevel, setOptionRatioLevel] = useSyncedState('optinRatioLevel', 'AA')
21 | const [optionFontSize, setOptionFontSize] = useSyncedState('optionFontSize', 'large')
22 | const [optionSize, setOptionSize] = useSyncedState('optionSize', 'large')
23 | const [settingsOpened, setSettingsOpened] = useSyncedState('settignsOpened', false)
24 |
25 | const id = useWidgetId()
26 | //#endregion
27 | //#region Helper methods - Colors & Contrast
28 |
29 | //#endregion
30 | //#region Helper methods - Nodes
31 | function getParentFrame(node: WidgetNode): FrameNode | undefined {
32 | if(!node.parent) return
33 |
34 | if(node.parent.type === 'FRAME') {
35 | return node.parent as FrameNode
36 | } else {
37 | return getParentFrame(node.parent as WidgetNode)
38 | }
39 | }
40 |
41 | function getParentFrameHex(node: WidgetNode): string | undefined {
42 | const parent = getParentFrame(node)
43 |
44 | if(!parent) {
45 | notify("Widget must be inside a frame", { error: true })
46 | return
47 | }
48 |
49 | const fill = parent.fills[0]
50 | if(fill === undefined) {
51 | notify("Parent frame needs a fill", { error: true })
52 | return
53 | }
54 | if(fill.type !== 'SOLID') {
55 | notify("Parent frame fill is not a solid color", { error: true })
56 | return
57 | }
58 |
59 | return RGBToHex(fill.color)
60 | }
61 | //#endregion
62 |
63 | function setPassOrFail(args: checkContrastArgs = {}) {
64 | const node = figma.getNodeById(id) as WidgetNode
65 |
66 | let fgColor = args.foregroundColor || foregroundColor
67 | let bgColor = getParentFrameHex(node)
68 | let ratioLevel = args.ratioLevel || optionRatioLevel
69 | let fontSize = args.fontSize || optionFontSize
70 |
71 | if(!foregroundColor || !backgroundColor) return
72 |
73 | const newRatio = getCurrentRatio(fgColor, bgColor)
74 | const newValidity = doesPass(newRatio, ratioLevel, fontSize)
75 |
76 | // if(args.foregroundColor) setForegroundColor(fgColor)
77 | // setBackgroundColor(backgroundColor)
78 | // setRatio(String(newRatio))
79 | // const newPrettyRatio = toPrettyRatio(newRatio)
80 | // setPrettyRatio(newPrettyRatio)
81 | // setPasses(String(newValidity))
82 |
83 | // node.name = `Contrast Checker (${newValidity === true ? 'PASS' : 'FAIL'} / ${newPrettyRatio})`
84 | }
85 |
86 | //#region Settings
87 | const propertyIconColor = "#BCBCBC"
88 | usePropertyMenu([
89 | {
90 | itemType: 'dropdown',
91 | tooltip: 'WCAG Level',
92 | propertyName: 'ratio',
93 | options: [
94 | { label: 'AA', option: 'AA' },
95 | { label: 'AAA', option: 'AAA' },
96 | ],
97 | selectedOption: optionRatioLevel
98 | },
99 | {
100 | itemType: 'dropdown',
101 | tooltip: 'Size of the text',
102 | propertyName: 'text',
103 | options: [
104 | { label: 'Normal Text', option: 'normal' },
105 | { label: 'Large Text', option: 'large' },
106 | ],
107 | selectedOption: optionFontSize
108 | },
109 | {
110 | itemType: 'separator',
111 | },
112 | {
113 | itemType: 'dropdown',
114 | tooltip: 'Change widget size',
115 | propertyName: 'size',
116 | options: [
117 | { label: 'Tiny Widget', option: 'tiny' },
118 | { label: 'Small Widget', option: 'small' },
119 | { label: 'Large Widget', option: 'large' },
120 | ],
121 | selectedOption: optionSize
122 | },
123 | {
124 | itemType: 'separator',
125 | },
126 | {
127 | itemType: 'action',
128 | propertyName: 'toggleSettings',
129 | tooltip: settingsOpened ? 'Close Settings' : 'Open Settings',
130 | icon: ``,
131 | }, {
132 | itemType: 'action',
133 | propertyName: 'recalculate',
134 | tooltip: 'Recalculate',
135 | icon: ``,
136 | },
137 | ],
138 | ({propertyName, propertyValue}) => {
139 | if (propertyName === 'size') {
140 | setOptionSize(propertyValue)
141 | }
142 | if (propertyName === 'ratio') {
143 | setOptionRatioLevel(propertyValue)
144 | setPassOrFail({ratioLevel: propertyValue})
145 | }
146 | if (propertyName === 'text') {
147 | setOptionFontSize(propertyValue)
148 | setPassOrFail({fontSize: propertyValue})
149 | }
150 | if (propertyName === 'toggleSettings') {
151 | setSettingsOpened(!settingsOpened)
152 | }
153 | if(propertyName === 'recalculate') {
154 | setPassOrFail()
155 | setSettingsOpened(false)
156 | }
157 | })
158 | //#endregion
159 |
160 | //#region Widget - currentColor
161 | function updateForegroundColor({ characters }: TextEditEvent) {
162 | let fgColor = characters
163 |
164 | if(!fgColor.includes('#'))
165 | fgColor = "#" + fgColor
166 |
167 | setPassOrFail({
168 | foregroundColor: fgColor
169 | })
170 | closeSettings()
171 | }
172 | //#endregion
173 | //#region Visual - Settings Opened
174 | function showSettings() { setSettingsOpened(true) }
175 | function closeSettings() { setSettingsOpened(false) }
176 | //#endregion
177 | //#region Visual - Size styles
178 | let widgetFontSize: number = 22
179 | let widgetPadding: WidgetJSX.Padding = { horizontal: 16, vertical: 8 }
180 | let widgetSpacing: number = 12
181 | let widgetWidth: WidgetJSX.Size = 200
182 | let widgetHeight: WidgetJSX.Size = 48
183 | let borderColor = "#e4e4e4"
184 | if(optionSize === 'tiny') {
185 | widgetFontSize = 14
186 | widgetSpacing = 8
187 | widgetPadding = 0
188 | widgetHeight = 34
189 | widgetWidth = 80
190 | }
191 | if(optionSize == 'small') {
192 | widgetFontSize = 18
193 | widgetSpacing = 8
194 | widgetHeight = 40
195 | widgetWidth = 100
196 | widgetPadding = { horizontal: 8, vertical: 2 }
197 | }
198 | const widgetBg = "#ffffff"
199 | const widgetFg = "#000000"
200 | //#endregion
201 |
202 | return (
203 |
213 |
221 |
230 | {settingsOpened && (
231 |
248 | )}
249 | {!settingsOpened && (
250 | <>
251 | {passes === 'true' && (
252 |
258 | Pass
259 | `} />
260 |
261 | )}
262 | {passes !== 'true' && (
263 |
269 | Fail
270 | `} />
272 |
273 | )}
274 | {optionSize === 'large' && (
275 | ({ prettyRatio })
276 | )}
277 | >
278 | )}
279 |
280 |
281 | )
282 | }
283 |
284 | widget.register(Widget)
285 |
--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------
1 | export const ratiosToCompare = {
2 | 'AA': {
3 | large: 1/3,
4 | normal: 1/4.5,
5 | },
6 | 'AAA': {
7 | large: 1/4.5,
8 | normal: 1/7,
9 | },
10 | }
--------------------------------------------------------------------------------
/src/lib/color.ts:
--------------------------------------------------------------------------------
1 | import { ratiosToCompare } from '../config';
2 |
3 | export function normalisedRGB(rgb: RGB): RGB {
4 | return {
5 | r: Math.round(rgb.r * 255),
6 | g: Math.round(rgb.g * 255),
7 | b: Math.round(rgb.b * 255),
8 | }
9 | }
10 |
11 | export function luminance(rgb: RGB) {
12 | const [r, g, b] = [rgb.r, rgb.g, rgb.b].map((v) => {
13 | v /= 255;
14 | return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
15 | });
16 | return r * 0.2126 + g * 0.7152 + b * 0.0722;
17 | };
18 |
19 | export function contrast(colorA: RGB, colorB: RGB) {
20 | const foregroundLumiance = luminance(colorA);
21 | const backgroundLuminance = luminance(colorB);
22 | return backgroundLuminance < foregroundLumiance
23 | ? ((backgroundLuminance + 0.05) / (foregroundLumiance + 0.05))
24 | : ((foregroundLumiance + 0.05) / (backgroundLuminance + 0.05));
25 | };
26 |
27 | export function HexToRGB(hex: string): RGB {
28 | hex = hex.slice(1);
29 | const value = parseInt(hex, 16);
30 | const r = (value >> 16) & 255;
31 | const g = (value >> 8) & 255;
32 | const b = value & 255;
33 | return { r, g, b };
34 | };
35 |
36 | export function RGBToHex(rgb: RGB): string {
37 | let normalised = normalisedRGB(rgb)
38 | let r = normalised.r.toString(16);
39 | let g = normalised.g.toString(16);
40 | let b = normalised.b.toString(16);
41 |
42 | if (r.length == 1)
43 | r = "0" + r;
44 | if (g.length == 1)
45 | g = "0" + g;
46 | if (b.length == 1)
47 | b = "0" + b;
48 |
49 | return "#" + r + g + b;
50 | }
51 |
52 | export function getCurrentRatio(fgColor: string, bgColor:string):number {
53 | const ratioContrast = contrast(
54 | HexToRGB(fgColor),
55 | HexToRGB(bgColor)
56 | )
57 | return ratioContrast
58 | }
59 |
60 | export function doesPass(ratio: number, ratioLevel: string, fontSize:string):boolean {
61 | const passRatio:number = ratiosToCompare[ratioLevel][fontSize]
62 | return ratio < passRatio
63 | }
64 |
65 | export function toPrettyRatio(ratio:number): string {
66 | const factor = 1 / ratio
67 | let factorFixed = factor.toFixed(1)
68 |
69 | if(factorFixed.includes(".0"))
70 | factorFixed = factorFixed.replace(".0", "")
71 |
72 | return `${factorFixed}:1`
73 | }
--------------------------------------------------------------------------------
/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react",
4 | "jsxFactory": "figma.widget.h",
5 | "jsxFragmentFactory": "figma.widget.Fragment",
6 | "target": "es6",
7 | "lib": ["es6"],
8 | "typeRoots": ["../node_modules/@types", "../node_modules/@figma"]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------