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 }) => { | ||||
|   await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' }) | ||||
|   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('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 }) => { | ||||
|   | ||||
| @@ -147,13 +147,14 @@ describe('SettingsService', () => { | ||||
|     ).toEqual('') | ||||
|  | ||||
|     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') | ||||
|     expect(addClassSpy).toHaveBeenCalledWith(document.body, 'primary-light') | ||||
|     expect(addClassSpy).toHaveBeenCalledWith( | ||||
|       document.body, | ||||
|       'color-scheme-system' | ||||
|     expect(setAttributeSpy).toHaveBeenCalledWith( | ||||
|       document.documentElement, | ||||
|       'data-bs-theme', | ||||
|       'auto' | ||||
|     ) | ||||
|     expect( | ||||
|       document.body.style.getPropertyValue('--pngx-primary-lightness') | ||||
| @@ -161,21 +162,23 @@ describe('SettingsService', () => { | ||||
|  | ||||
|     settingsService.updateAppearanceSettings(false, false, '#000000') | ||||
|     expect(addClassSpy).toHaveBeenCalledWith(document.body, 'primary-light') | ||||
|     expect(removeClassSpy).toHaveBeenCalledWith( | ||||
|       document.body, | ||||
|       'color-scheme-system' | ||||
|     expect(setAttributeSpy).toHaveBeenCalledWith( | ||||
|       document.documentElement, | ||||
|       'data-bs-theme', | ||||
|       'light' | ||||
|     ) | ||||
|  | ||||
|     expect( | ||||
|       document.body.style.getPropertyValue('--pngx-primary-lightness') | ||||
|     ).toEqual('0%') | ||||
|  | ||||
|     settingsService.updateAppearanceSettings(false, true, '#ffffff') | ||||
|     expect(addClassSpy).toHaveBeenCalledWith(document.body, 'primary-dark') | ||||
|     expect(removeClassSpy).toHaveBeenCalledWith( | ||||
|       document.body, | ||||
|       'color-scheme-system' | ||||
|     expect(setAttributeSpy).toHaveBeenCalledWith( | ||||
|       document.documentElement, | ||||
|       'data-bs-theme', | ||||
|       'dark' | ||||
|     ) | ||||
|     expect(addClassSpy).toHaveBeenCalledWith(document.body, 'color-scheme-dark') | ||||
|     expect( | ||||
|       document.body.style.getPropertyValue('--pngx-primary-lightness') | ||||
|     ).toEqual('100%') | ||||
|   | ||||
| @@ -106,19 +106,19 @@ export class SettingsService { | ||||
|     themeColor ??= this.get(SETTINGS_KEYS.THEME_COLOR) | ||||
|  | ||||
|     if (darkModeUseSystem) { | ||||
|       this._renderer.addClass(this.document.body, 'color-scheme-system') | ||||
|       this._renderer.removeClass(this.document.body, 'color-scheme-dark') | ||||
|       this._renderer.setAttribute( | ||||
|         this.document.documentElement, | ||||
|         'data-bs-theme', | ||||
|         'auto' | ||||
|       ) | ||||
|     } else { | ||||
|       this._renderer.removeClass(this.document.body, 'color-scheme-system') | ||||
|       darkModeEnabled | ||||
|         ? this._renderer.addClass(this.document.body, 'color-scheme-dark') | ||||
|         : this._renderer.removeClass(this.document.body, 'color-scheme-dark') | ||||
|       this._renderer.setAttribute( | ||||
|         this.document.documentElement, | ||||
|         'data-bs-theme', | ||||
|         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) { | ||||
|       const hsl = hexToHsl(themeColor) | ||||
|       const bgBrightnessEstimate = estimateBrightnessForColor(themeColor) | ||||
| @@ -142,6 +142,16 @@ export class SettingsService { | ||||
|         `${hsl.l * 100}%`, | ||||
|         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 { | ||||
|       this._renderer.removeStyle( | ||||
|         document.body, | ||||
| @@ -153,6 +163,11 @@ export class SettingsService { | ||||
|         '--pngx-primary-lightness', | ||||
|         RendererStyleFlags2.DashCase | ||||
|       ) | ||||
|       this._renderer.removeStyle( | ||||
|         document.body, | ||||
|         '--bs-primary', | ||||
|         RendererStyleFlags2.DashCase | ||||
|       ) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <!doctype html> | ||||
| <html lang="en"> | ||||
| <html lang="en" data-bs-theme="auto"> | ||||
| <head> | ||||
|   <meta charset="utf-8"> | ||||
|   <title>Paperless-ngx</title> | ||||
| @@ -11,7 +11,7 @@ | ||||
|   <link rel="icon" type="image/x-icon" href="favicon.ico"> | ||||
|   <link rel="apple-touch-icon" href="apple-touch-icon.png"> | ||||
| </head> | ||||
| <body class="color-scheme-system"> | ||||
| <body> | ||||
|   <app-root></app-root> | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| // bs options | ||||
| $enable-negative-margins: true; | ||||
|  | ||||
| @import "theme"; | ||||
| @import "node_modules/bootstrap/scss/bootstrap"; | ||||
| @import "theme"; | ||||
| @import "~@ng-select/ng-select/themes/default.theme.css"; | ||||
| @import "print"; | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,14 @@ | ||||
| $color-mode-type: data; | ||||
|  | ||||
| @import 'bootstrap/scss/mixins/color-mode'; | ||||
|  | ||||
| @mixin paperless-green { | ||||
|   // base color e.g. #17541f = hsl(128, 57%, 21%) | ||||
|   --pngx-primary: 128, 57%; | ||||
|   --pngx-primary-lightness: 21%; | ||||
| } | ||||
|  | ||||
| body { | ||||
| :root { | ||||
|   @include paperless-green; | ||||
|   --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 { | ||||
|     mix-blend-mode: normal; | ||||
|     mix-blend-mode: normal !important; | ||||
|     border-radius: 0; | ||||
|     border-color: var(--bs-border-color); | ||||
|     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 { | ||||
|   // no custom theme color | ||||
|   &:not(.primary-light):not(.primary-dark) { | ||||
| @include color-mode(dark) { | ||||
|   body:not(.primary-light):not(.primary-dark) { | ||||
|     @include paperless-green-dark-mode; | ||||
|  | ||||
|     .navbar.bg-primary { | ||||
| @@ -284,8 +287,10 @@ body.color-scheme-dark { | ||||
|   @include dark-mode; | ||||
| } | ||||
|  | ||||
| // Temp to not blink with white before angular loads | ||||
| @include color-mode(auto) { | ||||
|   @media (prefers-color-scheme: dark) { | ||||
|   body.color-scheme-system { | ||||
|     body { | ||||
|       // no custom theme color | ||||
|       &:not(.primary-light):not(.primary-dark) { | ||||
|         @include paperless-green-dark-mode; | ||||
| @@ -299,3 +304,5 @@ body.color-scheme-dark { | ||||
|       @include dark-mode; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user