From 2a262ff18c6fbe48697308f8ea715031be821050 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 6 Dec 2024 14:22:04 -0600 Subject: [PATCH] Add contrast helper --- package.json | 4 ++++ src/alf/util/contrast.ts | 35 +++++++++++++++++++++++++++++++++++ yarn.lock | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 src/alf/util/contrast.ts diff --git a/package.json b/package.json index 6c35d169ed..2032f5fd8b 100644 --- a/package.json +++ b/package.json @@ -148,6 +148,7 @@ "fast-text-encoding": "^1.0.6", "history": "^5.3.0", "hls.js": "^1.5.11", + "hsl-to-rgb-for-reals": "^1.1.1", "js-sha256": "^0.9.0", "jwt-decode": "^4.0.0", "lande": "^1.0.10", @@ -201,6 +202,7 @@ "tippy.js": "^6.3.7", "tlds": "^1.234.0", "tldts": "^6.1.46", + "wcag-contrast": "^3.0.0", "zeego": "^1.6.2", "zod": "^3.20.2" }, @@ -219,6 +221,7 @@ "@react-native/typescript-config": "^0.74.1", "@testing-library/jest-native": "^5.4.1", "@testing-library/react-native": "^11.5.2", + "@types/hsl-to-rgb-for-reals": "^1.1.3", "@types/jest": "^29.4.0", "@types/lodash.chunk": "^4.2.7", "@types/lodash.debounce": "^4.0.7", @@ -228,6 +231,7 @@ "@types/react-dom": "^18.2.18", "@types/react-responsive": "^8.0.5", "@types/react-test-renderer": "^17.0.1", + "@types/wcag-contrast": "^3.0.3", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", "babel-jest": "^29.7.0", diff --git a/src/alf/util/contrast.ts b/src/alf/util/contrast.ts new file mode 100644 index 0000000000..c9254df394 --- /dev/null +++ b/src/alf/util/contrast.ts @@ -0,0 +1,35 @@ +import {ColorValue} from 'react-native' +import hslToRgb from 'hsl-to-rgb-for-reals' +import {rgb} from 'wcag-contrast' + +export function compareAndContrast( + compare: ColorValue | undefined, + contrast: ColorValue | undefined, +) { + let result = contrast + + if (compare && contrast) { + const [, bgHue, bgSat, bgLuminance] = + compare.toString().match(/hsl\((\d+), (\d+)%, ([\d\.]+)%\)/) || [] + const [, textHue, textSat, textLuminance] = + contrast.toString().match(/hsl\((\d+), (\d+)%, ([\d\.]+)%\)/) || [] + const compareRgb = hslToRgb( + parseInt(bgHue), + parseInt(bgSat) / 100, + parseInt(bgLuminance) / 100, + ) + const contrastRgb = hslToRgb( + parseInt(textHue), + parseInt(textSat) / 100, + parseInt(textLuminance) / 100, + ) + const score = rgb(compareRgb, contrastRgb) + if (score < 3) { + result = `hsl(${textHue}, ${textSat}%, ${Math.abs( + parseInt(textLuminance) - 100, + )}%)` + } + } + + return result +} diff --git a/yarn.lock b/yarn.lock index e62dcb97ff..d5bb0d9f17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6944,6 +6944,11 @@ resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.41.tgz#f6ecf57d1b12d2befcce00e928a6a097c22980aa" integrity sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA== +"@types/hsl-to-rgb-for-reals@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@types/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.3.tgz#c42d18a4edb2e0fc24fcb3ae0123d4e64d301962" + integrity sha512-q8/VNwwpVhEJ3aXxZ2z0ce3P2IRZ60yS+rt9s8EQd9PFUPGcqYfK+1l6D1UpLcKrp5C2EBySCHJSGPNgIava4A== + "@types/html-minifier-terser@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" @@ -7204,6 +7209,11 @@ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz#60be8d21baab8c305132eb9cb912ed497852aadc" integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg== +"@types/wcag-contrast@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/wcag-contrast/-/wcag-contrast-3.0.3.tgz#0b0339ab210b859eeee555b3fef58d293216905c" + integrity sha512-oprevfwJSLfpQK4KaWsRKJuNoebV76+xhmbXiWJGy+FkS34LpCgCMNIwRXWTb8xmmSxUE2ycFOYE7uyRVRm3LA== + "@types/ws@^8.5.5": version "8.5.5" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" @@ -10314,6 +10324,11 @@ eslint@^8.19.0: strip-ansi "^6.0.1" text-table "^0.2.0" +esm@^3.0.84: + version "3.2.25" + resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" + integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== + espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" @@ -11752,6 +11767,11 @@ hpack.js@^2.1.6: readable-stream "^2.0.1" wbuf "^1.1.0" +hsl-to-rgb-for-reals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.1.tgz#e1eb23f6b78016e3722431df68197e6dcdc016d9" + integrity sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg== + html-encoding-sniffer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" @@ -16655,6 +16675,13 @@ relateurl@^0.2.7: resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== +relative-luminance@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/relative-luminance/-/relative-luminance-2.0.1.tgz#2babddf3a5a59673227d6f02e0f68e13989e3d13" + integrity sha512-wFuITNthJilFPwkK7gNJcULxXBcfFZvZORsvdvxeOdO44wCeZnuQkf3nFFzOR/dpJNxYsdRZJLsepWbyKhnMww== + dependencies: + esm "^3.0.84" + remove-trailing-slash@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d" @@ -18747,6 +18774,13 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" +wcag-contrast@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/wcag-contrast/-/wcag-contrast-3.0.0.tgz#51c2df43f15162993006454b3362844205889efb" + integrity sha512-RWbpg/S7FOXDCwqC2oFhN/vh8dHzj0OS6dpyOSDHyQFSmqmR+lAUStV/ziTT1GzDqL9wol+nZQB4vCi5yEak+w== + dependencies: + relative-luminance "^2.0.0" + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"