diff options
author | Julia Kirschenheuter <6078378+JuliaKirschenheuter@users.noreply.github.com> | 2023-12-27 19:02:43 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-27 19:02:43 +0100 |
commit | 4f66b3debcf17d86211b099b80c4a491d538986b (patch) | |
tree | 0a0790ad977ed31207dd9f1d0b6613389ae72dfe | |
parent | d053210eaa54ab1b9b1a40249d352a9d102a9e62 (diff) | |
parent | 45d4cae756aa7ffbc74a1c252ee85b1577ba8b74 (diff) | |
download | nextcloud-server-4f66b3debcf17d86211b099b80c4a491d538986b.tar.gz nextcloud-server-4f66b3debcf17d86211b099b80c4a491d538986b.zip |
Merge pull request #42429 from nextcloud/backport/42329/stable28
[stable28] fix(theming): Adjust dark high contrast to fulfill WCAG 2.1 AAA contrast
-rw-r--r-- | apps/theming/css/default.css | 8 | ||||
-rw-r--r-- | apps/theming/lib/Themes/CommonThemeTrait.php | 4 | ||||
-rw-r--r-- | apps/theming/lib/Themes/DarkHighContrastTheme.php | 31 | ||||
-rw-r--r-- | apps/theming/lib/Themes/DarkTheme.php | 12 | ||||
-rw-r--r-- | apps/theming/lib/Themes/DefaultTheme.php | 4 | ||||
-rw-r--r-- | apps/theming/lib/Util.php | 6 | ||||
-rw-r--r-- | apps/theming/tests/Themes/AccessibleThemeTestCase.php | 35 | ||||
-rw-r--r-- | apps/theming/tests/Themes/DarkHighContrastThemeTest.php | 142 |
8 files changed, 215 insertions, 27 deletions
diff --git a/apps/theming/css/default.css b/apps/theming/css/default.css index 19d874e5a8e..157e28982c0 100644 --- a/apps/theming/css/default.css +++ b/apps/theming/css/default.css @@ -21,10 +21,10 @@ /** @deprecated use `--color-text-maxcontrast` instead */ --color-text-lighter: var(--color-text-maxcontrast); --color-scrollbar: rgba(34,34,34, .15); - --color-error: #C00505; - --color-error-rgb: 192,5,5; - --color-error-hover: #c72424; - --color-error-text: #C00505; + --color-error: #DB0606; + --color-error-rgb: 219,6,6; + --color-error-hover: #df2525; + --color-error-text: #c20505; --color-warning: #A37200; --color-warning-rgb: 163,114,0; --color-warning-hover: #8a6000; diff --git a/apps/theming/lib/Themes/CommonThemeTrait.php b/apps/theming/lib/Themes/CommonThemeTrait.php index db92d400b32..42ee6212cee 100644 --- a/apps/theming/lib/Themes/CommonThemeTrait.php +++ b/apps/theming/lib/Themes/CommonThemeTrait.php @@ -38,9 +38,9 @@ trait CommonThemeTrait { * This is shared between multiple themes because colorMainBackground and colorMainText * will change in between. */ - protected function generatePrimaryVariables(string $colorMainBackground, string $colorMainText): array { + protected function generatePrimaryVariables(string $colorMainBackground, string $colorMainText, bool $highContrast = false): array { $isBrightColor = $this->util->isBrightColor($colorMainBackground); - $colorPrimaryElement = $this->util->elementColor($this->primaryColor, $isBrightColor, $colorMainBackground); + $colorPrimaryElement = $this->util->elementColor($this->primaryColor, $isBrightColor, $colorMainBackground, $highContrast); $colorPrimaryLight = $this->util->mix($colorPrimaryElement, $colorMainBackground, -80); $colorPrimaryElementLight = $this->util->mix($colorPrimaryElement, $colorMainBackground, -80); $invertPrimaryTextColor = $this->util->invertTextColor($colorPrimaryElement); diff --git a/apps/theming/lib/Themes/DarkHighContrastTheme.php b/apps/theming/lib/Themes/DarkHighContrastTheme.php index c76818e368f..de6fe2d4835 100644 --- a/apps/theming/lib/Themes/DarkHighContrastTheme.php +++ b/apps/theming/lib/Themes/DarkHighContrastTheme.php @@ -59,17 +59,22 @@ class DarkHighContrastTheme extends DarkTheme implements ITheme { $colorMainBackground = '#000000'; $colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground)); + $colorError = '#ff5252'; + $colorWarning = '#ffcc00'; + $colorSuccess = '#42a942'; + $colorInfo = '#38c0ff'; + return array_merge( $defaultVariables, - $this->generatePrimaryVariables($colorMainBackground, $colorMainText), + $this->generatePrimaryVariables($colorMainBackground, $colorMainText, true), [ '--color-main-background' => $colorMainBackground, '--color-main-background-rgb' => $colorMainBackgroundRGB, '--color-main-background-translucent' => 'rgba(var(--color-main-background-rgb), 1)', '--color-main-text' => $colorMainText, - '--color-background-dark' => $this->util->lighten($colorMainBackground, 30), - '--color-background-darker' => $this->util->lighten($colorMainBackground, 30), + '--color-background-dark' => $this->util->lighten($colorMainBackground, 25), + '--color-background-darker' => $this->util->lighten($colorMainBackground, 25), '--color-main-background-blur' => $colorMainBackground, '--filter-background-blur' => 'none', @@ -82,6 +87,26 @@ class DarkHighContrastTheme extends DarkTheme implements ITheme { '--color-text-light' => $colorMainText, '--color-text-lighter' => $colorMainText, + '--color-error' => $colorError, + '--color-error-rgb' => join(',', $this->util->hexToRGB($colorError)), + '--color-error-hover' => $this->util->lighten($colorError, 10), + '--color-error-text' => $this->util->lighten($colorError, 25), + + '--color-warning' => $colorWarning, + '--color-warning-rgb' => join(',', $this->util->hexToRGB($colorWarning)), + '--color-warning-hover' => $this->util->lighten($colorWarning, 10), + '--color-warning-text' => $this->util->lighten($colorWarning, 10), + + '--color-success' => $colorSuccess, + '--color-success-rgb' => join(',', $this->util->hexToRGB($colorSuccess)), + '--color-success-hover' => $this->util->lighten($colorSuccess, 10), + '--color-success-text' => $this->util->lighten($colorSuccess, 35), + + '--color-info' => $colorInfo, + '--color-info-rgb' => join(',', $this->util->hexToRGB($colorInfo)), + '--color-info-hover' => $this->util->lighten($colorInfo, 10), + '--color-info-text' => $this->util->lighten($colorInfo, 20), + '--color-scrollbar' => $this->util->lighten($colorMainBackground, 35), // used for the icon loading animation diff --git a/apps/theming/lib/Themes/DarkTheme.php b/apps/theming/lib/Themes/DarkTheme.php index 0fed06e2225..4b038a8812c 100644 --- a/apps/theming/lib/Themes/DarkTheme.php +++ b/apps/theming/lib/Themes/DarkTheme.php @@ -52,17 +52,17 @@ class DarkTheme extends DefaultTheme implements ITheme { public function getCSSVariables(): array { $defaultVariables = parent::getCSSVariables(); - $colorMainText = '#D8D8D8'; + $colorMainText = '#EBEBEB'; $colorMainBackground = '#171717'; $colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground)); - $colorTextMaxcontrast = $this->util->darken($colorMainText, 28); + $colorTextMaxcontrast = $this->util->darken($colorMainText, 32); $colorBoxShadow = $this->util->darken($colorMainBackground, 70); $colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow)); - $colorError = '#FF5252'; + $colorError = '#FF3333'; $colorWarning = '#FFCC00'; - $colorSuccess = '#50BB50'; + $colorSuccess = '#3B973B'; $colorInfo = '#00AEFF'; return array_merge( @@ -92,7 +92,7 @@ class DarkTheme extends DefaultTheme implements ITheme { '--color-error' => $colorError, '--color-error-rgb' => join(',', $this->util->hexToRGB($colorError)), '--color-error-hover' => $this->util->lighten($colorError, 10), - '--color-error-text' => $this->util->lighten($colorError, 10), + '--color-error-text' => $this->util->lighten($colorError, 15), '--color-warning' => $colorWarning, '--color-warning-rgb' => join(',', $this->util->hexToRGB($colorWarning)), '--color-warning-hover' => $this->util->lighten($colorWarning, 10), @@ -100,7 +100,7 @@ class DarkTheme extends DefaultTheme implements ITheme { '--color-success' => $colorSuccess, '--color-success-rgb' => join(',', $this->util->hexToRGB($colorSuccess)), '--color-success-hover' => $this->util->lighten($colorSuccess, 10), - '--color-success-text' => $colorSuccess, + '--color-success-text' => $this->util->lighten($colorSuccess, 15), '--color-info' => $colorInfo, '--color-info-rgb' => join(',', $this->util->hexToRGB($colorInfo)), '--color-info-hover' => $this->util->lighten($colorInfo, 10), diff --git a/apps/theming/lib/Themes/DefaultTheme.php b/apps/theming/lib/Themes/DefaultTheme.php index 599c5ed58a9..d36feb000ae 100644 --- a/apps/theming/lib/Themes/DefaultTheme.php +++ b/apps/theming/lib/Themes/DefaultTheme.php @@ -111,7 +111,7 @@ class DefaultTheme implements ITheme { $colorBoxShadow = $this->util->darken($colorMainBackground, 70); $colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow)); - $colorError = '#C00505'; + $colorError = '#DB0606'; $colorWarning = '#A37200'; $colorSuccess = '#2d7b41'; $colorInfo = '#0071ad'; @@ -148,7 +148,7 @@ class DefaultTheme implements ITheme { '--color-error' => $colorError, '--color-error-rgb' => join(',', $this->util->hexToRGB($colorError)), '--color-error-hover' => $this->util->mix($colorError, $colorMainBackground, 75), - '--color-error-text' => $colorError, + '--color-error-text' => $this->util->darken($colorError, 5), '--color-warning' => $colorWarning, '--color-warning-rgb' => join(',', $this->util->hexToRGB($colorWarning)), '--color-warning-hover' => $this->util->darken($colorWarning, 5), diff --git a/apps/theming/lib/Util.php b/apps/theming/lib/Util.php index c4f0123460e..71ab0a6dc6c 100644 --- a/apps/theming/lib/Util.php +++ b/apps/theming/lib/Util.php @@ -81,7 +81,7 @@ class Util { * @param ?bool $brightBackground * @return string */ - public function elementColor($color, ?bool $brightBackground = null, ?string $backgroundColor = null) { + public function elementColor($color, ?bool $brightBackground = null, ?string $backgroundColor = null, bool $highContrast = false) { if ($backgroundColor !== null) { $brightBackground = $brightBackground ?? $this->isBrightColor($backgroundColor); // Minimal amount that is possible to change the luminance @@ -93,7 +93,9 @@ class Util { $contrast = $this->colorContrast($color, $blurredBackground); // Min. element contrast is 3:1 but we need to keep hover states in mind -> min 3.2:1 - while ($contrast < 3.2 && $iteration++ < 100) { + $minContrast = $highContrast ? 5.5 : 3.2; + + while ($contrast < $minContrast && $iteration++ < 100) { $hsl = Color::hexToHsl($color); $hsl['L'] = max(0, min(1, $hsl['L'] + ($brightBackground ? -$epsilon : $epsilon))); $color = '#' . Color::hslToHex($hsl); diff --git a/apps/theming/tests/Themes/AccessibleThemeTestCase.php b/apps/theming/tests/Themes/AccessibleThemeTestCase.php index fbd0722552d..84121dd41b0 100644 --- a/apps/theming/tests/Themes/AccessibleThemeTestCase.php +++ b/apps/theming/tests/Themes/AccessibleThemeTestCase.php @@ -30,7 +30,15 @@ class AccessibleThemeTestCase extends TestCase { protected ITheme $theme; protected Util $util; + /** + * Set to true to check for WCAG AAA level accessibility + */ + protected bool $WCAGaaa = false; + public function dataAccessibilityPairs() { + $textContrast = $this->WCAGaaa ? 7.0 : 4.5; + $elementContrast = 3.0; + return [ 'primary-element on background' => [ [ @@ -44,7 +52,7 @@ class AccessibleThemeTestCase extends TestCase { '--color-background-darker', '--color-main-background-blur', ], - 3.0, + $elementContrast, ], 'status color elements on background' => [ [ @@ -64,7 +72,18 @@ class AccessibleThemeTestCase extends TestCase { '--color-background-darker', '--color-main-background-blur', ], - 3.0, + $elementContrast, + ], + // Those two colors are used for borders which will be `color-main-text` on focussed state, thus need 3:1 contrast to it + 'success-error-border-colors' => [ + [ + '--color-error', + '--color-success', + ], + [ + '--color-main-text', + ], + $elementContrast, ], 'primary-element-text' => [ [ @@ -75,7 +94,7 @@ class AccessibleThemeTestCase extends TestCase { '--color-primary-element', '--color-primary-element-hover', ], - 4.5, + $textContrast, ], 'primary-element-light-text' => [ ['--color-primary-element-light-text'], @@ -83,7 +102,7 @@ class AccessibleThemeTestCase extends TestCase { '--color-primary-element-light', '--color-primary-element-light-hover', ], - 4.5, + $textContrast, ], 'main-text' => [ ['--color-main-text'], @@ -94,7 +113,7 @@ class AccessibleThemeTestCase extends TestCase { '--color-background-darker', '--color-main-background-blur', ], - 4.5, + $textContrast, ], 'max-contrast-text' => [ ['--color-text-maxcontrast'], @@ -103,14 +122,14 @@ class AccessibleThemeTestCase extends TestCase { '--color-background-hover', '--color-background-dark', ], - 4.5, + $textContrast, ], 'max-contrast text-on blur' => [ ['--color-text-maxcontrast-background-blur'], [ '--color-main-background-blur', ], - 4.5, + $textContrast, ], 'status-text' => [ [ @@ -125,7 +144,7 @@ class AccessibleThemeTestCase extends TestCase { '--color-background-dark', '--color-main-background-blur', ], - 4.5, + $textContrast, ], ]; } diff --git a/apps/theming/tests/Themes/DarkHighContrastThemeTest.php b/apps/theming/tests/Themes/DarkHighContrastThemeTest.php new file mode 100644 index 00000000000..d3a357bcfe7 --- /dev/null +++ b/apps/theming/tests/Themes/DarkHighContrastThemeTest.php @@ -0,0 +1,142 @@ +<?php +/** + * @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com> + * + * @author John Molakvoæ <skjnldsv@protonmail.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\Theming\Tests\Themes; + +use OCA\Theming\AppInfo\Application; +use OCA\Theming\ImageManager; +use OCA\Theming\ITheme; +use OCA\Theming\Service\BackgroundService; +use OCA\Theming\Themes\DarkHighContrastTheme; +use OCA\Theming\ThemingDefaults; +use OCA\Theming\Util; +use OCP\App\IAppManager; +use OCP\Files\IAppData; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; + +class DarkHighContrastThemeTest extends AccessibleThemeTestCase { + /** @var ThemingDefaults|MockObject */ + private $themingDefaults; + /** @var IUserSession|MockObject */ + private $userSession; + /** @var IURLGenerator|MockObject */ + private $urlGenerator; + /** @var ImageManager|MockObject */ + private $imageManager; + /** @var IConfig|MockObject */ + private $config; + /** @var IL10N|MockObject */ + private $l10n; + /** @var IAppManager|MockObject */ + private $appManager; + + // !! important: Enable WCAG AAA tests + protected bool $WCAGaaa = true; + + protected function setUp(): void { + $this->themingDefaults = $this->createMock(ThemingDefaults::class); + $this->userSession = $this->createMock(IUserSession::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->imageManager = $this->createMock(ImageManager::class); + $this->config = $this->createMock(IConfig::class); + $this->l10n = $this->createMock(IL10N::class); + $this->appManager = $this->createMock(IAppManager::class); + + $this->util = new Util( + $this->config, + $this->appManager, + $this->createMock(IAppData::class), + $this->imageManager + ); + + $this->themingDefaults + ->expects($this->any()) + ->method('getColorPrimary') + ->willReturn('#0082c9'); + + $this->themingDefaults + ->expects($this->any()) + ->method('getDefaultColorPrimary') + ->willReturn('#0082c9'); + + $this->themingDefaults + ->expects($this->any()) + ->method('getBackground') + ->willReturn('/apps/' . Application::APP_ID . '/img/background/' . BackgroundService::DEFAULT_BACKGROUND_IMAGE); + + $this->l10n + ->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($text, $parameters = []) { + return vsprintf($text, $parameters); + }); + + $this->urlGenerator + ->expects($this->any()) + ->method('imagePath') + ->willReturnCallback(function ($app = 'core', $filename = '') { + return "/$app/img/$filename"; + }); + + $this->theme = new DarkHighContrastTheme( + $this->util, + $this->themingDefaults, + $this->userSession, + $this->urlGenerator, + $this->imageManager, + $this->config, + $this->l10n, + $this->appManager, + ); + + parent::setUp(); + } + + + public function testGetId() { + $this->assertEquals('dark-highcontrast', $this->theme->getId()); + } + + public function testGetType() { + $this->assertEquals(ITheme::TYPE_THEME, $this->theme->getType()); + } + + public function testGetTitle() { + $this->assertEquals('Dark theme with high contrast mode', $this->theme->getTitle()); + } + + public function testGetEnableLabel() { + $this->assertEquals('Enable dark high contrast mode', $this->theme->getEnableLabel()); + } + + public function testGetDescription() { + $this->assertEquals('Similar to the high contrast mode, but with dark colours.', $this->theme->getDescription()); + } + + public function testGetMediaQuery() { + $this->assertEquals('(prefers-color-scheme: dark) and (prefers-contrast: more)', $this->theme->getMediaQuery()); + } +} |