diff --git a/src/app/accounts/login.component.html b/src/app/accounts/login.component.html index 97d5c4783a..a130ff117f 100644 --- a/src/app/accounts/login.component.html +++ b/src/app/accounts/login.component.html @@ -1,7 +1,7 @@
- +

{{'loginOrCreateNewAccount' | i18n}}

diff --git a/src/app/services/services.module.ts b/src/app/services/services.module.ts index eb11b91d14..505c212cc2 100644 --- a/src/app/services/services.module.ts +++ b/src/app/services/services.module.ts @@ -167,11 +167,18 @@ export function initFactory(): Function { authService.init(); const htmlEl = window.document.documentElement; htmlEl.classList.add('locale_' + i18nService.translationLocale); - let theme = await storageService.get(ConstantsService.themeKey); + const theme = await storageService.get(ConstantsService.themeKey); if (theme == null) { - theme = 'light'; + htmlEl.classList.add('themeDefaultSet'); + } else { + htmlEl.classList.add(theme); + } + if (window.matchMedia('(prefers-color-scheme: dark)').matches && htmlEl.classList.contains('themeDefaultSet')) { + htmlEl.classList.add('themeDark'); + } + if (window.matchMedia('(prefers-color-scheme: light)').matches && htmlEl.classList.contains('themeDefaultSet')) { + htmlEl.classList.add('themeLight'); } - htmlEl.classList.add('theme_' + theme); stateService.save(ConstantsService.disableFaviconKey, await storageService.get(ConstantsService.disableFaviconKey)); stateService.save('enableGravatars', await storageService.get('enableGravatars')); diff --git a/src/app/settings/options.component.html b/src/app/settings/options.component.html index b369b90f6f..7fba5cf07a 100644 --- a/src/app/settings/options.component.html +++ b/src/app/settings/options.component.html @@ -87,6 +87,17 @@
{{'enableFullWidthDesc' | i18n}}
+
+
+
+ + + {{'themeDesc' | i18n}} +
+
+
diff --git a/src/app/settings/options.component.ts b/src/app/settings/options.component.ts index 3ad8be69a7..ca1cbfb1ca 100644 --- a/src/app/settings/options.component.ts +++ b/src/app/settings/options.component.ts @@ -26,9 +26,11 @@ export class OptionsComponent implements OnInit { disableIcons: boolean; enableGravatars: boolean; enableFullWidth: boolean; + theme: string; locale: string; vaultTimeouts: any[]; localeOptions: any[]; + themeOptions: any[]; private startingLocale: string; @@ -60,6 +62,11 @@ export class OptionsComponent implements OnInit { localeOptions.sort(Utils.getSortFunction(i18nService, 'name')); localeOptions.splice(0, 0, { name: i18nService.t('default'), value: null }); this.localeOptions = localeOptions; + this.themeOptions = [ + { name: i18nService.t('themeDefault'), value: 'themeDefaultSet' }, + { name: i18nService.t('themeLight'), value: 'themeLight' }, + { name: i18nService.t('themeDark'), value: 'themeDark' }, + ]; } async ngOnInit() { @@ -69,6 +76,7 @@ export class OptionsComponent implements OnInit { this.enableGravatars = await this.storageService.get('enableGravatars'); this.enableFullWidth = await this.storageService.get('enableFullWidth'); this.locale = this.startingLocale = await this.storageService.get(ConstantsService.localeKey); + this.theme = await this.storageService.get(ConstantsService.themeKey); } async submit() { @@ -80,6 +88,7 @@ export class OptionsComponent implements OnInit { await this.stateService.save('enableGravatars', this.enableGravatars); await this.storageService.save('enableFullWidth', this.enableFullWidth); this.messagingService.send('setFullWidth'); + await this.storageService.save('theme', this.theme); await this.storageService.save(ConstantsService.localeKey, this.locale); if (this.locale !== this.startingLocale) { window.location.reload(); @@ -101,4 +110,22 @@ export class OptionsComponent implements OnInit { } this.vaultTimeoutAction = newValue; } + + async themeChanged(themeUpdate: string) { + const theme = ['themeDefaultSet', 'themeDark', 'themeLight']; + const htmlEl = window.document.documentElement; + theme.forEach(element => { + htmlEl.classList.remove(element); + }); + if (themeUpdate === 'themeDefaultSet') { + if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + htmlEl.classList.add('themeDark', themeUpdate); + } + if (window.matchMedia('(prefers-color-scheme: light)').matches) { + htmlEl.classList.add('themeLight', themeUpdate); + } + } else { + htmlEl.classList.add(themeUpdate); + } + } } diff --git a/src/app/vault/add-edit.component.html b/src/app/vault/add-edit.component.html index d31b95feba..fb162ec01f 100644 --- a/src/app/vault/add-edit.component.html +++ b/src/app/vault/add-edit.component.html @@ -103,7 +103,7 @@
- diff --git a/src/images/loading-white.svg b/src/images/loading-white.svg new file mode 100644 index 0000000000..f7cf7935b0 --- /dev/null +++ b/src/images/loading-white.svg @@ -0,0 +1,6 @@ + + + Loading... + + \ No newline at end of file diff --git a/src/images/loading.svg b/src/images/loading.svg index 7076310516..0ca7925a24 100644 --- a/src/images/loading.svg +++ b/src/images/loading.svg @@ -1,6 +1,6 @@  - Loading... - + \ No newline at end of file diff --git a/src/images/logo-white@2x.png b/src/images/logo-white@2x.png new file mode 100644 index 0000000000..718a31bdee Binary files /dev/null and b/src/images/logo-white@2x.png differ diff --git a/src/images/totp-countdown.png b/src/images/totp-countdown.png index 87b9fe14ab..76aafabf75 100644 Binary files a/src/images/totp-countdown.png and b/src/images/totp-countdown.png differ diff --git a/src/index.html b/src/index.html index 3c663967f4..4d1673acf9 100644 --- a/src/index.html +++ b/src/index.html @@ -19,7 +19,7 @@
- +

diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 57147e0ae4..c0b8ed19f2 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -3969,6 +3969,24 @@ "removeSelectedUsersConfirmation": { "message": "Are you sure you want to remove the selected users?" }, + "usersHasBeenRemoved": { + "message": "The selected users have been removed." + }, + "theme": { + "message": "Theme" + }, + "themeDesc": { + "message": "Choose a theme for your web vault. This setting will preview the theme however it still requires you to save." + }, + "themeDefault": { + "message": "Default" + }, + "themeDark": { + "message": "Dark" + }, + "themeLight": { + "message": "Light" + }, "confirmSelected": { "message": "Confirm Selected" }, diff --git a/src/scss/styles.scss b/src/scss/styles.scss index 598fea83b3..5bfc2f3b4b 100644 --- a/src/scss/styles.scss +++ b/src/scss/styles.scss @@ -1,77 +1,5 @@ @import "../css/webfonts.css"; - -$primary: #175DDC; -$primary-accent: #1252A3; -$secondary: #ced4da; -$secondary-alt: #1A3B66; -$success: #00a65a; -$info: #555555; -$warning: #bf7e16; -$danger: #dd4b39; - -$theme-colors: ( - "primary-accent": $primary-accent, - "secondary-alt": $secondary-alt, -); - -$body-bg: #ffffff; -$body-color: #333333; - -$font-family-sans-serif: 'Open Sans','Helvetica Neue',Helvetica, - Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol'; - -$h1-font-size: 1.7rem; -$h2-font-size: 1.3rem; -$h3-font-size: 1rem; -$h4-font-size: 1rem; -$h5-font-size: 1rem; -$h6-font-size: 1rem; - -$small-font-size: 90%; -$font-size-lg: 1.15rem; -$code-font-size: 100%; - -$navbar-padding-y: .75rem; -$grid-gutter-width: 20px; -$card-spacer-y: .6rem; - -$list-group-item-padding-y: .6rem; -$list-group-active-color: $body-color; -$list-group-active-bg: #ffffff; -$list-group-active-border-color: rgba(#000000, .125); - -$dropdown-link-color: $body-color; -$dropdown-link-hover-bg: rgba(#000000, .06); -$dropdown-link-active-color: $dropdown-link-color; -$dropdown-link-active-bg: rgba(#000000, .1); -$dropdown-item-padding-x: 1rem; - -$navbar-brand-font-size: 35px; -$navbar-brand-height: 35px; -$navbar-brand-padding-y: 0; -$navbar-dark-color: rgba(#ffffff, .7); -$navbar-dark-hover-color: rgba(#ffffff, .9); -$navbar-nav-link-padding-x: 0.8rem; - -$input-bg: #fbfbfb; -$input-focus-bg: #ffffff; -$input-disabled-bg: #e0e0e0; -$input-placeholder-color: #b4b4b4; - -$table-accent-bg: rgba(#000000, .02); -$table-hover-bg: rgba(#000000, .03); - -$modal-backdrop-opacity: 0.3; -$btn-font-weight: 600; -$lead-font-weight: normal; - -$grid-breakpoints: ( - xs: 0, - sm: 1px, - md: 2px, - lg: 3px, - xl: 4px -); +@import "variables.scss"; //@import "~bootstrap/scss/bootstrap"; @import "~bootstrap/scss/_functions"; @@ -119,9 +47,9 @@ html { body { min-width: 1010px; - - &.layout_frontend { - background-color: #ecf0f5; + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); } &.full-width:not(.layout_frontend) { @@ -146,6 +74,15 @@ h1, h2, h3, h4, h5 { small { font-size: 80%; } + @include themify($themes) { + color: themed('textColor'); + } +} + +a { + @include themify($themes) { + color: themed('linkColor'); + } } input, select, textarea { @@ -154,6 +91,36 @@ input, select, textarea { } } +.text-body { + @include themify($themes) { + color: themed('textColor') !important; + } +} + +.bg-primary { + @include themify($themes) { + background-color: themed('bgPrimaryColor') !important; + } +} + +.bg-warning { + @include themify($themes) { + background-color: themed('warning') !important; + } +} + +.border-primary { + @include themify($themes) { + border-color: themed('borderPrimaryColor') !important; + } +} + +.border-warning { + @include themify($themes) { + border-color: themed('warning') !important; + } +} + .secondary-header, .spaced-header { margin-top: 4rem; } @@ -165,9 +132,22 @@ input, select, textarea { .dropdown-menu { min-width: 200px; max-width: 300px; + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); + } + + .dropdown-item { + @include themify($themes) { + color: themed('textColor'); + } + } .dropdown-item-text { line-height: 1.3; + @include themify($themes) { + color: themed('textColor'); + } span, small { display: block; @@ -212,14 +192,27 @@ input, select, textarea { } .list-group-item.active { - border-left: 3px solid theme-color("primary"); font-weight: bold; padding-left: calc(#{$list-group-item-padding-x} - 3px); + border-color: rgba(0,0,0,0.125); + @include themify($themes) { + border-left: 3px solid themed('borderPrimaryColor'); + } +} + +.text-muted { + @include themify($themes) { + color: themed('textMuted') !important; + } } .card-header, .modal-header { font-weight: bold; text-transform: uppercase; + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); + } small { font-weight: normal; @@ -279,10 +272,16 @@ input, select, textarea { } .modal-body { + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); + } h3, .section-header > * { font-weight: normal; text-transform: uppercase; - color: $text-muted; + @include themify($themes) { + color: themed('textMuted'); + } } } .modal .list-group-flush { @@ -296,8 +295,11 @@ input, select, textarea { .modal-footer { justify-content: flex-start; - background-color: $input-bg; @include border-radius($modal-content-border-radius); + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); + } } label:not(.form-check-label):not(.btn), label.bold { @@ -308,10 +310,34 @@ input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: searchfield-cancel-button; } +.btn-primary { + @include themify($themes) { + border-color: themed('buttonBorderColor'); + background-color: themed('buttonBackgroundColor'); + } + &:hover { + @include themify($themes) { + border-color: themed('buttonBorderColorHover'); + background-color: themed('buttonBackgroundColorHover'); + } + color: #FFFFFF; + } +} + .btn[class*="btn-outline-"] { - &:not(:hover) { - border-color: $secondary; - background-color: #fbfbfb; + &:not(:hover):not(.btn-outline-danger):not(.dropdown-toggle) { + @include themify($themes) { + border-color: themed('buttonBorderColor'); + background-color: themed('backgroundColor'); + color: themed('buttonTextColor'); + } + } + &:hover:not(.btn-outline-danger):not(.dropdown-toggle) { + @include themify($themes) { + border-color: themed('buttonBorderColorHover'); + background-color: themed('buttonBackgroundColorHover'); + color: #fff; + } } } @@ -323,10 +349,21 @@ input[type="search"]::-webkit-search-cancel-button { outline-style: auto; outline-width: 1px; } + &:not(.text-danger):not(.cursor-move) { + @include themify($themes) { + border-color: themed('buttonBorderColor'); + color: themed('buttonBackgroundColor'); + } + } + @include themify($themes) { + color: themed('buttonBackgroundColor'); + } } .btn-outline-secondary { - color: $text-muted; + @include themify($themes) { + color: themed('textMuted'); + } &:hover:not(:disabled) { color: $body-color; @@ -372,6 +409,10 @@ input[type="search"]::-webkit-search-cancel-button { &.focus { z-index: 100; } + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); + } } .fa-icon-above-input { @@ -379,6 +420,10 @@ input[type="search"]::-webkit-search-cancel-button { } .table.table-list { + @include themify($themes) { + color: themed('textColor'); + } + thead th { border-top: none; } @@ -400,7 +445,9 @@ input[type="search"]::-webkit-search-cancel-button { } small, > .fa, .icon { - color: $text-muted; + @include themify($themes) { + color: themed('textMuted'); + } } } @@ -453,11 +500,20 @@ input[type="search"]::-webkit-search-cancel-button { } td.table-list-strike { - color: $text-muted; + @include themify($themes) { + color: themed('textMuted'); + } text-decoration: line-through; } } +.table-hover tbody tr:hover { + @include themify($themes) { + color: themed('tableHover'); + background-color: rgba(0, 0, 0, 0.03) + } +} + .text-lg { font-size: $font-size-lg; } @@ -481,11 +537,59 @@ input[type="search"]::-webkit-search-cancel-button { } .password-number { - color: #007fde; + @include themify($themes) { + color: themed('pwNumber'); + } } .password-special { - color: #c40800; + @include themify($themes) { + color: themed('pwSpecial'); + } +} + +.card { + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); + border-color: themed('borderColor'); + } +} + +.card-body { + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); + } +} + +.badge[class*="badge-"] { + @include themify($themes) { + color: #FFFFFF; + background-color: themed('buttonBackgroundColor'); + } +} + +[class*="swal2-"] { + &:not(.swal2-container) { + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); + } + } +} + +.close { + @include themify($themes) { + color: themed('textColor'); + } +} + +.dropdown-menu, .dropdown-item { + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); + } } app-vault-groupings, app-org-vault-groupings, .groupings { @@ -497,7 +601,9 @@ app-vault-groupings, app-org-vault-groupings, .groupings { h3 { font-weight: normal; text-transform: uppercase; - color: $text-muted; + @include themify($themes) { + color: themed('textMuted'); + } } ul:last-child { @@ -505,11 +611,16 @@ app-vault-groupings, app-org-vault-groupings, .groupings { } .card-body a { - color: $body-color; + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); + } &:hover { &.text-muted { - color: $body-color !important; + @include themify($themes) { + color: themed('iconHover') !important; + } } } } @@ -533,11 +644,15 @@ app-vault-groupings, app-org-vault-groupings, .groupings { li.active { > a:first-of-type, > div a:first-of-type { font-weight: bold; - color: theme-color("primary"); + @include themify($themes) { + color: themed('linkColor'); + } } > .fa, > div > .fa { - color: theme-color("primary"); + @include themify($themes) { + color: themed('linkColor'); + } } } } @@ -605,7 +720,9 @@ app-user-billing { } #web-authn-frame { - background: url('../images/loading.svg') 0 0 no-repeat; + @include themify($themes) { + background: themed('imgLoading') 0 0 no-repeat; + } height: 290px; iframe { @@ -616,8 +733,11 @@ app-user-billing { } #bt-dropin-container { - background: url('../images/loading.svg') 0 0 no-repeat; - min-height: 50px; +/* @include themify($themes) { + background: themed('imgLoading') 0 0 no-repeat; + } + */min-height: 50px; + background: url('../images/loading-white.svg') 0 0 no-repeat; } .braintree-placeholder, .braintree-sheet__header { @@ -638,6 +758,18 @@ app-user-billing { border: none; } +[data-braintree-id="upper-container"]::before { + @include themify($themes) { + background-color: themed('backgroundColor'); + } +} + +#totpImage { + @include themify($themes) { + filter: themed('imgFilter'); + } +} + .totp { .totp-code { @extend .text-monospace; @@ -702,7 +834,10 @@ app-user-billing { border: 1px solid $card-border-color; border-left-width: 5px; border-radius: $card-inner-border-radius; - background-color: #fafafa; + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); + } .callout-heading { margin-top: 0; @@ -766,7 +901,9 @@ app-user-billing { > small { display: block; - color: $text-muted; + @include themify($themes) { + color: themed('textMuted'); + } font-weight: normal; } @@ -790,6 +927,18 @@ app-user-billing { } } +.form-control { + @include themify($themes) { + color: themed('textColor'); + background-color: themed('inputBackgroundColor'); + border-color: themed('inputBorderColor'); + } +} + +input[type="radio"], input[type="checkbox"] { + cursor: pointer; +} + .form-control.stripe-form-control { padding-top: 0.55rem; @@ -811,8 +960,11 @@ app-user-billing { } .org-nav { - background-color: $input-bg; - border-bottom: 1px solid $border-color; + @include themify($themes) { + color: themed('textColor'); + background-color: themed('backgroundColor'); + border-bottom: 1px solid themed('borderColor'); + } height: 100px; min-height: 100px; @@ -824,14 +976,18 @@ app-user-billing { border-bottom: none; a { - color: $body-color; + @include themify($themes) { + color: themed('textColor'); + } &:not(.active) { border-color: transparent; } &.active { - border-top: 3px solid theme-color("primary"); + @include themify($themes) { + border-top: 3px solid themed('primary'); + } font-weight: bold; padding-top: calc(#{$nav-link-padding-y} - 2px); } @@ -850,12 +1006,24 @@ app-user-billing { } } } +.nav-tabs .nav-link.active { + @include themify($themes) { + background: themed('navActiveBackground'); + border-color: themed('borderColor'); + } +} -img.logo { +img.logo, .logo#loginLogo { width: 284px; height: 43px; margin: 0 auto; display: block; + @include themify($themes) { + background: themed('loginLogo') no-repeat center center; + background-size: cover; + } + background: url("../images/logo-dark@2x.png") no-repeat center center; + background-size: cover; } .min-height-fix { @@ -869,12 +1037,15 @@ img.logo { .cdk-drag-preview { z-index: $zindex-tooltip !important; opacity: 0.8; - background-color: $white; + /*background-color: $white;*/ border-radius: $border-radius; + @include themify($themes) { + background: themed('cdkDraggingBackground'); + } } .cursor-move { cursor: move !important; } -@import "./register-layout"; +@import "./register-layout"; \ No newline at end of file diff --git a/src/scss/variables.scss b/src/scss/variables.scss new file mode 100644 index 0000000000..8084633f07 --- /dev/null +++ b/src/scss/variables.scss @@ -0,0 +1,150 @@ +$primary: #175DDC; +$primary-accent: #1252A3; +$secondary: #ced4da; +$secondary-alt: #1A3B66; +$success: #00a65a; +$info: #555555; +$warning: #bf7e16; +$danger: #dd4b39; + +$theme-colors: ( + "primary-accent": $primary-accent, + "secondary-alt": $secondary-alt, +); + +$body-bg: #ffffff; +$body-color: #333333; + +$font-family-sans-serif: 'Open Sans','Helvetica Neue',Helvetica, + Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol'; + +$h1-font-size: 1.7rem; +$h2-font-size: 1.3rem; +$h3-font-size: 1rem; +$h4-font-size: 1rem; +$h5-font-size: 1rem; +$h6-font-size: 1rem; + +$small-font-size: 90%; +$font-size-lg: 1.15rem; +$code-font-size: 100%; + +$navbar-padding-y: .75rem; +$grid-gutter-width: 20px; +$card-spacer-y: .6rem; + +$list-group-item-padding-y: .6rem; +$list-group-active-color: $body-color; +$list-group-active-bg: #ffffff; +$list-group-active-border-color: rgba(#000000, .125); + +$dropdown-link-color: $body-color; +$dropdown-link-hover-bg: rgba(#000000, .06); +$dropdown-link-active-color: $dropdown-link-color; +$dropdown-link-active-bg: rgba(#000000, .1); +$dropdown-item-padding-x: 1rem; + +$navbar-brand-font-size: 35px; +$navbar-brand-height: 35px; +$navbar-brand-padding-y: 0; +$navbar-dark-color: rgba(#ffffff, .7); +$navbar-dark-hover-color: rgba(#ffffff, .9); +$navbar-nav-link-padding-x: 0.8rem; + +$input-bg: #fbfbfb; +$input-focus-bg: #ffffff; +$input-disabled-bg: #e0e0e0; +$input-placeholder-color: #b4b4b4; + +$table-accent-bg: rgba(#000000, .02); +$table-hover-bg: rgba(#000000, .03); + +$modal-backdrop-opacity: 0.3; +$btn-font-weight: 600; +$lead-font-weight: normal; + +$grid-breakpoints: ( + xs: 0, + sm: 1px, + md: 2px, + lg: 3px, + xl: 4px +); + +$text-color: #333333; +$border-color: #ced4da; + +$themes: ( + Light: ( + primary: $primary, + textColor: $text-color, + textMuted: #6c757d, + linkColor: $primary, + iconHover: $body-color, + borderColor: $border-color, + backgroundColor: $body-bg, + inputBackgroundColor: #fbfbfb, + inputBorderColor: $border-color, + bgPrimaryColor: $primary, + borderPrimaryColor: $primary, + buttonBorderColor: $secondary, + buttonBackgroundColor: $primary, + buttonBackgroundColorHover: $primary, + buttonBorderColorHover: $primary, + buttonTextColor: $primary, + warning: $warning, + loginLogo: url("../images/logo-dark@2x.png"), + totpFilter: invert(0) grayscale(0), + imgLoading: url('../images/loading.svg'), + cdkDraggingBackground: #FFFFFF, + tableHover: #333333, + navActiveBackground: #FFFFFF, + pwNumber: #007fde, + pwSpecial: #c40800 + ), + Dark: ( + primary: $secondary-alt, + textColor: #fbfbfb, + textMuted: #C1C4C8, + linkColor: #46ace7, + iconHover: #555555, + borderColor: #111111, + backgroundColor: #222222, + inputBackgroundColor: #1A1A1A, + inputBorderColor: #111111, + bgPrimaryColor: $secondary-alt, + borderPrimaryColor: $secondary-alt, + buttonBorderColor: $secondary-alt, + buttonBackgroundColor: $secondary-alt, + buttonBackgroundColorHover: $primary-accent, + buttonBorderColorHover: $secondary-alt, + buttonTextColor: $secondary, + warning: $warning, + loginLogo: url("../images/logo-white@2x.png"), + imgFilter: invert(1) grayscale(1), + imgLoading: url('../images/loading-white.svg'), + cdkDraggingBackground: #000000, + tableHover: $secondary, + navActiveBackground: #1A1A1A, + pwNumber: #51b5ff, + pwSpecial: #ff8c87 + ), +); + +@mixin themify($themes: $themes) { + @each $theme, $map in $themes { + html.theme#{$theme} & { + $theme-map: () !global; + @each $key, $submap in $map { + $value: map-get(map-get($themes, $theme), '#{$key}'); + $theme-map: map-merge($theme-map, ($key: $value)) !global; + } + @content; + $theme-map: null !global; + } + } +} + +@function themed($key) { + @return map-get($theme-map, $key); +} \ No newline at end of file diff --git a/src/services/htmlStorage.service.ts b/src/services/htmlStorage.service.ts index 2af52b9e2f..771c917f88 100644 --- a/src/services/htmlStorage.service.ts +++ b/src/services/htmlStorage.service.ts @@ -5,7 +5,7 @@ import { ConstantsService } from 'jslib/services'; export class HtmlStorageService implements StorageService { private localStorageKeys = new Set(['appId', 'anonymousAppId', 'rememberedEmail', 'passwordGenerationOptions', ConstantsService.disableFaviconKey, 'rememberEmail', 'enableGravatars', 'enableFullWidth', - ConstantsService.localeKey, ConstantsService.autoConfirmFingerprints, + ConstantsService.themeKey, ConstantsService.localeKey, ConstantsService.autoConfirmFingerprints, ConstantsService.vaultTimeoutKey, ConstantsService.vaultTimeoutActionKey, ConstantsService.ssoCodeVerifierKey, ConstantsService.ssoStateKey, 'ssoOrgIdentifier']); private localStorageStartsWithKeys = ['twoFactorToken_', ConstantsService.collapsedGroupingsKey + '_']; @@ -26,6 +26,12 @@ export class HtmlStorageService implements StorageService { if (vaultTimeoutAction == null) { await this.save(ConstantsService.vaultTimeoutActionKey, 'lock'); } + + // Default theme to match the browser if the theme isn't set + const theme = await this.get(ConstantsService.themeKey); + if (theme == null) { + await this.save(ConstantsService.themeKey, 'themeDefaultSet'); + } } get(key: string): Promise { diff --git a/webpack.config.js b/webpack.config.js index 9aad213860..ae959b375a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -28,7 +28,7 @@ const moduleRules = [ }, { test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/, - exclude: /loading.svg/, + exclude: /loading(|-white).svg/, use: [{ loader: 'file-loader', options: {