diff options
author | Ferdinand Thiessen <opensource@fthiessen.de> | 2023-10-25 13:57:28 +0200 |
---|---|---|
committer | Ferdinand Thiessen <opensource@fthiessen.de> | 2023-10-27 12:56:23 +0200 |
commit | 3676295f7b13852ec328e32f510aefd4c7b709c7 (patch) | |
tree | 77236e44d27adfac9bb54c5bdae7210fbf333c88 /apps | |
parent | be129a7d1f256a9a6aeaadbd9355c088fe0df929 (diff) | |
download | nextcloud-server-3676295f7b13852ec328e32f510aefd4c7b709c7.tar.gz nextcloud-server-3676295f7b13852ec328e32f510aefd4c7b709c7.zip |
fix(theming): Ensure all text colors have enough contrast for accessibility
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Diffstat (limited to 'apps')
-rw-r--r-- | apps/theming/__tests__/accessibility.cy.ts | 94 | ||||
-rw-r--r-- | apps/theming/css/default.css | 32 | ||||
-rw-r--r-- | apps/theming/lib/Service/BackgroundService.php | 2 | ||||
-rw-r--r-- | apps/theming/lib/Themes/CommonThemeTrait.php | 6 | ||||
-rw-r--r-- | apps/theming/lib/Themes/DefaultTheme.php | 4 | ||||
-rw-r--r-- | apps/theming/tests/Themes/DefaultThemeTest.php | 2 |
6 files changed, 78 insertions, 62 deletions
diff --git a/apps/theming/__tests__/accessibility.cy.ts b/apps/theming/__tests__/accessibility.cy.ts index bea8e5febde..3bbf8a64972 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 571ae8a7829..8cc3eb2de32 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 d5bc4296b5b..3886bf855c4 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 9b516bf6c70..3460c7e68eb 100644 --- a/apps/theming/lib/Themes/CommonThemeTrait.php +++ b/apps/theming/lib/Themes/CommonThemeTrait.php @@ -64,15 +64,15 @@ trait CommonThemeTrait { // 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 0c2594ebe7f..e2bd31548ca 100644 --- a/apps/theming/lib/Themes/DefaultTheme.php +++ b/apps/theming/lib/Themes/DefaultTheme.php @@ -104,7 +104,7 @@ class DefaultTheme implements ITheme { $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 @@ class DefaultTheme implements ITheme { '--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 6044f5c10d3..0d86a8d6b28 100644 --- a/apps/theming/tests/Themes/DefaultThemeTest.php +++ b/apps/theming/tests/Themes/DefaultThemeTest.php @@ -157,6 +157,8 @@ class DefaultThemeTest extends TestCase { $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); } |