mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Initial build of primary color contrast
This commit is contained in:
		| @@ -132,8 +132,22 @@ export class SettingsService { | ||||
|         : this.renderer.removeClass(this.document.body, 'color-scheme-dark') | ||||
|     } | ||||
|  | ||||
|     // remove these in case they were there | ||||
|     this.renderer.removeClass(this.document.body, 'text-bg-dark') | ||||
|     this.renderer.removeClass(this.document.body, 'text-bg-light') | ||||
|  | ||||
|     if (themeColor) { | ||||
|       const hsl = hexToHsl(themeColor) | ||||
|       const useDarkTextColor = | ||||
|         parseInt(themeColor.replace('#', ''), 16) > 0xffffff / 1.5 | ||||
|  | ||||
|       if (useDarkTextColor) { | ||||
|         this.renderer.addClass(this.document.body, 'text-bg-dark') | ||||
|         this.renderer.removeClass(this.document.body, 'text-bg-light') | ||||
|       } else { | ||||
|         this.renderer.addClass(this.document.body, 'text-bg-light') | ||||
|         this.renderer.removeClass(this.document.body, 'text-bg-dark') | ||||
|       } | ||||
|       this.renderer.setStyle( | ||||
|         document.documentElement, | ||||
|         '--pngx-primary', | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { HSL } from 'ngx-color' | ||||
| import { HSL, RGB } from 'ngx-color' | ||||
|  | ||||
| function componentToHex(c) { | ||||
|   var hex = Math.floor(c).toString(16) | ||||
| @@ -86,14 +86,19 @@ export function rgbToHsl(r, g, b) { | ||||
| } | ||||
|  | ||||
| export function hexToHsl(hex: string): HSL { | ||||
|   const rgb = hexToRGB(hex) | ||||
|   const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b) | ||||
|   return { h: hsl[0], s: hsl[1], l: hsl[2] } | ||||
| } | ||||
|  | ||||
| export function hexToRGB(hex: string): RGB { | ||||
|   hex = hex.replace('#', '') | ||||
|   let aRgbHex = hex.match(/.{1,2}/g) | ||||
|   const hsl = rgbToHsl( | ||||
|     parseInt(aRgbHex[0], 16), | ||||
|     parseInt(aRgbHex[1], 16), | ||||
|     parseInt(aRgbHex[2], 16) | ||||
|   ) | ||||
|   return { h: hsl[0], s: hsl[1], l: hsl[2] } | ||||
|   return { | ||||
|     r: parseInt(aRgbHex[0], 16), | ||||
|     g: parseInt(aRgbHex[1], 16), | ||||
|     b: parseInt(aRgbHex[2], 16), | ||||
|   } | ||||
| } | ||||
|  | ||||
| export function randomColor() { | ||||
|   | ||||
| @@ -4,7 +4,6 @@ $enable-negative-margins: true; | ||||
| @import "node_modules/bootstrap/scss/bootstrap"; | ||||
| @import "~@ng-select/ng-select/themes/default.theme.css"; | ||||
| @import "theme"; | ||||
| @import "theme_dark"; | ||||
| @import "print"; | ||||
|  | ||||
| // Paperless-ngx styles | ||||
| @@ -36,9 +35,19 @@ svg.logo { | ||||
|  | ||||
| .bg-primary { | ||||
|   background-color: var(--bs-primary) !important; | ||||
|   color: var(--pngx-primary-text-contrast); | ||||
| } | ||||
|  | ||||
| .navbar-brand { | ||||
|   color: var(--pngx-primary-text-contrast) !important; | ||||
| } | ||||
|  | ||||
| .navbar .dropdown .btn { | ||||
|   color: var(--pngx-primary-text-contrast) !important; | ||||
| } | ||||
|  | ||||
| .btn-primary { | ||||
|   color: var(--pngx-primary-text-contrast); | ||||
|   background-color: var(--bs-primary); | ||||
|   border-color: var(--bs-primary); | ||||
|  | ||||
| @@ -48,6 +57,7 @@ svg.logo { | ||||
|   } | ||||
|  | ||||
|   &:disabled, &.disabled { | ||||
|     color: var(--pngx-primary-text-contrast); | ||||
|     background-color: var(--pngx-primary-darken-10) !important; | ||||
|     border-color: var(--pngx-primary-darken-10) !important; | ||||
|   } | ||||
| @@ -350,6 +360,14 @@ table.table { | ||||
|   } | ||||
| } | ||||
|  | ||||
| .toast { | ||||
|   color: var(--pngx-primary-text-contrast); | ||||
|  | ||||
|   .toast-header { | ||||
|     color: var(--pngx-primary-text-contrast); | ||||
|   } | ||||
| } | ||||
|  | ||||
| .close { | ||||
|   color: var(--bs-body-color); | ||||
| } | ||||
|   | ||||
| @@ -15,3 +15,195 @@ | ||||
|   --pngx-bg-darker: var(--bs-gray-100); | ||||
|   --pngx-focus-alpha: 0.3; | ||||
| } | ||||
|  | ||||
| // Dark text colors allow for maintain contrast with theme color changes | ||||
| $text-color-light-bg: #212529; | ||||
| $text-color-dark-bg: #abb2bf; | ||||
| $text-color-dark-bg-accent: lighten($text-color-dark-bg, 10%); | ||||
| // Taken from bootstrap | ||||
| $form-check-input-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path fill='none' stroke='#212529' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/></svg>"); | ||||
| $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='2' fill='#212529'/></svg>"); | ||||
|  | ||||
| .text-bg-light { | ||||
|   --pngx-primary-text-contrast: #{$text-color-light-bg} !important; | ||||
|  | ||||
|   .form-check-input:checked[type=checkbox] { | ||||
|     background-image: escape-svg($form-check-input-checked-bg-image-dark); | ||||
|   } | ||||
|  | ||||
|   .form-check-input:checked[type=radio] { | ||||
|     background-image: escape-svg($form-check-radio-checked-bg-image-dark); | ||||
|   } | ||||
| } | ||||
|  | ||||
| .text-bg-dark { | ||||
|   --pngx-primary-text-contrast: #{$text-color-dark-bg} !important; | ||||
| } | ||||
|  | ||||
| // Dark mode | ||||
| $primary-dark-mode: #45973a; | ||||
| $primary-dark-mode-rgb: 69, 151, 58; | ||||
| $primary-dark-mode-darken-10: darken($primary-dark-mode, 10%); | ||||
| $danger-dark-mode: #b71631; | ||||
| $danger-dark-mode-rgb: 183, 22, 49; | ||||
| $bg-dark-mode: #161618; | ||||
| $bg-dark-mode-rgb: 22, 22, 24; | ||||
| $bg-dark-mode-accent: #101216; | ||||
| $bg-dark-mode-alt: #242529; | ||||
| $bg-light-dark-mode: #1c1c1f; | ||||
| $bg-light-dark-mode-rgb: 28, 28, 31; | ||||
| $border-color-dark-mode: #47494f; | ||||
|  | ||||
| @mixin dark-mode { | ||||
|   --bs-primary: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 10%)); | ||||
|   --bs-body-color: #{$text-color-dark-bg}; | ||||
|   --pngx-body-color-accent: #{$text-color-dark-bg-accent}; | ||||
|   --bs-danger: #{$danger-dark-mode}; | ||||
|   --bs-danger-rgb: #{$danger-dark-mode-rgb}; | ||||
|   --bs-body-bg: #{$bg-dark-mode}; | ||||
|   --bs-body-bg-rgb: #{$bg-dark-mode-rgb}; | ||||
|   --bs-light: #{$bg-light-dark-mode}; | ||||
|   --bs-light-rgb: #{$bg-light-dark-mode-rgb}; | ||||
|   --bs-border-color: #{$border-color-dark-mode}; | ||||
|   --pngx-bg-darker: #{$bg-dark-mode-accent}; | ||||
|   --pngx-bg-alt: #{$bg-dark-mode-alt}; | ||||
|   --pngx-focus-alpha: 0.7; | ||||
|   --pngx-primary-faded: var(--pngx-primary-darken-15); | ||||
|   --pngx-primary-text-contrast: var(--bs-body-color); | ||||
|  | ||||
|   .navbar.bg-primary { | ||||
|     --bs-primary: hsl(var(--pngx-primary),var(--pngx-primary-lightness)); | ||||
|     --bs-primary-rgb: var(--bs-primary); | ||||
|   } | ||||
|  | ||||
|   .border { | ||||
|     border-color: var(--bs-border-color) !important; | ||||
|   } | ||||
|  | ||||
|   .border-end { | ||||
|     border-right: 1px solid var(--bs-border-color) !important; | ||||
|   } | ||||
|  | ||||
|   .border-start { | ||||
|     border-left: 1px solid var(--bs-border-color) !important; | ||||
|   } | ||||
|  | ||||
|   .border-bottom { | ||||
|     border-bottom: 1px solid var(--bs-border-color) !important; | ||||
|   } | ||||
|  | ||||
|   .text-dark, .text-light { | ||||
|     color: var(--bs-body-color) !important; | ||||
|   } | ||||
|  | ||||
|   .btn-outline-primary, .btn-primary { | ||||
|     &:hover, &:focus, &.active, &:active { | ||||
|       color: var(--bs-light) !important; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .btn-outline-secondary { | ||||
|     &:hover, &:focus, &.active, &:active { | ||||
|       background-color: var(--pngx-bg-darker); | ||||
|       color: var(--bs-primary); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .search-form-container { | ||||
|     input, input:focus { | ||||
|       color: var(--bs-body-color) !important; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .card { | ||||
|     background-color: var(--bs-body-bg); | ||||
|  | ||||
|     .card-header { | ||||
|       background-color: rgba(0, 0, 0, 0.12); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .modal-content, .modal-header, .modal-body, .modal-footer { | ||||
|     background-color: var(--bs-body-bg); | ||||
|     border-color: var(--bs-border-color); | ||||
|   } | ||||
|  | ||||
|   app-tag .badge { | ||||
|     filter: brightness(.8); | ||||
|   } | ||||
|  | ||||
|   .doc-img-container { | ||||
|     border: none !important; | ||||
|     border-top-left-radius: .25rem; | ||||
|     border-top-right-radius: .25rem; | ||||
|     overflow: hidden; | ||||
|   } | ||||
|  | ||||
|   .doc-img { | ||||
|     mix-blend-mode: normal; | ||||
|     border-radius: 0; | ||||
|     border-color: var(--bs-border-color); | ||||
|     filter: invert(10%); | ||||
|  | ||||
|     &.border-end { | ||||
|       border-right: none !important; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .doc-img.inverted { | ||||
|     filter: invert(95%) hue-rotate(180deg); | ||||
|   } | ||||
|  | ||||
|   .card-selected .doc-img { | ||||
|     mix-blend-mode: luminosity; | ||||
|   } | ||||
|  | ||||
|   .ng-dropdown-panel .ng-dropdown-panel-items .ng-option:hover, | ||||
|   .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-marked { | ||||
|     background-color: $bg-light-dark-mode; | ||||
|   } | ||||
|  | ||||
|   table { | ||||
|     .des, | ||||
|     .asc { | ||||
|       &::after { | ||||
|         filter: invert(0.8); /* arrow is a black inline png bkgd image (!) so use filter */ | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     &.table-hover > tbody > tr:hover > * { | ||||
|       background-color: $bg-light-dark-mode; | ||||
|       color: var(--pngx-body-color-accent); | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   .table-striped > tbody > tr:nth-of-type(odd) > * { | ||||
|     color: var(--pngx-body-color-accent); | ||||
|   } | ||||
|  | ||||
|   .close, .modal .btn-close, .alert .btn-close { | ||||
|     text-shadow: 0 1px 0 #666; | ||||
|   } | ||||
|  | ||||
|   .modal .btn-close, .alert .btn-close { | ||||
|     filter: invert(1) grayscale(100%) brightness(200%); | ||||
|   } | ||||
|  | ||||
|   .toast { | ||||
|     background-color: hsla(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 18%), 0.9); | ||||
|   } | ||||
|  | ||||
|   .toast-header { | ||||
|     background-color: hsla(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 10%), 0.9); | ||||
|   } | ||||
| } | ||||
|  | ||||
| body.color-scheme-dark { | ||||
|   @include dark-mode; | ||||
| } | ||||
| body.color-scheme-system { | ||||
|   @media (prefers-color-scheme: dark) { | ||||
|     @include dark-mode; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,171 +0,0 @@ | ||||
| $primary-dark-mode: #45973a; | ||||
| $primary-dark-mode-rgb: 69, 151, 58; | ||||
| $primary-dark-mode-darken-10: darken($primary-dark-mode, 10%); | ||||
| $danger-dark-mode: #b71631; | ||||
| $danger-dark-mode-rgb: 183, 22, 49; | ||||
| $bg-dark-mode: #161618; | ||||
| $bg-dark-mode-rgb: 22, 22, 24; | ||||
| $bg-dark-mode-accent: #101216; | ||||
| $bg-dark-mode-alt: #242529; | ||||
| $bg-light-dark-mode: #1c1c1f; | ||||
| $bg-light-dark-mode-rgb: 28, 28, 31; | ||||
| $text-color-dark-mode: #abb2bf; | ||||
| $text-color-dark-mode-accent: lighten($text-color-dark-mode, 10%); | ||||
| $border-color-dark-mode: #47494f; | ||||
|  | ||||
| @mixin dark-mode { | ||||
|   --bs-primary: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 10%)); | ||||
|   --bs-danger: #{$danger-dark-mode}; | ||||
|   --bs-danger-rgb: #{$danger-dark-mode-rgb}; | ||||
|   --bs-body-bg: #{$bg-dark-mode}; | ||||
|   --bs-body-bg-rgb: #{$bg-dark-mode-rgb}; | ||||
|   --bs-body-color: #{$text-color-dark-mode}; | ||||
|   --bs-light: #{$bg-light-dark-mode}; | ||||
|   --bs-light-rgb: #{$bg-light-dark-mode-rgb}; | ||||
|   --bs-border-color: #{$border-color-dark-mode}; | ||||
|   --pngx-bg-darker: #{$bg-dark-mode-accent}; | ||||
|   --pngx-bg-alt: #{$bg-dark-mode-alt}; | ||||
|   --pngx-body-color-accent: #{$text-color-dark-mode-accent}; | ||||
|   --pngx-focus-alpha: 0.7; | ||||
|   --pngx-primary-faded: var(--pngx-primary-darken-15); | ||||
|   --pngx-primary-text-contrast: var(--bs-body-color); | ||||
|  | ||||
|   .navbar.bg-primary{ | ||||
|     --bs-primary: hsl(var(--pngx-primary),var(--pngx-primary-lightness)); | ||||
|     --bs-primary-rgb: var(--bs-primary); | ||||
|   } | ||||
|  | ||||
|   .navbar-brand { | ||||
|     color: var(--bs-body-color); | ||||
|   } | ||||
|  | ||||
|   .border { | ||||
|     border-color: var(--bs-border-color) !important; | ||||
|   } | ||||
|  | ||||
|   .border-end { | ||||
|     border-right: 1px solid var(--bs-border-color) !important; | ||||
|   } | ||||
|  | ||||
|   .border-start { | ||||
|     border-left: 1px solid var(--bs-border-color) !important; | ||||
|   } | ||||
|  | ||||
|   .border-bottom { | ||||
|     border-bottom: 1px solid var(--bs-border-color) !important; | ||||
|   } | ||||
|  | ||||
|   .text-dark, .text-light { | ||||
|     color: var(--bs-body-color) !important; | ||||
|   } | ||||
|  | ||||
|   .btn-outline-primary, .btn-primary { | ||||
|     &:hover, &:focus, &.active, &:active { | ||||
|       color: var(--bs-light) !important; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .btn-outline-secondary { | ||||
|     &:hover, &:focus, &.active, &:active { | ||||
|       background-color: var(--pngx-bg-darker); | ||||
|       color: var(--bs-primary); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .search-form-container { | ||||
|     input, input:focus { | ||||
|       color: var(--bs-body-color) !important; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .card { | ||||
|     background-color: var(--bs-body-bg); | ||||
|  | ||||
|     .card-header { | ||||
|       background-color: rgba(0, 0, 0, 0.12); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .modal-content, .modal-header, .modal-body, .modal-footer { | ||||
|     background-color: var(--bs-body-bg); | ||||
|     border-color: var(--bs-border-color); | ||||
|   } | ||||
|  | ||||
|   app-tag .badge { | ||||
|     filter: brightness(.8); | ||||
|   } | ||||
|  | ||||
|   .doc-img-container { | ||||
|     border: none !important; | ||||
|     border-top-left-radius: .25rem; | ||||
|     border-top-right-radius: .25rem; | ||||
|     overflow: hidden; | ||||
|   } | ||||
|  | ||||
|   .doc-img { | ||||
|     mix-blend-mode: normal; | ||||
|     border-radius: 0; | ||||
|     border-color: var(--bs-border-color); | ||||
|     filter: invert(10%); | ||||
|  | ||||
|     &.border-end { | ||||
|       border-right: none !important; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .doc-img.inverted { | ||||
|     filter: invert(95%) hue-rotate(180deg); | ||||
|   } | ||||
|  | ||||
|   .card-selected .doc-img { | ||||
|     mix-blend-mode: luminosity; | ||||
|   } | ||||
|  | ||||
|   .ng-dropdown-panel .ng-dropdown-panel-items .ng-option:hover, | ||||
|   .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-marked { | ||||
|     background-color: $bg-light-dark-mode; | ||||
|   } | ||||
|  | ||||
|   table { | ||||
|     .des, | ||||
|     .asc { | ||||
|       &::after { | ||||
|         filter: invert(0.8); /* arrow is a black inline png bkgd image (!) so use filter */ | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     &.table-hover > tbody > tr:hover > * { | ||||
|       background-color: $bg-light-dark-mode; | ||||
|       color: $text-color-dark-mode-accent; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .table-striped > tbody > tr:nth-of-type(odd) > * { | ||||
|     color: $text-color-dark-mode-accent; | ||||
|   } | ||||
|  | ||||
|   .close, .modal .btn-close, .alert .btn-close { | ||||
|     text-shadow: 0 1px 0 #666; | ||||
|   } | ||||
|  | ||||
|   .modal .btn-close, .alert .btn-close { | ||||
|     filter: invert(1) grayscale(100%) brightness(200%); | ||||
|   } | ||||
|  | ||||
|   .toast { | ||||
|     background-color: hsla(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 18%), 0.9); | ||||
|   } | ||||
|  | ||||
|   .toast-header { | ||||
|     background-color: hsla(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 10%), 0.9); | ||||
|   } | ||||
| } | ||||
|  | ||||
| body.color-scheme-dark { | ||||
|   @include dark-mode; | ||||
| } | ||||
| body.color-scheme-system { | ||||
|   @media (prefers-color-scheme: dark) { | ||||
|     @include dark-mode; | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Michael Shamoon
					Michael Shamoon