From 1c4787af849aac50eabb540da9ed51bf033afb4c Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Wed, 4 Oct 2023 23:32:55 +0200 Subject: [PATCH 1/5] fix(theming): Ensure that maxcontrast text has always a contrast of 4.5:1 even on hover Signed-off-by: Ferdinand Thiessen --- apps/theming/css/default.css | 8 ++++---- apps/theming/lib/Themes/DefaultTheme.php | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/theming/css/default.css b/apps/theming/css/default.css index 05e21e79b962e..702ab65569b76 100644 --- a/apps/theming/css/default.css +++ b/apps/theming/css/default.css @@ -11,11 +11,11 @@ --color-placeholder-light: #e6e6e6; --color-placeholder-dark: #cccccc; --color-main-text: #222222; - --color-text-maxcontrast: #767676; - --color-text-maxcontrast-default: #767676; - --color-text-maxcontrast-background-blur: #646464; + --color-text-maxcontrast: #707070; + --color-text-maxcontrast-default: #707070; + --color-text-maxcontrast-background-blur: #5e5e5e; --color-text-light: #222222; - --color-text-lighter: #767676; + --color-text-lighter: #6f6f6f; --color-scrollbar: rgba(34,34,34, .15); --color-error: #d91812; --color-error-rgb: 217,24,18; diff --git a/apps/theming/lib/Themes/DefaultTheme.php b/apps/theming/lib/Themes/DefaultTheme.php index 70a7b5fdff83b..820291146bfcb 100644 --- a/apps/theming/lib/Themes/DefaultTheme.php +++ b/apps/theming/lib/Themes/DefaultTheme.php @@ -103,7 +103,8 @@ public function getMediaQuery(): string { public function getCSSVariables(): array { $colorMainText = '#222222'; $colorMainTextRgb = join(',', $this->util->hexToRGB($colorMainText)); - $colorTextMaxcontrast = $this->util->lighten($colorMainText, 33); + // Color that still provides enough contrast for text, so we need a ratio of 4.5:1 on main background AND hover + $colorTextMaxcontrast = '#707070'; // 4.54 : 1 for hover background $colorMainBackground = '#ffffff'; $colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground)); $colorBoxShadow = $this->util->darken($colorMainBackground, 70); @@ -138,7 +139,7 @@ public function getCSSVariables(): array { '--color-text-maxcontrast-default' => $colorTextMaxcontrast, '--color-text-maxcontrast-background-blur' => $this->util->darken($colorTextMaxcontrast, 7), '--color-text-light' => $colorMainText, - '--color-text-lighter' => $this->util->lighten($colorMainText, 33), + '--color-text-lighter' => $this->util->lighten($colorMainText, 30), '--color-scrollbar' => 'rgba(' . $colorMainTextRgb . ', .15)', From ec1ae4505b7ae111fed40f87d614a1b0121cc399 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Thu, 5 Oct 2023 14:55:22 +0200 Subject: [PATCH 2/5] tests: Add accessibility tests for CSS color variables Signed-off-by: Ferdinand Thiessen --- apps/theming/__tests__/accessibility.cy.ts | 116 +++++++++++++++++++++ cypress/support/component.ts | 2 + package-lock.json | 68 +++++++++++- package.json | 4 +- tsconfig.json | 2 +- 5 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 apps/theming/__tests__/accessibility.cy.ts diff --git a/apps/theming/__tests__/accessibility.cy.ts b/apps/theming/__tests__/accessibility.cy.ts new file mode 100644 index 0000000000000..bea8e5febdec0 --- /dev/null +++ b/apps/theming/__tests__/accessibility.cy.ts @@ -0,0 +1,116 @@ +// eslint-disable-next-line import/no-webpack-loader-syntax, import/no-unresolved +import style from '!raw-loader!../css/default.css' + +const testCases = { + 'Generic combinations': { + foregroundColors: [ + 'color-main-text', + // 'color-text-light', deprecated + // 'color-text-lighter', deprecated + 'color-text-maxcontrast', + 'color-text-maxcontrast-default', + 'color-error-text', + 'color-warning-text', + 'color-success-text', + 'color-info-text', + ], + backgroundColors: [ + 'color-background-main', + 'color-background-hover', + 'color-background-dark', + 'color-background-darker', + ], + }, + Primary: { + foregroundColors: [ + 'color-primary-text', + ], + backgroundColors: [ + 'color-primary-default', + 'color-primary', + 'color-primary-hover', + ], + }, + 'Primary light': { + foregroundColors: [ + 'color-primary-light-text', + ], + backgroundColors: [ + 'color-primary-light', + 'color-primary-light-hover', + ], + }, + 'Primary element': { + foregroundColors: [ + 'color-primary-element-text', + 'color-primary-element-text-dark', + ], + backgroundColors: [ + 'color-primary-element', + 'color-primary-element-hover', + ], + }, + 'Primary element light': { + foregroundColors: [ + 'color-primary-element-light-text', + ], + backgroundColors: [ + 'color-primary-element-light', + 'color-primary-element-light-hover', + ], + }, +} + +before(() => { + cy.injectAxe() + + const el = document.createElement('style') + el.innerText = style + document.head.appendChild(el) +}) + +/** + * Create a wrapper element with color and background set + * + * @param foreground The foreground color (css variable without leading --) + * @param background The background color + */ +function createTestCase(foreground: string, background: string) { + const wrapper = document.createElement('div') + wrapper.innerText = `${foreground} ${background}` + wrapper.style.color = `var(--${foreground})` + wrapper.style.backgroundColor = `var(--${background})` + wrapper.style.padding = '4px' + wrapper.setAttribute('data-cy-testcase', '') + return wrapper +} + +for (const [name, { backgroundColors, foregroundColors }] of Object.entries(testCases)) { + describe(`Accessibility of CSS color variables for ${name}`, () => { + afterEach(() => { + cy.document().then(doc => { + const root = doc.querySelector('[data-cy-root]') + if (root === null) { + throw new Error('No test root found') + } + for (const child of root.children) { + root.removeChild(child) + } + }) + }) + + for (const foreground of foregroundColors) { + for (const background of backgroundColors) { + it(`color contrast of ${foreground} on ${background}`, () => { + const element = createTestCase(foreground, background) + cy.document().then(doc => { + const root = doc.querySelector('[data-cy-root]') + console.warn(root) + root?.appendChild(element) + cy.checkA11y('[data-cy-testcase]') + }) + }) + } + } + }) +} diff --git a/cypress/support/component.ts b/cypress/support/component.ts index b1e0a1b2c0f82..da56f12482643 100644 --- a/cypress/support/component.ts +++ b/cypress/support/component.ts @@ -19,6 +19,8 @@ * along with this program. If not, see . * */ +import 'cypress-axe' + /* eslint-disable */ import { mount } from '@cypress/vue2' diff --git a/package-lock.json b/package-lock.json index 62286b993e37a..e4748226ba7ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,6 @@ "camelcase": "^8.0.0", "cancelable-promise": "^4.3.1", "clipboard": "^2.0.11", - "colord": "^2.9.3", "core-js": "^3.33.0", "davclient.js": "github:owncloud/davclient.js.git#0.2.1", "debounce": "^1.2.1", @@ -113,8 +112,10 @@ "babel-jest": "^29.6.4", "babel-loader": "^9.1.0", "babel-loader-exclude-node-modules-except": "^1.2.1", + "colord": "^2.9.3", "css-loader": "^6.8.1", "cypress": "^13.3.0", + "cypress-axe": "^1.5.0", "cypress-if": "^1.10.5", "cypress-split": "^1.15.3", "cypress-wait-until": "^2.0.1", @@ -141,6 +142,7 @@ "karma-viewport": "^1.0.9", "node-polyfill-webpack-plugin": "^2.0.1", "puppeteer": "^21.0.3", + "raw-loader": "^4.0.2", "regextras": "^0.8.0", "sass": "^1.66.1", "sass-loader": "^13.2.2", @@ -7729,6 +7731,16 @@ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, + "node_modules/axe-core": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.8.2.tgz", + "integrity": "sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, "node_modules/axios": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", @@ -9222,7 +9234,8 @@ "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true }, "node_modules/colorette": { "version": "2.0.20", @@ -10004,6 +10017,19 @@ "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, + "node_modules/cypress-axe": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/cypress-axe/-/cypress-axe-1.5.0.tgz", + "integrity": "sha512-Hy/owCjfj+25KMsecvDgo4fC/781ccL+e8p+UUYoadGVM2ogZF9XIKbiM6KI8Y3cEaSreymdD6ZzccbI2bY0lQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "axe-core": "^3 || ^4", + "cypress": "^10 || ^11 || ^12 || ^13" + } + }, "node_modules/cypress-if": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/cypress-if/-/cypress-if-1.10.5.tgz", @@ -22145,6 +22171,44 @@ "node": ">= 0.8" } }, + "node_modules/raw-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/raw-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/package.json b/package.json index c91cac50deedd..8a3024cccd858 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,6 @@ "camelcase": "^8.0.0", "cancelable-promise": "^4.3.1", "clipboard": "^2.0.11", - "colord": "^2.9.3", "core-js": "^3.33.0", "davclient.js": "github:owncloud/davclient.js.git#0.2.1", "debounce": "^1.2.1", @@ -140,8 +139,10 @@ "babel-jest": "^29.6.4", "babel-loader": "^9.1.0", "babel-loader-exclude-node-modules-except": "^1.2.1", + "colord": "^2.9.3", "css-loader": "^6.8.1", "cypress": "^13.3.0", + "cypress-axe": "^1.5.0", "cypress-if": "^1.10.5", "cypress-split": "^1.15.3", "cypress-wait-until": "^2.0.1", @@ -168,6 +169,7 @@ "karma-viewport": "^1.0.9", "node-polyfill-webpack-plugin": "^2.0.1", "puppeteer": "^21.0.3", + "raw-loader": "^4.0.2", "regextras": "^0.8.0", "sass": "^1.66.1", "sass-loader": "^13.2.2", diff --git a/tsconfig.json b/tsconfig.json index 47b75a076d850..ea4818103c681 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "extends": "@vue/tsconfig/tsconfig.json", "include": ["./apps/**/*.ts", "./core/**/*.ts", "./*.d.ts"], "compilerOptions": { - "types": ["cypress", "jest", "node", "vue"], + "types": ["cypress", "cypress-axe", "jest", "node", "vue"], "outDir": "./dist/", "target": "ESNext", "module": "esnext", From be129a7d1f256a9a6aeaadbd9355c088fe0df929 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Thu, 5 Oct 2023 16:49:22 +0200 Subject: [PATCH 3/5] fix(theming): Make it clearer that color-text-light and -lighter are deprecated Signed-off-by: Ferdinand Thiessen --- apps/theming/css/default.css | 6 ++++-- apps/theming/lib/Themes/DarkTheme.php | 4 ++-- apps/theming/lib/Themes/DefaultTheme.php | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/theming/css/default.css b/apps/theming/css/default.css index 702ab65569b76..571ae8a7829c7 100644 --- a/apps/theming/css/default.css +++ b/apps/theming/css/default.css @@ -14,8 +14,10 @@ --color-text-maxcontrast: #707070; --color-text-maxcontrast-default: #707070; --color-text-maxcontrast-background-blur: #5e5e5e; - --color-text-light: #222222; - --color-text-lighter: #6f6f6f; + /** @deprecated use ` --color-main-text` instead */ + --color-text-light: var(--color-main-text); + /** @deprecated use `--color-text-maxcontrast` instead */ + --color-text-lighter: var(--color-text-maxcontrast); --color-scrollbar: rgba(34,34,34, .15); --color-error: #d91812; --color-error-rgb: 217,24,18; diff --git a/apps/theming/lib/Themes/DarkTheme.php b/apps/theming/lib/Themes/DarkTheme.php index 80c65cd0eb662..1def7f378a73d 100644 --- a/apps/theming/lib/Themes/DarkTheme.php +++ b/apps/theming/lib/Themes/DarkTheme.php @@ -84,8 +84,8 @@ public function getCSSVariables(): array { '--color-text-maxcontrast' => $colorTextMaxcontrast, '--color-text-maxcontrast-default' => $colorTextMaxcontrast, '--color-text-maxcontrast-background-blur' => $this->util->lighten($colorTextMaxcontrast, 2), - '--color-text-light' => $this->util->darken($colorMainText, 10), - '--color-text-lighter' => $this->util->darken($colorMainText, 20), + '--color-text-light' => 'var(--color-main-text)', // deprecated + '--color-text-lighter' => 'var(--color-text-maxcontrast)', // deprecated '--color-error' => $colorError, '--color-error-rgb' => join(',', $this->util->hexToRGB($colorError)), diff --git a/apps/theming/lib/Themes/DefaultTheme.php b/apps/theming/lib/Themes/DefaultTheme.php index 820291146bfcb..0c2594ebe7f58 100644 --- a/apps/theming/lib/Themes/DefaultTheme.php +++ b/apps/theming/lib/Themes/DefaultTheme.php @@ -138,8 +138,8 @@ public function getCSSVariables(): array { '--color-text-maxcontrast' => $colorTextMaxcontrast, '--color-text-maxcontrast-default' => $colorTextMaxcontrast, '--color-text-maxcontrast-background-blur' => $this->util->darken($colorTextMaxcontrast, 7), - '--color-text-light' => $colorMainText, - '--color-text-lighter' => $this->util->lighten($colorMainText, 30), + '--color-text-light' => 'var(--color-main-text)', // deprecated + '--color-text-lighter' => 'var(--color-text-maxcontrast)', // deprecated '--color-scrollbar' => 'rgba(' . $colorMainTextRgb . ', .15)', From 3676295f7b13852ec328e32f510aefd4c7b709c7 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Wed, 25 Oct 2023 13:57:28 +0200 Subject: [PATCH 4/5] fix(theming): Ensure all text colors have enough contrast for accessibility Signed-off-by: Ferdinand Thiessen --- apps/theming/__tests__/accessibility.cy.ts | 94 +++++++++++-------- apps/theming/css/default.css | 32 ++++--- .../theming/lib/Service/BackgroundService.php | 2 +- apps/theming/lib/Themes/CommonThemeTrait.php | 6 +- apps/theming/lib/Themes/DefaultTheme.php | 4 +- .../theming/tests/Themes/DefaultThemeTest.php | 2 + cypress/e2e/theming/themingUtils.ts | 2 +- 7 files changed, 79 insertions(+), 63 deletions(-) diff --git a/apps/theming/__tests__/accessibility.cy.ts b/apps/theming/__tests__/accessibility.cy.ts index bea8e5febdec0..3bbf8a64972e8 100644 --- a/apps/theming/__tests__/accessibility.cy.ts +++ b/apps/theming/__tests__/accessibility.cy.ts @@ -2,23 +2,19 @@ import style from '!raw-loader!../css/default.css' const testCases = { - 'Generic combinations': { + 'Main text': { foregroundColors: [ 'color-main-text', // 'color-text-light', deprecated // 'color-text-lighter', deprecated 'color-text-maxcontrast', 'color-text-maxcontrast-default', - 'color-error-text', - 'color-warning-text', - 'color-success-text', - 'color-info-text', ], backgroundColors: [ 'color-background-main', 'color-background-hover', 'color-background-dark', - 'color-background-darker', + // 'color-background-darker', this should only be used for elements not for text ], }, Primary: { @@ -26,9 +22,9 @@ const testCases = { 'color-primary-text', ], backgroundColors: [ - 'color-primary-default', + // 'color-primary-default', this should only be used for elements not for text! + // 'color-primary-hover', this should only be used for elements and not for text! 'color-primary', - 'color-primary-hover', ], }, 'Primary light': { @@ -59,16 +55,20 @@ const testCases = { 'color-primary-element-light-hover', ], }, + 'Servity information texts': { + foregroundColors: [ + 'color-error-text', + 'color-warning-text', + 'color-success-text', + 'color-info-text', + ], + backgroundColors: [ + 'color-background-main', + 'color-background-hover', + ], + }, } -before(() => { - cy.injectAxe() - - const el = document.createElement('style') - el.innerText = style - document.head.appendChild(el) -}) - /** * Create a wrapper element with color and background set * @@ -85,32 +85,44 @@ function createTestCase(foreground: string, background: string) { return wrapper } -for (const [name, { backgroundColors, foregroundColors }] of Object.entries(testCases)) { - describe(`Accessibility of CSS color variables for ${name}`, () => { - afterEach(() => { - cy.document().then(doc => { - const root = doc.querySelector('[data-cy-root]') - if (root === null) { - throw new Error('No test root found') - } - for (const child of root.children) { - root.removeChild(child) - } - }) +describe('Accessibility of Nextcloud theming', () => { + before(() => { + cy.injectAxe() + + const el = document.createElement('style') + el.innerText = style + document.head.appendChild(el) + }) + + beforeEach(() => { + cy.document().then(doc => { + const root = doc.querySelector('[data-cy-root]') + if (root === null) { + throw new Error('No test root found') + } + for (const child of root.children) { + root.removeChild(child) + } }) + }) - for (const foreground of foregroundColors) { - for (const background of backgroundColors) { - it(`color contrast of ${foreground} on ${background}`, () => { - const element = createTestCase(foreground, background) - cy.document().then(doc => { - const root = doc.querySelector('[data-cy-root]') - console.warn(root) - root?.appendChild(element) - cy.checkA11y('[data-cy-testcase]') + for (const [name, { backgroundColors, foregroundColors }] of Object.entries(testCases)) { + context(`Accessibility of CSS color variables for ${name}`, () => { + for (const foreground of foregroundColors) { + for (const background of backgroundColors) { + it(`color contrast of ${foreground} on ${background}`, () => { + const element = createTestCase(foreground, background) + cy.document().then(doc => { + const root = doc.querySelector('[data-cy-root]') + // eslint-disable-next-line no-unused-expressions + expect(root).not.to.be.undefined + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + root!.appendChild(element) + cy.checkA11y('[data-cy-testcase]') + }) }) - }) + } } - } - }) -} + }) + } +}) diff --git a/apps/theming/css/default.css b/apps/theming/css/default.css index 571ae8a7829c7..8cc3eb2de3212 100644 --- a/apps/theming/css/default.css +++ b/apps/theming/css/default.css @@ -6,14 +6,16 @@ --filter-background-blur: blur(25px); --gradient-main-background: var(--color-main-background) 0%, var(--color-main-background-translucent) 85%, transparent 100%; --color-background-hover: #f5f5f5; + /** Can be used e.g. to colorize selected table rows */ --color-background-dark: #ededed; + /** This should only be used for elements, not as a text background! Otherwise it will not work for accessibility. */ --color-background-darker: #dbdbdb; --color-placeholder-light: #e6e6e6; --color-placeholder-dark: #cccccc; --color-main-text: #222222; - --color-text-maxcontrast: #707070; - --color-text-maxcontrast-default: #707070; - --color-text-maxcontrast-background-blur: #5e5e5e; + --color-text-maxcontrast: #6b6b6b; + --color-text-maxcontrast-default: #6b6b6b; + --color-text-maxcontrast-background-blur: #595959; /** @deprecated use ` --color-main-text` instead */ --color-text-light: var(--color-main-text); /** @deprecated use `--color-text-maxcontrast` instead */ @@ -26,7 +28,7 @@ --color-warning: #c28900; --color-warning-rgb: 194,137,0; --color-warning-hover: #cea032; - --color-warning-text: #996c00; + --color-warning-text: #8f6500; --color-success: #2d7b41; --color-success-rgb: 45,123,65; --color-success-hover: #448955; @@ -66,20 +68,20 @@ --background-invert-if-bright: invert(100%); --background-image-invert-if-bright: no; --primary-invert-if-bright: no; - --color-primary: #006aa3; + --color-primary: #006889; --color-primary-default: #0082c9; --color-primary-text: #ffffff; - --color-primary-hover: #3287b5; - --color-primary-light: #e5f0f5; - --color-primary-light-text: #002a41; - --color-primary-light-hover: #dbe5ea; - --color-primary-element: #006aa3; - --color-primary-element-hover: #1f7cae; + --color-primary-hover: #3286a0; + --color-primary-light: #e5eff3; + --color-primary-light-text: #002936; + --color-primary-light-hover: #dbe4e8; + --color-primary-element: #006889; + --color-primary-element-hover: #187694; --color-primary-element-text: #ffffff; - --color-primary-element-light: #e5f0f5; - --color-primary-element-light-hover: #dbe5ea; - --color-primary-element-light-text: #002a41; - --color-primary-element-text-dark: #ededed; + --color-primary-element-text-dark: #f0f0f0; + --color-primary-element-light: #e5eff3; + --color-primary-element-light-hover: #dbe4e8; + --color-primary-element-light-text: #002936; --gradient-primary-background: linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%); --image-background-default: url('/apps/theming/img/background/kamil-porembinski-clouds.jpg'); --color-background-plain: #0082c9; diff --git a/apps/theming/lib/Service/BackgroundService.php b/apps/theming/lib/Service/BackgroundService.php index d5bc4296b5bdc..3886bf855c4f9 100644 --- a/apps/theming/lib/Service/BackgroundService.php +++ b/apps/theming/lib/Service/BackgroundService.php @@ -46,7 +46,7 @@ class BackgroundService { // true when the background is bright and need dark icons public const THEMING_MODE_DARK = 'dark'; public const DEFAULT_COLOR = '#0082c9'; - public const DEFAULT_ACCESSIBLE_COLOR = '#006aa3'; + public const DEFAULT_ACCESSIBLE_COLOR = '#006889'; public const BACKGROUND_SHIPPED = 'shipped'; public const BACKGROUND_CUSTOM = 'custom'; diff --git a/apps/theming/lib/Themes/CommonThemeTrait.php b/apps/theming/lib/Themes/CommonThemeTrait.php index 9b516bf6c70da..3460c7e68ebbe 100644 --- a/apps/theming/lib/Themes/CommonThemeTrait.php +++ b/apps/theming/lib/Themes/CommonThemeTrait.php @@ -64,15 +64,15 @@ protected function generatePrimaryVariables(string $colorMainBackground, string // used for buttons, inputs... '--color-primary-element' => $colorPrimaryElement, - '--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 75), + '--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 81), '--color-primary-element-text' => $this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', + // mostly used for disabled states + '--color-primary-element-text-dark' => $this->util->darken($this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', 6), // used for hover/focus states '--color-primary-element-light' => $colorPrimaryElementLight, '--color-primary-element-light-hover' => $this->util->mix($colorPrimaryElementLight, $colorMainText, 90), '--color-primary-element-light-text' => $this->util->mix($colorPrimaryElement, $this->util->invertTextColor($colorPrimaryElementLight) ? '#000000' : '#ffffff', -20), - // mostly used for disabled states - '--color-primary-element-text-dark' => $this->util->darken($this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', 7), // to use like this: background-image: var(--gradient-primary-background); '--gradient-primary-background' => 'linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%)', diff --git a/apps/theming/lib/Themes/DefaultTheme.php b/apps/theming/lib/Themes/DefaultTheme.php index 0c2594ebe7f58..e2bd31548ca6e 100644 --- a/apps/theming/lib/Themes/DefaultTheme.php +++ b/apps/theming/lib/Themes/DefaultTheme.php @@ -104,7 +104,7 @@ public function getCSSVariables(): array { $colorMainText = '#222222'; $colorMainTextRgb = join(',', $this->util->hexToRGB($colorMainText)); // Color that still provides enough contrast for text, so we need a ratio of 4.5:1 on main background AND hover - $colorTextMaxcontrast = '#707070'; // 4.54 : 1 for hover background + $colorTextMaxcontrast = '#6b6b6b'; // 4.5 : 1 for hover background and background dark $colorMainBackground = '#ffffff'; $colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground)); $colorBoxShadow = $this->util->darken($colorMainBackground, 70); @@ -151,7 +151,7 @@ public function getCSSVariables(): array { '--color-warning' => $colorWarning, '--color-warning-rgb' => join(',', $this->util->hexToRGB($colorWarning)), '--color-warning-hover' => $this->util->mix($colorWarning, $colorMainBackground, 60), - '--color-warning-text' => $this->util->darken($colorWarning, 8), + '--color-warning-text' => $this->util->darken($colorWarning, 10), '--color-success' => $colorSuccess, '--color-success-rgb' => join(',', $this->util->hexToRGB($colorSuccess)), '--color-success-hover' => $this->util->mix($colorSuccess, $colorMainBackground, 78), diff --git a/apps/theming/tests/Themes/DefaultThemeTest.php b/apps/theming/tests/Themes/DefaultThemeTest.php index 6044f5c10d3f8..0d86a8d6b2825 100644 --- a/apps/theming/tests/Themes/DefaultThemeTest.php +++ b/apps/theming/tests/Themes/DefaultThemeTest.php @@ -157,6 +157,8 @@ public function testThemindDisabledFallbackCss() { $css = ":root {" . PHP_EOL . "$variables}" . PHP_EOL; $fallbackCss = file_get_contents(__DIR__ . '/../../css/default.css'); + // Remove comments + $fallbackCss = preg_replace('/\s*\/\*[\s\S]*?\*\//m', '', $fallbackCss); $this->assertEquals($css, $fallbackCss); } diff --git a/cypress/e2e/theming/themingUtils.ts b/cypress/e2e/theming/themingUtils.ts index 2cf92c47e6aa6..088c58dda45b3 100644 --- a/cypress/e2e/theming/themingUtils.ts +++ b/cypress/e2e/theming/themingUtils.ts @@ -22,7 +22,7 @@ import { colord } from 'colord' export const defaultPrimary = '#0082c9' -export const defaultAccessiblePrimary = '#006aa3' +export const defaultAccessiblePrimary = '#006889' export const defaultBackground = 'kamil-porembinski-clouds.jpg' /** From 3378a73e99171ac31860ccca81b1b3197e63417f Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Wed, 25 Oct 2023 16:29:26 +0200 Subject: [PATCH 5/5] fix(theming): Make changes on primary color less invasiv Signed-off-by: Ferdinand Thiessen --- apps/theming/css/default.css | 20 +++++++++---------- .../theming/lib/Service/BackgroundService.php | 2 +- apps/theming/lib/Themes/CommonThemeTrait.php | 2 +- cypress/e2e/theming/themingUtils.ts | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/theming/css/default.css b/apps/theming/css/default.css index 8cc3eb2de3212..3e36e03f8941d 100644 --- a/apps/theming/css/default.css +++ b/apps/theming/css/default.css @@ -68,20 +68,20 @@ --background-invert-if-bright: invert(100%); --background-image-invert-if-bright: no; --primary-invert-if-bright: no; - --color-primary: #006889; + --color-primary: #00679e; --color-primary-default: #0082c9; --color-primary-text: #ffffff; - --color-primary-hover: #3286a0; - --color-primary-light: #e5eff3; - --color-primary-light-text: #002936; - --color-primary-light-hover: #dbe4e8; - --color-primary-element: #006889; - --color-primary-element-hover: #187694; + --color-primary-hover: #3285b1; + --color-primary-light: #e5eff5; + --color-primary-light-text: #00293f; + --color-primary-light-hover: #dbe4ea; + --color-primary-element: #00679e; + --color-primary-element-hover: #1674a6; --color-primary-element-text: #ffffff; --color-primary-element-text-dark: #f0f0f0; - --color-primary-element-light: #e5eff3; - --color-primary-element-light-hover: #dbe4e8; - --color-primary-element-light-text: #002936; + --color-primary-element-light: #e5eff5; + --color-primary-element-light-hover: #dbe4ea; + --color-primary-element-light-text: #00293f; --gradient-primary-background: linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%); --image-background-default: url('/apps/theming/img/background/kamil-porembinski-clouds.jpg'); --color-background-plain: #0082c9; diff --git a/apps/theming/lib/Service/BackgroundService.php b/apps/theming/lib/Service/BackgroundService.php index 3886bf855c4f9..c385bc7b072cf 100644 --- a/apps/theming/lib/Service/BackgroundService.php +++ b/apps/theming/lib/Service/BackgroundService.php @@ -46,7 +46,7 @@ class BackgroundService { // true when the background is bright and need dark icons public const THEMING_MODE_DARK = 'dark'; public const DEFAULT_COLOR = '#0082c9'; - public const DEFAULT_ACCESSIBLE_COLOR = '#006889'; + public const DEFAULT_ACCESSIBLE_COLOR = '#00679e'; public const BACKGROUND_SHIPPED = 'shipped'; public const BACKGROUND_CUSTOM = 'custom'; diff --git a/apps/theming/lib/Themes/CommonThemeTrait.php b/apps/theming/lib/Themes/CommonThemeTrait.php index 3460c7e68ebbe..66775500f659c 100644 --- a/apps/theming/lib/Themes/CommonThemeTrait.php +++ b/apps/theming/lib/Themes/CommonThemeTrait.php @@ -64,7 +64,7 @@ protected function generatePrimaryVariables(string $colorMainBackground, string // used for buttons, inputs... '--color-primary-element' => $colorPrimaryElement, - '--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 81), + '--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 82), '--color-primary-element-text' => $this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', // mostly used for disabled states '--color-primary-element-text-dark' => $this->util->darken($this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', 6), diff --git a/cypress/e2e/theming/themingUtils.ts b/cypress/e2e/theming/themingUtils.ts index 088c58dda45b3..532cee46911b1 100644 --- a/cypress/e2e/theming/themingUtils.ts +++ b/cypress/e2e/theming/themingUtils.ts @@ -22,7 +22,7 @@ import { colord } from 'colord' export const defaultPrimary = '#0082c9' -export const defaultAccessiblePrimary = '#006889' +export const defaultAccessiblePrimary = '#00679e' export const defaultBackground = 'kamil-porembinski-clouds.jpg' /**