diff --git a/src/components/page/theming/_utils/color.ts b/src/components/page/theming/_utils/color.ts index c825c8e8604..7511fbb630a 100755 --- a/src/components/page/theming/_utils/color.ts +++ b/src/components/page/theming/_utils/color.ts @@ -133,6 +133,28 @@ const rgbToYIQ = ({ r, g, b }: RGB): number => { return (r * 299 + g * 587 + b * 114) / 1000; }; +const RED = 0.2126; +const GREEN = 0.7152; +const BLUE = 0.0722; +const GAMMA = 2.4; + +const luminance = ({ r, g, b }: RGB) => { + const a = [r, g, b].map((v) => { + v /= 255; + return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, GAMMA); + }); + return a[0] * RED + a[1] * GREEN + a[2] * BLUE; +}; + +// Original source: https://stackoverflow.com/a/9733420 +const contrast = (rgb1: RGB, rgb2: RGB) => { + const lum1 = luminance(rgb1); + const lum2 = luminance(rgb2); + const brightest = Math.max(lum1, lum2); + const darkest = Math.min(lum1, lum2); + return (brightest + 0.05) / (darkest + 0.05); +}; + export class Color { readonly hex: string; readonly hsl: HSL; @@ -176,8 +198,11 @@ export class Color { return /(^#[0-9a-fA-F]+)/.test(value.trim()); } - contrast(threshold = 128): Color { - return new Color(this.yiq >= threshold ? '#000' : '#fff'); + contrast(): Color { + const blackContrastRatio = contrast(this.rgb, { r: 0, g: 0, b: 0 }); + const whiteContrastRatio = contrast(this.rgb, { r: 255, g: 255, b: 255 }); + + return new Color(blackContrastRatio >= whiteContrastRatio ? '#000' : '#fff'); } mix(from: string | RGB | HSL | Color, amount = 0.5): Color { diff --git a/static/code/stackblitz/v7/react/package-lock.json b/static/code/stackblitz/v7/react/package-lock.json index bd141b3582f..a0a97af7882 100644 --- a/static/code/stackblitz/v7/react/package-lock.json +++ b/static/code/stackblitz/v7/react/package-lock.json @@ -683,9 +683,9 @@ } }, "node_modules/@ionic/core": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.5.5.tgz", - "integrity": "sha512-7Rn3OUz8QCob7yMuCLs5dVLpChscNCm+OqH8lqTzy3N/8VSKHLNPHIHTIa4DQm9HwjtpOB+XCTeIqbdWHkLgnA==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.5.6.tgz", + "integrity": "sha512-bYQp2twwm61uA0Q31ToVIpQWsiQ9so1dRoWZPD+l+y4fVuFmOCLYeS6XTLTm73jVBq40JfEcsac7eYC4DxoemQ==", "dependencies": { "@stencil/core": "^4.7.2", "ionicons": "^7.2.1", @@ -693,11 +693,11 @@ } }, "node_modules/@ionic/react": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.5.5.tgz", - "integrity": "sha512-OcuTMinb7+HedD7dSQXxMKRct1MNK9Jzszoxi25JI0Iues/mKB0DPJyMe0ICyQdS2ualfin8I2FClHBSjPYBoQ==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.5.6.tgz", + "integrity": "sha512-/2NrJuKGllZzJTDbaBuep/0GFCDO7YhWAvda/FugqS1WLuqNoW2p3LYk0uUqMWCoXVtx972hoyQJzCNf55axkQ==", "dependencies": { - "@ionic/core": "7.5.5", + "@ionic/core": "7.5.6", "ionicons": "^7.0.0", "tslib": "*" }, @@ -707,11 +707,11 @@ } }, "node_modules/@ionic/react-router": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@ionic/react-router/-/react-router-7.4.0.tgz", - "integrity": "sha512-MmqlHtvBMxCHfyovuxaG/0Gb3OpM9tvIFcI+wg7SC35wq1KGRT0uI3On7tcX4xGKFShnGUdr221viGC/x1UVWg==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@ionic/react-router/-/react-router-7.5.6.tgz", + "integrity": "sha512-RiyNxsmO4LxPosuxETNVCHe+Rpxq/O5vErHt9gldaHrk8fGNnJSLH1ZPBPrR4f2WAgJoFGBxkqUUSn3IP3VHCw==", "dependencies": { - "@ionic/react": "7.4.0", + "@ionic/react": "7.5.6", "tslib": "*" }, "peerDependencies": { @@ -721,50 +721,6 @@ "react-router-dom": "^5.0.1" } }, - "node_modules/@ionic/react-router/node_modules/@ionic/core": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.4.0.tgz", - "integrity": "sha512-Kuu04UljgmKz2Umcm77QCP+4O1rV67EEUtK/Kx0eIp+h+eoSkJJK4/p3EpkvrlKRDOfv4xlUnqKw7+yqhBg36w==", - "dependencies": { - "@stencil/core": "^4.2.1", - "ionicons": "7.1.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@ionic/react-router/node_modules/@ionic/react": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.4.0.tgz", - "integrity": "sha512-BUytiAPasBuGkHT/rc43+KpmVDoJB2lsCXWUhmyzWIVtd8dPnbYy7y3QIV85U0bpEyc21VMI8Jin9pL/U0OOog==", - "dependencies": { - "@ionic/core": "7.4.0", - "ionicons": "^7.0.0", - "tslib": "*" - }, - "peerDependencies": { - "react": ">=16.8.6", - "react-dom": ">=16.8.6" - } - }, - "node_modules/@ionic/react-router/node_modules/ionicons": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.1.0.tgz", - "integrity": "sha512-iE4GuEdEHARJpp0sWL7WJZCzNCf5VxpNRhAjW0fLnZPnNL5qZOJUcfup2Z2Ty7Jk8Q5hacrHfGEB1lCwOdXqGg==", - "dependencies": { - "@stencil/core": "^2.18.0" - } - }, - "node_modules/@ionic/react-router/node_modules/ionicons/node_modules/@stencil/core": { - "version": "2.22.3", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.22.3.tgz", - "integrity": "sha512-kmVA0M/HojwsfkeHsifvHVIYe4l5tin7J5+DLgtl8h6WWfiMClND5K3ifCXXI2ETDNKiEk21p6jql3Fx9o2rng==", - "bin": { - "stencil": "bin/stencil" - }, - "engines": { - "node": ">=12.10.0", - "npm": ">=6.0.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -1962,9 +1918,9 @@ "optional": true }, "@ionic/core": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.5.5.tgz", - "integrity": "sha512-7Rn3OUz8QCob7yMuCLs5dVLpChscNCm+OqH8lqTzy3N/8VSKHLNPHIHTIa4DQm9HwjtpOB+XCTeIqbdWHkLgnA==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.5.6.tgz", + "integrity": "sha512-bYQp2twwm61uA0Q31ToVIpQWsiQ9so1dRoWZPD+l+y4fVuFmOCLYeS6XTLTm73jVBq40JfEcsac7eYC4DxoemQ==", "requires": { "@stencil/core": "^4.7.2", "ionicons": "^7.2.1", @@ -1972,59 +1928,22 @@ } }, "@ionic/react": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.5.5.tgz", - "integrity": "sha512-OcuTMinb7+HedD7dSQXxMKRct1MNK9Jzszoxi25JI0Iues/mKB0DPJyMe0ICyQdS2ualfin8I2FClHBSjPYBoQ==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.5.6.tgz", + "integrity": "sha512-/2NrJuKGllZzJTDbaBuep/0GFCDO7YhWAvda/FugqS1WLuqNoW2p3LYk0uUqMWCoXVtx972hoyQJzCNf55axkQ==", "requires": { - "@ionic/core": "7.5.5", + "@ionic/core": "7.5.6", "ionicons": "^7.0.0", "tslib": "*" } }, "@ionic/react-router": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@ionic/react-router/-/react-router-7.4.0.tgz", - "integrity": "sha512-MmqlHtvBMxCHfyovuxaG/0Gb3OpM9tvIFcI+wg7SC35wq1KGRT0uI3On7tcX4xGKFShnGUdr221viGC/x1UVWg==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@ionic/react-router/-/react-router-7.5.6.tgz", + "integrity": "sha512-RiyNxsmO4LxPosuxETNVCHe+Rpxq/O5vErHt9gldaHrk8fGNnJSLH1ZPBPrR4f2WAgJoFGBxkqUUSn3IP3VHCw==", "requires": { - "@ionic/react": "7.4.0", + "@ionic/react": "7.5.6", "tslib": "*" - }, - "dependencies": { - "@ionic/core": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.4.0.tgz", - "integrity": "sha512-Kuu04UljgmKz2Umcm77QCP+4O1rV67EEUtK/Kx0eIp+h+eoSkJJK4/p3EpkvrlKRDOfv4xlUnqKw7+yqhBg36w==", - "requires": { - "@stencil/core": "^4.2.1", - "ionicons": "7.1.0", - "tslib": "^2.1.0" - } - }, - "@ionic/react": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.4.0.tgz", - "integrity": "sha512-BUytiAPasBuGkHT/rc43+KpmVDoJB2lsCXWUhmyzWIVtd8dPnbYy7y3QIV85U0bpEyc21VMI8Jin9pL/U0OOog==", - "requires": { - "@ionic/core": "7.4.0", - "ionicons": "^7.0.0", - "tslib": "*" - } - }, - "ionicons": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.1.0.tgz", - "integrity": "sha512-iE4GuEdEHARJpp0sWL7WJZCzNCf5VxpNRhAjW0fLnZPnNL5qZOJUcfup2Z2Ty7Jk8Q5hacrHfGEB1lCwOdXqGg==", - "requires": { - "@stencil/core": "^2.18.0" - }, - "dependencies": { - "@stencil/core": { - "version": "2.22.3", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.22.3.tgz", - "integrity": "sha512-kmVA0M/HojwsfkeHsifvHVIYe4l5tin7J5+DLgtl8h6WWfiMClND5K3ifCXXI2ETDNKiEk21p6jql3Fx9o2rng==" - } - } - } } }, "@jridgewell/gen-mapping": { diff --git a/static/code/stackblitz/v7/vue/package-lock.json b/static/code/stackblitz/v7/vue/package-lock.json index 8ca02bc300b..b4a1d202182 100644 --- a/static/code/stackblitz/v7/vue/package-lock.json +++ b/static/code/stackblitz/v7/vue/package-lock.json @@ -384,9 +384,9 @@ } }, "node_modules/@ionic/core": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.5.5.tgz", - "integrity": "sha512-7Rn3OUz8QCob7yMuCLs5dVLpChscNCm+OqH8lqTzy3N/8VSKHLNPHIHTIa4DQm9HwjtpOB+XCTeIqbdWHkLgnA==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.5.6.tgz", + "integrity": "sha512-bYQp2twwm61uA0Q31ToVIpQWsiQ9so1dRoWZPD+l+y4fVuFmOCLYeS6XTLTm73jVBq40JfEcsac7eYC4DxoemQ==", "dependencies": { "@stencil/core": "^4.7.2", "ionicons": "^7.2.1", @@ -394,20 +394,20 @@ } }, "node_modules/@ionic/vue": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-7.5.5.tgz", - "integrity": "sha512-e+7WywBYQeiF8H2U99FBQwrqRVk3eFkEZR1Oo3unVek+OlmLWrgunzfbzlWK9epAMHgih5ptrAMKVLlbJFkFEw==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-7.5.6.tgz", + "integrity": "sha512-AtHx+mKFp2zN21PMLgkbZwPKvSF+HZtilqDyLBW+LdLVwphjWoGa+dZHiDmhyAYkO8u10rU8cPnlzMd77ArDXA==", "dependencies": { - "@ionic/core": "7.5.5", + "@ionic/core": "7.5.6", "ionicons": "^7.0.0" } }, "node_modules/@ionic/vue-router": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@ionic/vue-router/-/vue-router-7.5.5.tgz", - "integrity": "sha512-2YJmyX5kG9BABVc/fw8n1auAgCUXbnta117DD3MWhZl/dWEWW0Xj3mb3ricAwynHbok0naflXiH9OlvPwJs/VA==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@ionic/vue-router/-/vue-router-7.5.6.tgz", + "integrity": "sha512-Q1eiTUSbFSLDyqsluyFS4Xln5zhtQrlobp3UPyLZX+R5Tj2+rdJ37UiPp3uWSTPfJrrhFl1D9G6xg/2JwBAu2Q==", "dependencies": { - "@ionic/vue": "7.5.5" + "@ionic/vue": "7.5.6" } }, "node_modules/@jridgewell/sourcemap-codec": { @@ -1144,9 +1144,9 @@ "optional": true }, "@ionic/core": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.5.5.tgz", - "integrity": "sha512-7Rn3OUz8QCob7yMuCLs5dVLpChscNCm+OqH8lqTzy3N/8VSKHLNPHIHTIa4DQm9HwjtpOB+XCTeIqbdWHkLgnA==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.5.6.tgz", + "integrity": "sha512-bYQp2twwm61uA0Q31ToVIpQWsiQ9so1dRoWZPD+l+y4fVuFmOCLYeS6XTLTm73jVBq40JfEcsac7eYC4DxoemQ==", "requires": { "@stencil/core": "^4.7.2", "ionicons": "^7.2.1", @@ -1154,20 +1154,20 @@ } }, "@ionic/vue": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-7.5.5.tgz", - "integrity": "sha512-e+7WywBYQeiF8H2U99FBQwrqRVk3eFkEZR1Oo3unVek+OlmLWrgunzfbzlWK9epAMHgih5ptrAMKVLlbJFkFEw==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-7.5.6.tgz", + "integrity": "sha512-AtHx+mKFp2zN21PMLgkbZwPKvSF+HZtilqDyLBW+LdLVwphjWoGa+dZHiDmhyAYkO8u10rU8cPnlzMd77ArDXA==", "requires": { - "@ionic/core": "7.5.5", + "@ionic/core": "7.5.6", "ionicons": "^7.0.0" } }, "@ionic/vue-router": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@ionic/vue-router/-/vue-router-7.5.5.tgz", - "integrity": "sha512-2YJmyX5kG9BABVc/fw8n1auAgCUXbnta117DD3MWhZl/dWEWW0Xj3mb3ricAwynHbok0naflXiH9OlvPwJs/VA==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@ionic/vue-router/-/vue-router-7.5.6.tgz", + "integrity": "sha512-Q1eiTUSbFSLDyqsluyFS4Xln5zhtQrlobp3UPyLZX+R5Tj2+rdJ37UiPp3uWSTPfJrrhFl1D9G6xg/2JwBAu2Q==", "requires": { - "@ionic/vue": "7.5.5" + "@ionic/vue": "7.5.6" } }, "@jridgewell/sourcemap-codec": {