aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorFerdinand Thiessen <opensource@fthiessen.de>2023-10-25 13:57:28 +0200
committerFerdinand Thiessen <opensource@fthiessen.de>2023-10-27 12:56:23 +0200
commit3676295f7b13852ec328e32f510aefd4c7b709c7 (patch)
tree77236e44d27adfac9bb54c5bdae7210fbf333c88 /apps
parentbe129a7d1f256a9a6aeaadbd9355c088fe0df929 (diff)
downloadnextcloud-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.ts94
-rw-r--r--apps/theming/css/default.css32
-rw-r--r--apps/theming/lib/Service/BackgroundService.php2
-rw-r--r--apps/theming/lib/Themes/CommonThemeTrait.php6
-rw-r--r--apps/theming/lib/Themes/DefaultTheme.php4
-rw-r--r--apps/theming/tests/Themes/DefaultThemeTest.php2
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);
}