├── .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 | --------------------------------------------------------------------------------