Merge pull request #41948 from nextcloud/fix/accessibility-dark-mode

fix(theming): Adjust dark theme to be accessible adjust cypress tests
pull/42253/head
Ferdinand Thiessen 5 months ago committed by GitHub
commit aa30452cb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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]')
})
})
}
}
})
}
})

@ -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;

@ -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,

@ -112,7 +112,7 @@ class DefaultTheme implements ITheme {
$colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow));
$colorError = '#d91812';
$colorWarning = '#c28900';
$colorWarning = '#b88100';
$colorSuccess = '#2d7b41';
$colorInfo = '#0071ad';

@ -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'],
})
})
})
}
}
})
}
})
}
})

@ -19,6 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import 'cypress-axe'
import './commands.ts'
// Fix ResizeObserver loop limit exceeded happening in Cypress only

Loading…
Cancel
Save