Browse Source
Merge pull request #41948 from nextcloud/fix/accessibility-dark-mode
Merge pull request #41948 from nextcloud/fix/accessibility-dark-mode
fix(theming): Adjust dark theme to be accessible adjust cypress testspull/42253/head
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 163 additions and 137 deletions
-
128apps/theming/__tests__/accessibility.cy.ts
-
8apps/theming/css/default.css
-
8apps/theming/lib/Themes/DarkTheme.php
-
2apps/theming/lib/Themes/DefaultTheme.php
-
153cypress/e2e/theming/a11y-color-contrast.cy.ts
-
1cypress/support/e2e.ts
@ -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]') |
|||
}) |
|||
}) |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
@ -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'], |
|||
}) |
|||
}) |
|||
}) |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue