mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -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:
		 Dominik Mielcarek
					Dominik Mielcarek
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						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; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user