mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-19 10:19:27 -05:00
Chore: change dark mode to use Bootstrap's color modes (#4174)
* Change setting dark mode to use Bootstrap's data-bs-theme attribute * Update dark mode styling to use Bootstrap's color mode attribute * Update unit tests and lints * Fix not reflecting custom theme color * Remove commented-out code * fix inverted thumbnails in dark mode & card borders * prettier * Fix application of dark mode, tests --------- Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
parent
d1292c59ea
commit
78ae4c42f7
@ -46,10 +46,10 @@ test('should warn on unsaved changes', async ({ page }) => {
|
|||||||
test('should apply appearance changes when set', async ({ page }) => {
|
test('should apply appearance changes when set', async ({ page }) => {
|
||||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||||
await page.goto('/settings')
|
await page.goto('/settings')
|
||||||
await expect(page.locator('body')).toHaveClass(/color-scheme-system/)
|
await expect(page.locator('html')).toHaveAttribute('data-bs-theme', /auto/)
|
||||||
await page.getByLabel('Use system setting').click()
|
await page.getByLabel('Use system setting').click()
|
||||||
await page.getByLabel('Enable dark mode').click()
|
await page.getByLabel('Enable dark mode').click()
|
||||||
await expect(page.locator('body')).toHaveClass(/color-scheme-dark/)
|
await expect(page.locator('html')).toHaveAttribute('data-bs-theme', /dark/)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should toggle saved view options when set & saved', async ({ page }) => {
|
test('should toggle saved view options when set & saved', async ({ page }) => {
|
||||||
|
@ -147,13 +147,14 @@ describe('SettingsService', () => {
|
|||||||
).toEqual('')
|
).toEqual('')
|
||||||
|
|
||||||
const addClassSpy = jest.spyOn(settingsService.renderer, 'addClass')
|
const addClassSpy = jest.spyOn(settingsService.renderer, 'addClass')
|
||||||
const removeClassSpy = jest.spyOn(settingsService.renderer, 'removeClass')
|
const setAttributeSpy = jest.spyOn(settingsService.renderer, 'setAttribute')
|
||||||
|
|
||||||
settingsService.updateAppearanceSettings(true, true, '#fff000')
|
settingsService.updateAppearanceSettings(true, true, '#fff000')
|
||||||
expect(addClassSpy).toHaveBeenCalledWith(document.body, 'primary-light')
|
expect(addClassSpy).toHaveBeenCalledWith(document.body, 'primary-light')
|
||||||
expect(addClassSpy).toHaveBeenCalledWith(
|
expect(setAttributeSpy).toHaveBeenCalledWith(
|
||||||
document.body,
|
document.documentElement,
|
||||||
'color-scheme-system'
|
'data-bs-theme',
|
||||||
|
'auto'
|
||||||
)
|
)
|
||||||
expect(
|
expect(
|
||||||
document.body.style.getPropertyValue('--pngx-primary-lightness')
|
document.body.style.getPropertyValue('--pngx-primary-lightness')
|
||||||
@ -161,21 +162,23 @@ describe('SettingsService', () => {
|
|||||||
|
|
||||||
settingsService.updateAppearanceSettings(false, false, '#000000')
|
settingsService.updateAppearanceSettings(false, false, '#000000')
|
||||||
expect(addClassSpy).toHaveBeenCalledWith(document.body, 'primary-light')
|
expect(addClassSpy).toHaveBeenCalledWith(document.body, 'primary-light')
|
||||||
expect(removeClassSpy).toHaveBeenCalledWith(
|
expect(setAttributeSpy).toHaveBeenCalledWith(
|
||||||
document.body,
|
document.documentElement,
|
||||||
'color-scheme-system'
|
'data-bs-theme',
|
||||||
|
'light'
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
document.body.style.getPropertyValue('--pngx-primary-lightness')
|
document.body.style.getPropertyValue('--pngx-primary-lightness')
|
||||||
).toEqual('0%')
|
).toEqual('0%')
|
||||||
|
|
||||||
settingsService.updateAppearanceSettings(false, true, '#ffffff')
|
settingsService.updateAppearanceSettings(false, true, '#ffffff')
|
||||||
expect(addClassSpy).toHaveBeenCalledWith(document.body, 'primary-dark')
|
expect(addClassSpy).toHaveBeenCalledWith(document.body, 'primary-dark')
|
||||||
expect(removeClassSpy).toHaveBeenCalledWith(
|
expect(setAttributeSpy).toHaveBeenCalledWith(
|
||||||
document.body,
|
document.documentElement,
|
||||||
'color-scheme-system'
|
'data-bs-theme',
|
||||||
|
'dark'
|
||||||
)
|
)
|
||||||
expect(addClassSpy).toHaveBeenCalledWith(document.body, 'color-scheme-dark')
|
|
||||||
expect(
|
expect(
|
||||||
document.body.style.getPropertyValue('--pngx-primary-lightness')
|
document.body.style.getPropertyValue('--pngx-primary-lightness')
|
||||||
).toEqual('100%')
|
).toEqual('100%')
|
||||||
|
@ -106,19 +106,19 @@ export class SettingsService {
|
|||||||
themeColor ??= this.get(SETTINGS_KEYS.THEME_COLOR)
|
themeColor ??= this.get(SETTINGS_KEYS.THEME_COLOR)
|
||||||
|
|
||||||
if (darkModeUseSystem) {
|
if (darkModeUseSystem) {
|
||||||
this._renderer.addClass(this.document.body, 'color-scheme-system')
|
this._renderer.setAttribute(
|
||||||
this._renderer.removeClass(this.document.body, 'color-scheme-dark')
|
this.document.documentElement,
|
||||||
|
'data-bs-theme',
|
||||||
|
'auto'
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
this._renderer.removeClass(this.document.body, 'color-scheme-system')
|
this._renderer.setAttribute(
|
||||||
darkModeEnabled
|
this.document.documentElement,
|
||||||
? this._renderer.addClass(this.document.body, 'color-scheme-dark')
|
'data-bs-theme',
|
||||||
: this._renderer.removeClass(this.document.body, 'color-scheme-dark')
|
darkModeEnabled ? 'dark' : 'light'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove these in case they were there
|
|
||||||
this._renderer.removeClass(this.document.body, 'primary-dark')
|
|
||||||
this._renderer.removeClass(this.document.body, 'primary-light')
|
|
||||||
|
|
||||||
if (themeColor) {
|
if (themeColor) {
|
||||||
const hsl = hexToHsl(themeColor)
|
const hsl = hexToHsl(themeColor)
|
||||||
const bgBrightnessEstimate = estimateBrightnessForColor(themeColor)
|
const bgBrightnessEstimate = estimateBrightnessForColor(themeColor)
|
||||||
@ -142,6 +142,16 @@ export class SettingsService {
|
|||||||
`${hsl.l * 100}%`,
|
`${hsl.l * 100}%`,
|
||||||
RendererStyleFlags2.DashCase
|
RendererStyleFlags2.DashCase
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix for not reflecting changed variables. (--bs-primary is at :root while here we set them to body)
|
||||||
|
*/
|
||||||
|
this._renderer.setStyle(
|
||||||
|
document.body,
|
||||||
|
'--bs-primary',
|
||||||
|
'hsl(var(--pngx-primary), var(--pngx-primary-lightness))',
|
||||||
|
RendererStyleFlags2.DashCase
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
this._renderer.removeStyle(
|
this._renderer.removeStyle(
|
||||||
document.body,
|
document.body,
|
||||||
@ -153,6 +163,11 @@ export class SettingsService {
|
|||||||
'--pngx-primary-lightness',
|
'--pngx-primary-lightness',
|
||||||
RendererStyleFlags2.DashCase
|
RendererStyleFlags2.DashCase
|
||||||
)
|
)
|
||||||
|
this._renderer.removeStyle(
|
||||||
|
document.body,
|
||||||
|
'--bs-primary',
|
||||||
|
RendererStyleFlags2.DashCase
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en" data-bs-theme="auto">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Paperless-ngx</title>
|
<title>Paperless-ngx</title>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
<link rel="apple-touch-icon" href="apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="apple-touch-icon.png">
|
||||||
</head>
|
</head>
|
||||||
<body class="color-scheme-system">
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// bs options
|
// bs options
|
||||||
$enable-negative-margins: true;
|
$enable-negative-margins: true;
|
||||||
|
|
||||||
@import "theme";
|
|
||||||
@import "node_modules/bootstrap/scss/bootstrap";
|
@import "node_modules/bootstrap/scss/bootstrap";
|
||||||
|
@import "theme";
|
||||||
@import "~@ng-select/ng-select/themes/default.theme.css";
|
@import "~@ng-select/ng-select/themes/default.theme.css";
|
||||||
@import "print";
|
@import "print";
|
||||||
|
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
|
$color-mode-type: data;
|
||||||
|
|
||||||
|
@import 'bootstrap/scss/mixins/color-mode';
|
||||||
|
|
||||||
@mixin paperless-green {
|
@mixin paperless-green {
|
||||||
// base color e.g. #17541f = hsl(128, 57%, 21%)
|
// base color e.g. #17541f = hsl(128, 57%, 21%)
|
||||||
--pngx-primary: 128, 57%;
|
--pngx-primary: 128, 57%;
|
||||||
--pngx-primary-lightness: 21%;
|
--pngx-primary-lightness: 21%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
:root {
|
||||||
@include paperless-green;
|
@include paperless-green;
|
||||||
--pngx-primary-text-contrast: var(--bs-light);
|
--pngx-primary-text-contrast: var(--bs-light);
|
||||||
|
|
||||||
@ -158,7 +162,7 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt
|
|||||||
}
|
}
|
||||||
|
|
||||||
.doc-img {
|
.doc-img {
|
||||||
mix-blend-mode: normal;
|
mix-blend-mode: normal !important;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border-color: var(--bs-border-color);
|
border-color: var(--bs-border-color);
|
||||||
filter: invert(10%);
|
filter: invert(10%);
|
||||||
@ -270,9 +274,8 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body.color-scheme-dark {
|
@include color-mode(dark) {
|
||||||
// no custom theme color
|
body:not(.primary-light):not(.primary-dark) {
|
||||||
&:not(.primary-light):not(.primary-dark) {
|
|
||||||
@include paperless-green-dark-mode;
|
@include paperless-green-dark-mode;
|
||||||
|
|
||||||
.navbar.bg-primary {
|
.navbar.bg-primary {
|
||||||
@ -284,18 +287,22 @@ body.color-scheme-dark {
|
|||||||
@include dark-mode;
|
@include dark-mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
// Temp to not blink with white before angular loads
|
||||||
body.color-scheme-system {
|
@include color-mode(auto) {
|
||||||
// no custom theme color
|
@media (prefers-color-scheme: dark) {
|
||||||
&:not(.primary-light):not(.primary-dark) {
|
body {
|
||||||
@include paperless-green-dark-mode;
|
// no custom theme color
|
||||||
|
&:not(.primary-light):not(.primary-dark) {
|
||||||
|
@include paperless-green-dark-mode;
|
||||||
|
|
||||||
.navbar.bg-primary {
|
.navbar.bg-primary {
|
||||||
// navbar is og green in dark mode
|
// navbar is og green in dark mode
|
||||||
@include paperless-green;
|
@include paperless-green;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@include dark-mode;
|
@include dark-mode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user