diff --git a/apps/theming/__tests__/accessibility.cy.ts b/apps/theming/__tests__/accessibility.cy.ts deleted file mode 100644 index 3bbf8a64972..00000000000 --- a/apps/theming/__tests__/accessibility.cy.ts +++ /dev/null @@ -1,128 +0,0 @@ -// eslint-disable-next-line import/no-webpack-loader-syntax, import/no-unresolved -import style from '!raw-loader!../css/default.css' - -const testCases = { - 'Main text': { - foregroundColors: [ - 'color-main-text', - // 'color-text-light', deprecated - // 'color-text-lighter', deprecated - 'color-text-maxcontrast', - 'color-text-maxcontrast-default', - ], - backgroundColors: [ - 'color-background-main', - 'color-background-hover', - 'color-background-dark', - // 'color-background-darker', this should only be used for elements not for text - ], - }, - Primary: { - foregroundColors: [ - 'color-primary-text', - ], - backgroundColors: [ - // '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', - ], - }, - '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', - ], - }, - 'Servity information texts': { - foregroundColors: [ - 'color-error-text', - 'color-warning-text', - 'color-success-text', - 'color-info-text', - ], - backgroundColors: [ - 'color-background-main', - 'color-background-hover', - ], - }, -} - -/** - * 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 -} - -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 [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 190ec0fb9cf..17b3eea8381 100644 --- a/apps/theming/css/default.css +++ b/apps/theming/css/default.css @@ -25,10 +25,10 @@ --color-error-rgb: 217,24,18; --color-error-hover: #dd342f; --color-error-text: #c61610; - --color-warning: #c28900; - --color-warning-rgb: 194,137,0; - --color-warning-hover: #cea032; - --color-warning-text: #8f6500; + --color-warning: #b88100; + --color-warning-rgb: 184,129,0; + --color-warning-hover: #c69a32; + --color-warning-text: #855d00; --color-success: #2d7b41; --color-success-rgb: 45,123,65; --color-success-hover: #448955; diff --git a/apps/theming/lib/Themes/DarkTheme.php b/apps/theming/lib/Themes/DarkTheme.php index af374a01603..d33f13dc579 100644 --- a/apps/theming/lib/Themes/DarkTheme.php +++ b/apps/theming/lib/Themes/DarkTheme.php @@ -55,15 +55,15 @@ class DarkTheme extends DefaultTheme implements ITheme { $colorMainText = '#D8D8D8'; $colorMainBackground = '#171717'; $colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground)); - $colorTextMaxcontrast = $this->util->darken($colorMainText, 30); + $colorTextMaxcontrast = $this->util->darken($colorMainText, 28); $colorBoxShadow = $this->util->darken($colorMainBackground, 70); $colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow)); - $colorError = '#d91812'; + $colorError = '#ee312b'; $colorWarning = '#c28900'; - $colorSuccess = '#2d7b41'; - $colorInfo = '#0071ad'; + $colorSuccess = '#36914e'; + $colorInfo = '#007bbd'; return array_merge( $defaultVariables, diff --git a/apps/theming/lib/Themes/DefaultTheme.php b/apps/theming/lib/Themes/DefaultTheme.php index 44930e457fa..e215ade7b6d 100644 --- a/apps/theming/lib/Themes/DefaultTheme.php +++ b/apps/theming/lib/Themes/DefaultTheme.php @@ -112,7 +112,7 @@ class DefaultTheme implements ITheme { $colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow)); $colorError = '#d91812'; - $colorWarning = '#c28900'; + $colorWarning = '#b88100'; $colorSuccess = '#2d7b41'; $colorInfo = '#0071ad'; diff --git a/cypress/e2e/theming/a11y-color-contrast.cy.ts b/cypress/e2e/theming/a11y-color-contrast.cy.ts new file mode 100644 index 00000000000..03a6814ea1f --- /dev/null +++ b/cypress/e2e/theming/a11y-color-contrast.cy.ts @@ -0,0 +1,153 @@ +const themesToTest = ['light', 'dark', 'light-highcontrast', 'dark-highcontrast'] + +const testCases = { + 'Main text': { + foregroundColors: [ + 'color-main-text', + // 'color-text-light', deprecated + // 'color-text-lighter', deprecated + 'color-text-maxcontrast', + ], + backgroundColors: [ + 'color-main-background', + 'color-background-hover', + 'color-background-dark', + // 'color-background-darker', this should only be used for elements not for text + ], + }, + 'blurred background': { + foregroundColors: [ + 'color-main-text', + 'color-text-maxcontrast-blur', + ], + backgroundColors: [ + 'color-main-background-blur', + ], + }, + Primary: { + foregroundColors: [ + 'color-primary-text', + ], + backgroundColors: [ + // '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', + ], + }, + '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', + ], + }, + 'Servity information texts': { + foregroundColors: [ + 'color-error-text', + 'color-warning-text', + 'color-success-text', + 'color-info-text', + ], + backgroundColors: [ + 'color-main-background', + 'color-background-hover', + 'color-main-background-blur', + ], + }, +} + +/** + * 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.style.padding = '14px' + wrapper.style.color = `var(--${foreground})` + wrapper.style.backgroundColor = `var(--${background})` + if (background.includes('blur')) { + wrapper.style.backdropFilter = 'var(--filter-background-blur)' + } + + const testCase = document.createElement('div') + testCase.innerText = `${foreground} ${background}` + testCase.setAttribute('data-cy-testcase', '') + + wrapper.appendChild(testCase) + return wrapper +} + +describe('Accessibility of Nextcloud theming colors', () => { + for (const theme of themesToTest) { + context(`Theme: ${theme}`, () => { + before(() => { + cy.createRandomUser().then(($user) => { + // set user theme + cy.runOccCommand(`user:setting -- '${$user.userId}' theming enabled-themes '["${theme}"]'`) + cy.login($user) + cy.visit('/') + cy.injectAxe({ axeCorePath: 'node_modules/axe-core/axe.min.js' }) + }) + }) + + beforeEach(() => { + cy.document().then(doc => { + // Unset background image and thus use background-color for testing blur background (images do not work with axe-core) + doc.body.style.backgroundImage = 'unset' + + const root = doc.querySelector('main') + if (root === null) { + throw new Error('No test root found') + } + root.innerHTML = '' + }) + }) + + 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}`, () => { + cy.document().then(doc => { + const element = createTestCase(foreground, background) + const root = doc.querySelector('main') + // 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]', { + runOnly: ['color-contrast'], + }) + }) + }) + } + } + }) + } + }) + } +}) diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts index 2bc91f83ebd..6cf83e94961 100644 --- a/cypress/support/e2e.ts +++ b/cypress/support/e2e.ts @@ -19,6 +19,7 @@ * along with this program. If not, see . * */ +import 'cypress-axe' import './commands.ts' // Fix ResizeObserver loop limit exceeded happening in Cypress only