$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%; } :root { @include paperless-green; --pngx-primary-text-contrast: var(--bs-light); --bs-primary: hsl(var(--pngx-primary), var(--pngx-primary-lightness)); --bs-border-color: var(--bs-gray-400); --pngx-primary-faded: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 76%)); --pngx-primary-lighten-10: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 10%)); --pngx-primary-lighten-30: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 30%)); --pngx-primary-darken-5: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 5%)); --pngx-primary-darken-15: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 15%)); --pngx-primary-darken-18: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 18%)); --pngx-primary-darken-27: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 27%)); --pngx-success-darken-10: hsl(152, 69%, 11%); // based on success #198754 --pngx-bg-alt: #fff; --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,"); $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,"); .primary-light { --pngx-primary-text-contrast: #{$text-color-light-bg} !important; .form-check:not(.form-switch) { .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); } } .toast .btn-close { filter: none !important; } } .primary-dark { --pngx-primary-text-contrast: #{$text-color-dark-bg} !important; } .text-primary-contrast { color: var(--pngx-primary-text-contrast); } // Dark mode @mixin paperless-green-dark-mode { --pngx-primary-lightness: 31%; --bs-primary: hsl(var(--pngx-primary),var(--pngx-primary-lightness)); } @mixin dark-mode { --bs-body-color: #{$text-color-dark-bg}; --pngx-body-color-accent: #{$text-color-dark-bg-accent}; --bs-secondary-color: #6c757d; --bs-danger: #b71631; --bs-danger-rgb: 183, 22, 49; --bs-body-bg: #161618; --bs-body-bg-rgb: 22, 22, 24; --bs-light: #1c1c1f; --bs-light-rgb: 28, 28, 31; --bs-border-color: #47494f; --pngx-bg-darker: #101216; --pngx-bg-alt: #242529; --pngx-focus-alpha: 0.6; --pngx-primary-faded: var(--pngx-primary-darken-15); --pngx-primary-text-contrast: var(--bs-body-color); --bs-dark-border-subtle: var(--pngx-bg-darker); --bs-border-color-translucent: rgba(0, 0, 0, .175); // override bs .text-dark, .text-light { color: var(--bs-body-color) !important; } .btn { --bs-btn-disabled-opacity: 0.35; } .btn.btn-link { --bs-btn-disabled-opacity: 0.85; } .btn-primary { &:hover, &:focus, &.active, &:active { color: var(--bs-body-color) !important; } } &.primary-light .btn-primary { &:hover, &:focus, &.active, &:active { color: var(--pngx-bg-darker) !important; } } .btn-dark { --bs-btn-color: $text-color-dark-bg; --bs-btn-bg: var(--pngx-bg-alt); --bs-btn-border-color: var(--pngx-bg-alt); --bs-btn-hover-bg: var(--bs-light); --bs-btn-hover-border-color: var(--pngx-bg-darker); --bs-btn-active-bg: var(--pngx-bg-alt); } .btn-outline-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); } } .btn-light { color: var(--bs-body-color); } .btn .progress { background-color: var(--pngx-body-color-accent); } .search-form-container { input, input:focus { color: var(--bs-body-color) !important; } } .card { background-color: var(--bs-body-bg); --bs-card-cap-bg: var(--bs-body-bg-rgb); .card-header { background-color: rgba(0, 0, 0, 0.12); } } .row-cols-paperless-cards .card { --bs-border-color-translucent: rgba(0, 0, 0, .3); } .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); } .badge.bg-light.border { border-color: rgba(0,0,0,0) !important; } .document-card .card-body.bg-light { background-color: var(--bs-body-bg); } .doc-img { mix-blend-mode: normal !important; 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: var(--bs-light); } .ng-select-multiple .ng-select-container .ng-value-container .ng-value { background-color: var(--pngx-bg-alt); color: var(--bs-body-color); .ng-value-icon.left { border-color: var(--pngx-bg-alt); &:hover { background-color: var(--pngx-primary-lighten-30); } } } 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: var(--bs-light); color: var(--pngx-body-color-accent); } } .card .table { border-color: var(--bs-gray-800); } .alert-secondary { background-color: var(--bs-light); border-color: var(--pngx-bg-darker); color: var(--bs-body-color); } .alert-primary { --bs-alert-color: var(--bs-primary); --bs-alert-bg: var(--pngx-primary-darken-18); --bs-alert-border-color: var(--pngx-bg-darker); } .alert-success { --bs-alert-color: var(--pngx-body-color-accent); --bs-alert-bg: var(--pngx-success-darken-10); --bs-alert-border-color: var(--pngx-bg-darker); } .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, .toast .btn-close { filter: invert(1) grayscale(100%) brightness(200%); } .toast { background-color: hsla(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 15%), 0.9); } .toast, .toast .btn, .toast .btn-close { color: var(--pngx-primary-text-contrast); } .toast .btn-outline-dark:hover { color: var(--bs-body-color) } .dropdown-menu { --bs-dropdown-color: var(--bs-body-color); } .list-group { --bs-list-group-action-hover-color: var(--bs-secondary-color); } .card .list-group-item { --bs-border-color: rgb(var(--bs-dark-rgb)); .bg-secondary { background-color: rgb(var(--bs-dark-rgb)) !important; } } .tooltip { --bs-tooltip-bg: #000; } } @include color-mode(dark) { body:not(.primary-light):not(.primary-dark) { @include paperless-green-dark-mode; .navbar.bg-primary { // navbar is og green in dark mode @include paperless-green; } } @include dark-mode; } // Temp to not blink with white before angular loads @include color-mode(auto) { @media (prefers-color-scheme: dark) { body { // no custom theme color &:not(.primary-light):not(.primary-dark) { @include paperless-green-dark-mode; .navbar.bg-primary { // navbar is og green in dark mode @include paperless-green; } } @include dark-mode; } } }