-

+
diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json
index e87893e1..2b4a9ae4 100644
--- a/src/locales/en/messages.json
+++ b/src/locales/en/messages.json
@@ -4094,6 +4094,21 @@
"removeUsersWarning": {
"message": "Are you sure you want to remove the following users? The process may take a few seconds to complete and cannot be interrupted or canceled."
},
+ "theme": {
+ "message": "Theme"
+ },
+ "themeDesc": {
+ "message": "Choose a theme for your web vault."
+ },
+ "themeSystem": {
+ "message": "Use System Theme"
+ },
+ "themeDark": {
+ "message": "Dark"
+ },
+ "themeLight": {
+ "message": "Light"
+ },
"confirmSelected": {
"message": "Confirm Selected"
},
diff --git a/src/scss/base.scss b/src/scss/base.scss
new file mode 100644
index 00000000..b4ba0a16
--- /dev/null
+++ b/src/scss/base.scss
@@ -0,0 +1,246 @@
+html {
+ font-size: 14px;
+}
+
+body {
+ min-width: 1010px;
+
+ &.layout_frontend {
+ @media (prefers-color-scheme: dark) {
+ background-color: $darkDarkBlue2;
+ }
+ @media (prefers-color-scheme: light) {
+ background-color: $white;
+ }
+ @include themify($themes) {
+ background-color: themed('layoutFrontendColor');
+ color: themed('headingColor');
+ }
+ }
+
+ @include themify($themes) {
+ background-color: themed('backgroundColor');
+ color: themed('textColor');
+ }
+
+ &.full-width:not(.layout_frontend) {
+ .container {
+ min-width: 980px;
+ width: 90%;
+ }
+ }
+}
+
+.container {
+ margin: 0 auto;
+ max-width: none !important;
+ padding: 0;
+ width: 980px;
+}
+
+.page-header, .secondary-header {
+ margin-bottom: 0.5rem;
+ padding-bottom: 0.6rem;
+ @include themify($themes) {
+ border-bottom: 1px solid themed('separator');
+ }
+
+ &:not(.text-danger) {
+ h1, h2, h3, h4 {
+ margin: 0;
+ @include themify($themes) {
+ color: themed('headingColor');
+ }
+ }
+ }
+}
+
+.secondary-header, .spaced-header {
+ margin-top: 4rem;
+}
+
+img.logo {
+ display: block;
+ height: 43px;
+ margin: 0 auto;
+ width: 284px;
+ @include themify($themes) {
+ content: url('../images/logo-' + themed('logoSuffix') + '@2x.png');
+ }
+}
+
+.page-content {
+ margin-top: 20px;
+}
+
+.footer {
+ margin-top: 40px;
+ padding: 40px 0 40px 0;
+ @include themify($themes) {
+ border-top: 1px solid themed('separator');
+ }
+}
+
+hr, .dropdown-divider {
+ @include themify($themes) {
+ border-top: 1px solid themed('separatorHr');
+ }
+}
+
+.min-height-fix {
+ min-height: 1px;
+}
+
+.overflow-hidden {
+ overflow: hidden;
+}
+
+.cursor-move {
+ cursor: move !important;
+}
+
+
+h1, h2, h3, h4, h5 {
+ @include themify($themes) {
+ color: themed('headingColor');
+ }
+
+ small {
+ font-size: 80%;
+ }
+
+ &.spaced-header {
+ @include themify($themes) {
+ color: themed('headingColor');
+ }
+ }
+}
+
+a {
+ @include themify($themes) {
+ color: themed('linkColor');
+ }
+
+ &.text-body {
+ @include themify($themes) {
+ color: themed('headingColor') !important;
+ font-weight: themed('linkWeight');
+ }
+ }
+}
+
+a:hover {
+ @include themify($themes) {
+ color: themed('linkColorHover');
+ }
+}
+
+code {
+ @include themify($themes) {
+ color: themed('codeColor');
+ }
+}
+
+.fa-icon-above-input {
+ height: 1.5em;
+}
+
+.text-lg {
+ font-size: $font-size-lg;
+}
+
+.text-strike {
+ text-decoration: line-through;
+}
+
+.font-weight-semibold {
+ font-weight: 600;
+}
+
+.btn:focus, .swal2-popup .swal2-actions button:focus, .btn.focus, .swal2-popup .swal2-actions button.focus, .form-control:focus {
+ @include themify($themes) {
+ box-shadow: 0 0 0 0.2rem themed('focus');
+ }
+}
+
+/* Override Bootstrap theming */
+
+.bg-primary {
+ @include themify($themes) {
+ background-color: themed('bgPrimaryColor');
+ }
+}
+
+.bg-light {
+ @include themify($themes) {
+ background-color: themed('bgLightColor') !important;
+ }
+}
+
+.border-primary {
+ @include themify($themes) {
+ border-color: themed('borderPrimaryColor') !important;
+ }
+}
+
+.border-warning {
+ @include themify($themes) {
+ border-color: themed('warning') !important;
+ }
+}
+
+.border-danger {
+ @include themify($themes) {
+ border-color: themed('danger') !important;
+ }
+}
+
+.border-info {
+ @include themify($themes) {
+ border-color: themed('info') !important;
+ }
+}
+
+.text-success {
+ @include themify($themes) {
+ color: themed('success') !important;
+ }
+
+ & > h1,h2,h3,h4 {
+ @include themify($themes) {
+ color: themed('success') !important;
+ }
+ }
+}
+
+.text-warning {
+ @include themify($themes) {
+ color: themed('warning') !important;
+ }
+
+ & > h1,h2,h3,h4 {
+ @include themify($themes) {
+ color: themed('warning') !important;
+ }
+ }
+}
+
+.text-danger {
+ &:not(.dropdown-item) {
+ @include themify($themes) {
+ color: themed('danger') !important;
+ }
+
+ & > h1,h2,h3,h4 {
+ @include themify($themes) {
+ color: themed('danger') !important;
+ }
+ }
+ }
+}
+
+.text-muted {
+ @include themify($themes) {
+ color: themed('textMuted') !important;
+ }
+}
diff --git a/src/scss/buttons.scss b/src/scss/buttons.scss
new file mode 100644
index 00000000..74ab5771
--- /dev/null
+++ b/src/scss/buttons.scss
@@ -0,0 +1,224 @@
+.btn-primary, .swal2-confirm {
+ @include themify($themes) {
+ background-color: themed('btnPrimary');
+ border-color: themed('btnPrimary');
+ color: themed('btnPrimaryText');
+ }
+
+ &:hover:not(:disabled), &:active:not(:disabled) {
+ @include themify($themes) {
+ background-color: themed('btnPrimaryHover');
+ border-color: themed('btnPrimaryBorderHover');
+ color: themed('btnPrimaryText');
+ }
+ }
+
+ &:disabled {
+ opacity: 0.65;
+ }
+}
+
+.btn-outline-primary {
+ @include themify($themes) {
+ background-color: themed('btnOutlinePrimaryBackground');
+ border-color: themed('btnOutlinePrimaryBorder');
+ color: themed('btnOutlinePrimaryText');
+ }
+
+ &:hover:not(:disabled), &:active {
+ @include themify($themes) {
+ background-color: themed('btnOutlinePrimaryBackgroundHover');
+ border-color: themed('btnOutlinePrimaryBorderHover');
+ color: themed('btnOutlinePrimaryTextHover');
+ }
+ }
+}
+
+.btn-secondary, .swal2-cancel {
+ @include themify($themes) {
+ background-color: themed('btnSecondary');
+ border-color: themed('btnSecondaryBorder');
+ color: themed('btnSecondaryText');
+ }
+
+ &:hover:not(:disabled), &:active:not(:disabled) {
+ @include themify($themes) {
+ background-color: themed('btnSecondaryHover');
+ border-color: themed('btnSecondaryBorderHover');
+ color: themed('btnSecondaryTextHover');
+ }
+ }
+
+ &:disabled {
+ opacity: 0.65;
+ }
+
+ &:focus,
+ &.focus {
+ @include themify($themes) {
+ box-shadow: 0 0 0 $btn-focus-width rgba(mix(color-yiq(themed('primary')), themed('primary'), 15%), .5);
+ }
+ }
+}
+
+.btn-outline-secondary {
+ @include themify($themes) {
+ background-color: themed('btnOutlineSecondaryBackground');
+ border-color: themed('btnOutlineSecondaryBorder');
+ color: themed('btnOutlineSecondaryText');
+ }
+
+ &:hover:not(:disabled), &:active {
+ @include themify($themes) {
+ background-color: themed('btnOutlineSecondaryBackgroundHover');
+ border-color: themed('btnOutlineSecondaryBorderHover');
+ color: themed('btnOutlineSecondaryTextHover');
+ }
+ }
+}
+
+.show > .btn-outline-secondary {
+ &.dropdown-toggle, &:focus {
+ @include themify($themes) {
+ background-color: themed('btnOutlineSecondaryBackground');
+ border-color: themed('btnOutlineSecondaryBorder');
+ color: themed('btnOutlineSecondaryText');
+ }
+ }
+
+ &:hover {
+ @include themify($themes) {
+ background-color: themed('btnOutlineSecondaryBackgroundHover');
+ border-color: themed('btnOutlineSecondaryBorderHover');
+ color: themed('btnOutlineSecondaryTextHover');
+ }
+ }
+}
+
+.btn-danger, .swal2-deny {
+ @include themify($themes) {
+ background-color: themed('btnDanger');
+ border-color: themed('btnDanger');
+ color: themed('btnDangerText');
+ }
+
+ &:hover:not(:disabled), &:active:not(:disabled) {
+ @include themify($themes) {
+ background-color: themed('btnDangerHover');
+ border-color: themed('btnDangerHover');
+ color: themed('btnDangerText');
+ }
+ }
+}
+
+.btn-outline-danger {
+ @include themify($themes) {
+ background-color: themed('btnOutlineDangerBackground');
+ border-color: themed('btnOutlineDangerBorder');
+ color: themed('btnOutlineDangerText');
+ }
+
+ &:hover:not(:disabled), &:active {
+ @include themify($themes) {
+ background-color: themed('btnOutlineDangerBackgroundHover');
+ border-color: themed('btnOutlineDangerBorderHover');
+ color: themed('btnOutlineDangerTextHover');
+ }
+ }
+}
+
+.btn-link {
+ &:focus,
+ &.focus {
+ outline-color: -webkit-focus-ring-color;
+ outline-offset: 1px;
+ outline-style: auto;
+ outline-width: 1px;
+ }
+
+ &:not(.text-danger):not(.cursor-move) {
+ @include themify($themes) {
+ color: themed('btnLinkText');
+ }
+ }
+
+ &:hover:not(.text-danger):not(.cursor-move) {
+ @include themify($themes) {
+ color: themed('btnLinkTextHover');
+ }
+ }
+}
+
+.btn-submit {
+ position: relative;
+
+ .fa-spinner {
+ align-items: center;
+ display: none;
+ justify-content: center;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ top: 0;
+ }
+
+ &:disabled:not(.manual), &.loading {
+ .fa-spinner {
+ display: flex;
+ }
+
+ span {
+ visibility: hidden;
+ }
+ }
+}
+
+.badge-primary {
+ @include themify($themes) {
+ background-color: themed('badgePrimaryBackground');
+ color: themed('badgePrimaryText');
+ }
+
+ &:hover {
+ @include themify($themes) {
+ background-color: themed('badgePrimaryBackgroundHover');
+ color: themed('badgePrimaryText');
+ }
+ }
+}
+
+.badge-secondary {
+ @include themify($themes) {
+ background-color: themed('badgeSecondaryBackground');
+ color: themed('badgeSecondaryText');
+ }
+}
+
+.badge-info {
+ @include themify($themes) {
+ background-color: themed('badgeInfoBackground');
+ color: themed('badgeInfoText');
+ }
+}
+
+.badge-danger {
+ @include themify($themes) {
+ background-color: themed('badgeDangerBackground');
+ color: themed('badgeDangerText');
+ }
+}
+
+.badge-warning {
+ @include themify($themes) {
+ background-color: themed('warning');
+ color: themed('warningTextColor');
+ }
+}
+
+.badge-success {
+ @include themify($themes) {
+ background-color: themed('success');
+ color: themed('successTextColor');
+ }
+}
diff --git a/src/scss/callouts.scss b/src/scss/callouts.scss
new file mode 100644
index 00000000..43763dba
--- /dev/null
+++ b/src/scss/callouts.scss
@@ -0,0 +1,83 @@
+.callout {
+ border-left-width: 5px !important;
+ border-radius: $card-inner-border-radius;
+ margin-bottom: $alert-margin-bottom;
+ padding: $alert-padding-y $alert-padding-x;
+ @include themify($themes) {
+ background-color: themed('calloutBackground');
+ border: 1px solid themed('borderColor');
+ color: themed('calloutColor');
+ }
+
+ .callout-heading {
+ margin-top: 0;
+ }
+
+ h3.callout-heading {
+ font-weight: bold;
+ text-transform: uppercase;
+ }
+
+ &.callout-primary {
+ @include themify($themes) {
+ border-left-color: themed('primary');
+ }
+ .callout-heading {
+ @include themify($themes) {
+ color: themed('primary');
+ }
+ }
+ }
+
+ &.callout-info {
+ @include themify($themes) {
+ border-left-color: themed('info');
+ }
+
+ .callout-heading {
+ @include themify($themes) {
+ color: themed('info');
+ }
+ }
+ }
+
+ &.callout-danger {
+ @include themify($themes) {
+ border-left-color: themed('danger');
+ }
+
+ .callout-heading {
+ @include themify($themes) {
+ color: themed('danger');
+ }
+ }
+ }
+
+ &.callout-success {
+ @include themify($themes) {
+ border-left-color: themed('success');
+ }
+
+ .callout-heading {
+ @include themify($themes) {
+ color: themed('success');
+ }
+ }
+ }
+
+ &.callout-warning {
+ @include themify($themes) {
+ border-left-color: themed('warning');
+ }
+
+ .callout-heading {
+ @include themify($themes) {
+ color: themed('warning');
+ }
+ }
+ }
+
+ .enforced-policy-options ul {
+ margin-bottom: 0px;
+ }
+}
diff --git a/src/scss/cards.scss b/src/scss/cards.scss
new file mode 100644
index 00000000..b1f514bd
--- /dev/null
+++ b/src/scss/cards.scss
@@ -0,0 +1,93 @@
+.card {
+ @include themify($themes) {
+ background-color: themed('foregroundColor');
+ border-color: themed('borderColor');
+ color: themed('textColor');
+ }
+
+ &.text-danger {
+ &.text-danger > .card-body {
+ @include themify($themes) {
+ color: themed('danger');
+ }
+ }
+ }
+}
+
+.card-header, .modal-header {
+ font-weight: bold;
+ text-transform: uppercase;
+
+ small {
+ font-weight: normal;
+ text-transform: none;
+ @extend .text-muted;
+ }
+}
+
+.card-header {
+ @include themify($themes) {
+ background-color: themed('headerColor');
+ color: themed('headingColor');
+ }
+
+ a:hover {
+ &:not(.badge){
+ @include themify($themes) {
+ color: themed('learnMoreHover');
+ }
+ }
+ }
+}
+
+.card-body-header {
+ font-size: $font-size-lg;
+ @extend .mb-4
+}
+
+.card ul.fa-ul.card-ul {
+ margin-left: 1.9em;
+
+ li {
+ word-break: break-all;
+ }
+
+ .fa-li {
+ top: 4px;
+ }
+
+ &.carets {
+ margin-left: 1.1em;
+
+ .fa-li {
+ left: -17px;
+ width: 1.1em;
+ }
+ }
+
+ ul {
+ &.carets {
+ margin-left: 0.85em;
+ }
+ }
+}
+
+.card-org-plans {
+ h2 {
+ font-size: $font-size-lg;
+ }
+}
+
+.card-body {
+ &:not(.bg-light > .card-body) {
+ @include themify($themes) {
+ background-color: themed('foregroundColor');
+ color: themed('textColor');
+ }
+ &.card-body a:not(li a) {
+ @include themify($themes) {
+ font-weight: themed('linkWeight');
+ }
+ }
+ }
+}
diff --git a/src/scss/export.module.scss b/src/scss/export.module.scss
new file mode 100644
index 00000000..f727344c
--- /dev/null
+++ b/src/scss/export.module.scss
@@ -0,0 +1,8 @@
+@import 'variables';
+
+:export {
+ lightInputColor: $lightInputColor;
+ lightInputPlaceholderColor: $lightInputPlaceholderColor;
+ darkInputColor: $darkInputColor;
+ darkInputPlaceholderColor: $darkInputPlaceholderColor;
+}
diff --git a/src/scss/export.module.scss.d.ts b/src/scss/export.module.scss.d.ts
new file mode 100644
index 00000000..c4b74b98
--- /dev/null
+++ b/src/scss/export.module.scss.d.ts
@@ -0,0 +1,9 @@
+export interface ThemeVariableExport {
+ lightInputColor: string;
+ lightInputPlaceholderColor: string;
+ darkInputColor: string;
+ darkInputPlaceholderColor: string;
+}
+
+export const ThemeVariables: ThemeVariableExport;
+export default ThemeVariables;
diff --git a/src/scss/forms.scss b/src/scss/forms.scss
new file mode 100644
index 00000000..5d372aaf
--- /dev/null
+++ b/src/scss/forms.scss
@@ -0,0 +1,180 @@
+::-ms-reveal {
+ display: none;
+}
+
+::placeholder {
+ @include themify($themes) {
+ color: themed('inputPlaceholderColor');
+ }
+}
+
+input, select, textarea {
+ &:required {
+ box-shadow: none;
+ }
+}
+
+input[type="search"]::-webkit-search-cancel-button {
+ -webkit-appearance:
+ -cancel-button;
+}
+
+label:not(.form-check-label):not(.btn), label.bold {
+ font-weight: 600;
+ @include themify($themes) {
+ color: themed('headingColor');
+ }
+}
+
+label.form-check-label, .form-control-file {
+ @include themify($themes) {
+ color: themed('headingColor');
+ }
+}
+
+.form-check-block {
+ .form-check-label {
+ font-weight: 600;
+
+ > small {
+ display: block;
+ font-weight: normal;
+ @include themify($themes) {
+ color: themed('textMuted');
+ }
+ }
+
+ > span {
+ display: block;
+ font-weight: normal;
+ @extend .mt-2;
+ }
+ }
+}
+
+.form-check-block + .form-check-block {
+ &:not(.mt-2) {
+ @extend .mt-3;
+ }
+}
+
+.form-inline {
+ input[type='datetime-local'] {
+ width: 200px;
+ }
+}
+
+.form-control {
+ @include themify($themes) {
+ background-color: themed('inputBackgroundColor');
+ border-color: themed('inputBorderColor');
+ color: themed('inputTextColor');
+ }
+
+ &:disabled, &[readonly] {
+ @include themify($themes) {
+ background-color: themed('inputDisabledBackground');
+ color: themed('inputDisabledColor');
+ }
+ }
+}
+
+input[type="radio"], input[type="checkbox"] {
+ cursor: pointer;
+}
+
+.form-control.stripe-form-control {
+ padding-top: 0.55rem;
+
+ &.is-focused {
+ outline: 0;
+ @include themify($themes) {
+ background-color: themed('inputBackgroundColor');
+ border-color: themed('inputBorderColor');
+ box-shadow: 0 0 0 $input-focus-width rgba(mix(color-yiq(themed('primary')), themed('primary'), 15%), .5);
+ color: themed('inputTextColor');
+ }
+
+ &.is-invalid {
+ opacity: 0.75;
+ @include themify($themes) {
+ box-shadow: 0 0 0 $input-focus-width themed('danger');
+ }
+ }
+ }
+
+ &.is-invalid {
+ @include themify($themes) {
+ border-color: themed('danger');
+ }
+ }
+}
+
+.dropdown-menu, .dropdown-item {
+ @include themify($themes) {
+ background-color: themed('dropdownBackground');
+ color: themed('dropdownTextColor');
+ }
+}
+
+.dropdown-item {
+ &.text-danger {
+ color: #FFFFFF !important;
+ @include themify($themes) {
+ background-color: themed('danger');
+ }
+
+ &:hover {
+ color: #FFFFFF !important;
+ @include themify($themes){
+ background-color: themed('dropdownDangerHover') !important;
+ }
+ }
+ }
+ &:hover:not(.text-danger) {
+ @include themify($themes) {
+ background-color: themed('dropdownHover');
+ color: themed('dropdownTextColor');
+ }
+ }
+ &:active{
+ background-color: rgba(0,0,0,0.1) !important;
+ }
+}
+
+.dropdown-menu {
+ button {
+ cursor: pointer;
+ }
+ @include themify($themes) {
+ border: 1px solid themed('listItemBorder');
+ }
+}
+
+.list-group-item {
+ &:focus,
+ &.focus {
+ z-index: 100;
+ }
+ @include themify($themes) {
+ background-color: themed('foregroundColor');
+ border-color: themed('listItemBorder');
+ color: themed('listItemColor');
+ font-weight: themed('linkWeight');
+ }
+ &:hover {
+ @include themify($themes) {
+ color: themed('listItemColorHover');
+ }
+ }
+}
+
+.list-group-item.active {
+ font-weight: bold !important;
+ padding-left: calc(#{$list-group-item-padding-x} - 3px);
+ @include themify($themes) {
+ border-color: themed('borderColor');
+ border-left: 3px solid themed('borderPrimaryColor');
+ color: themed('listItemActive');
+ }
+}
diff --git a/src/scss/modals.scss b/src/scss/modals.scss
new file mode 100644
index 00000000..d39b1714
--- /dev/null
+++ b/src/scss/modals.scss
@@ -0,0 +1,147 @@
+.modal-content {
+ border: none;
+ border-radius: none;
+ @include themify($themes) {
+ background-color: themed('backgroundColor');
+ }
+}
+
+.modal-dialog {
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 0.3rem;
+ width: $modal-md;
+}
+
+.modal-sm {
+ width: $modal-sm;
+}
+
+.modal-lg {
+ width: $modal-lg;
+}
+
+.modal-header {
+ @include themify($themes) {
+ background-color: themed('foregroundColor');
+ border-bottom: 1px solid themed('separator');
+ color: themed('textColor');
+ }
+}
+
+.modal-body {
+ @include themify($themes) {
+ background-color: themed('foregroundColor');
+ color: themed('textColor');
+ }
+
+ h3, .section-header > * {
+ font-weight: normal;
+ text-transform: uppercase;
+ @include themify($themes) {
+ color: themed('textMuted');
+ }
+ }
+}
+.modal .list-group-flush {
+ :first-child {
+ border-top: none;
+ }
+
+ :last-child {
+ border-bottom: none;
+ }
+}
+
+.modal-footer {
+ border-radius: 0.3rem 0.3rem 0 0;
+ justify-content: flex-start;
+ @include themify($themes) {
+ background-color: themed('footerBackgroundColor');
+ border-top: 1px solid themed('separator');
+ }
+}
+
+.close {
+ @include themify($themes) {
+ color: themed('textColor');
+ }
+}
+
+#totpImage {
+ @include themify($themes) {
+ filter: themed('imgFilter');
+ }
+}
+
+.totp {
+ .totp-code {
+ @extend .text-monospace;
+ font-size: 1.2rem;
+ }
+
+ .totp-countdown {
+ display: block;
+ margin: 3px 3px 0 0;
+ user-select: none;
+
+ .totp-sec {
+ font-size: 0.85em;
+ line-height: 32px;
+ position: absolute;
+ text-align: center;
+ width: 32px;
+ }
+
+ svg {
+ height: 32px;
+ transform: rotate(-90deg);
+ width: 32px;
+ }
+
+ .totp-circle {
+ fill: none;
+ @include themify($themes) {
+ stroke: themed('primary');
+ }
+
+ &.inner {
+ stroke-dasharray: 78.6;
+ stroke-dashoffset: 0;
+ stroke-width: 3;
+ }
+
+ &.outer {
+ stroke-dasharray: 88;
+ stroke-dashoffset: 0;
+ stroke-width: 2;
+ }
+ }
+ }
+
+ > .align-items-center {
+ margin-bottom: -5px;
+ }
+
+ &.low {
+ .totp-sec, .totp-code {
+ @include themify($themes) {
+ color: themed('danger');
+ }
+ }
+
+ .totp-circle {
+ @include themify($themes) {
+ stroke: themed('danger');
+ }
+ }
+ }
+}
+
+.cdk-drag-preview {
+ border-radius: $border-radius;
+ opacity: 0.8;
+ z-index: $zindex-tooltip !important;
+ @include themify($themes) {
+ background: themed('cdkDraggingBackground');
+ }
+}
diff --git a/src/scss/navigation.scss b/src/scss/navigation.scss
new file mode 100644
index 00000000..230cdbef
--- /dev/null
+++ b/src/scss/navigation.scss
@@ -0,0 +1,109 @@
+.navbar {
+ padding-left: 0;
+ padding-right: 0;
+ @include themify($themes) {
+ background-color: themed('navBackground') !important;
+ }
+
+ .dropdown-menu {
+ max-width: 300px;
+ min-width: 200px;
+
+ .dropdown-item-text {
+ line-height: 1.3;
+ @include themify($themes) {
+ color: themed('dropdownTextColor');
+ }
+
+ span, small {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ &.text-muted {
+ @include themify($themes) {
+ color: themed('dropdownTextMuted') !important;
+ }
+ }
+ }
+ }
+ }
+ .nav-item {
+ > .nav-link {
+ @include themify($themes) {
+ font-weight: themed('navWeight');
+ }
+ }
+ &.active > .nav-link {
+ @include themify($themes) {
+ font-weight: themed('navActiveWeight');
+ }
+ }
+ }
+}
+
+.navbar-brand {
+ margin-bottom: -20px;
+ margin-top: -20px;
+}
+
+.nav-tabs .nav-link.active {
+ @include themify($themes) {
+ background: themed('navActiveBackground');
+ border-color: themed('borderColor');
+ }
+}
+
+.org-nav {
+ height: 100px;
+ min-height: 100px;
+ @include themify($themes) {
+ background-color: themed('navOrgBackgroundColor');
+ border-bottom: 1px solid themed('borderColor');
+ color: themed('textColor');
+ }
+
+ .container {
+ height: 100%;
+ }
+
+ .nav-tabs {
+ border-bottom: none;
+
+ a {
+ &:not(.active) {
+ border-color: transparent;
+ @include themify($themes) {
+ color: themed('textColor');
+ }
+ }
+
+ &.active {
+ font-weight: bold;
+ padding-top: calc(#{$nav-link-padding-y} - 2px);
+ @include themify($themes) {
+ border-top: 3px solid themed('primary');
+ color: themed('linkColor');
+ }
+ }
+
+ &.disabled {
+ @include themify($themes) {
+ color: themed('inputDisabledColor');
+ }
+ }
+ }
+ }
+
+ .org-name {
+ line-height: 1;
+ span {
+ display: block;
+ font-size: $font-size-lg;
+ @include themify($themes) {
+ color: themed('headingColor');
+ }
+ }
+ }
+}
+
diff --git a/src/scss/pages.scss b/src/scss/pages.scss
new file mode 100644
index 00000000..aa19235c
--- /dev/null
+++ b/src/scss/pages.scss
@@ -0,0 +1,283 @@
+.password-wrapper {
+ min-width: 0;
+ white-space: pre-wrap;
+ word-break: break-all;
+}
+
+.password-row {
+ min-width: 0;
+}
+
+.password-letter {
+ @include themify($themes) {
+ color: themed('pwLetter');
+ }
+}
+
+.password-number {
+ @include themify($themes) {
+ color: themed('pwNumber');
+ }
+}
+
+.password-special {
+ @include themify($themes) {
+ color: themed('pwSpecial');
+ }
+}
+
+app-vault-groupings, app-org-vault-groupings, .groupings {
+ .card {
+ #search {
+ margin-bottom: 1rem;
+ @include themify($themes) {
+ background-color: themed('inputBackgroundColor');
+ border-color: themed('inputBorderColor');
+ color: themed('inputTextColor');
+ }
+
+ &::placeholder {
+ @include themify($themes) {
+ color: themed('inputPlaceholderColor');
+ }
+ }
+ }
+
+ h3 {
+ font-weight: normal;
+ text-transform: uppercase;
+ @include themify($themes) {
+ color: themed('textMuted');
+ }
+ }
+
+ ul:last-child {
+ margin-bottom: 0;
+ }
+
+ .card-body a {
+ @include themify($themes) {
+ color: themed('headingColor');
+ font-weight: themed('linkWeight');
+ }
+
+ &:hover {
+ &.text-muted {
+ @include themify($themes) {
+ color: themed('iconHover') !important;
+ }
+ }
+ }
+ }
+
+ .show-active {
+ display: none;
+ }
+
+ li {
+ > .fa, > div > .fa {
+ cursor: pointer;
+ }
+ }
+
+ li.active {
+ > .show-active, > div .show-active {
+ display: inline;
+ }
+ }
+
+ li.active {
+ > a:first-of-type, > div a:first-of-type {
+ font-weight: bold;
+ @include themify($themes) {
+ color: themed('linkColor');
+ }
+ }
+
+ > .fa, > div > .fa {
+ @include themify($themes) {
+ color: themed('linkColor');
+ }
+ }
+ }
+ }
+}
+
+app-password-generator {
+ #lengthRange {
+ width: 100%;
+ }
+
+ .card-password {
+ .card-body {
+ @include themify($themes) {
+ background: themed('foregroundColor');
+ }
+ align-items: center;
+ display: flex;
+ flex-wrap: wrap;
+ font-family: $font-family-monospace;
+ font-size: $font-size-lg;
+ justify-content: center;
+ text-align: center;
+ }
+ }
+}
+
+app-password-generator-history {
+ .list-group-item {
+ line-height: 1;
+ @include themify($themes) {
+ background: themed('backgroundColor');
+ }
+
+ .password {
+ font-family: $font-family-monospace;
+ }
+ }
+}
+
+app-import {
+ textarea {
+ height: 150px;
+ }
+}
+
+app-avatar {
+ img {
+ @extend .rounded;
+ }
+}
+
+app-user-billing {
+ .progress {
+ height: 20px;
+
+ .progress-bar {
+ min-width: 50px;
+ }
+ }
+}
+
+
+/* Register Layout Page - Exempt from themify */
+body.theme_light_force {
+ background-color: #ECF0F5;
+ a, .btn-link {
+ color: #175DDC;
+ &:hover {
+ color: #104097;
+ }
+ }
+
+ .btn-outline-secondary {
+ color: #6c757d;
+ &:hover {
+ color: #212529;
+ }
+ }
+
+ .text-muted {
+ color: #6C757D !important;
+ }
+
+ .card, .card-body {
+ background-color: #FFFFFF;
+ border-color: rgba(0,0,0,.125);
+ }
+
+ .form-control {
+ background-color: #FBFBFB;
+ border: 1px solid #CED4DA;
+ color: #495057;
+ }
+
+ label {
+ color: #333333;
+ font-weight: 600;
+ &.small {
+ font-weight: 400;
+ }
+ }
+
+ hr {
+ border-top: 1px solid rgba(0,0,0,.1)
+ }
+ .progress {
+ background-color: #E9ECEF;
+ }
+ .bg-primary {
+ background-color: #175DDC !important;
+ }
+ .bg-success {
+ background-color: #00A65A !important;
+ }
+ .bg-warning {
+ background-color: #BF7E16 !important;
+ }
+ .bg-danger {
+ background-color: #DD4B39 !important;
+ }
+
+ .layout {
+ &.enterprise2 {
+ header {
+ background: #175DDC;
+ color: #CED4DA;
+
+ &:before {
+ background: #175DDC;
+ content: "";
+ height: 416px;
+ left: 0;
+ position: absolute;
+ top: -76px;
+ transform: skewY(-3deg);
+ width: 100%;
+ z-index: -1;
+ }
+ img.logo {
+ margin: 12px 0 0;
+ max-width: 284px;
+ height: 57px;
+ width: 284px;
+ }
+ }
+
+ h2 {
+ color: #FFFFFF;
+ font-size: 1.8rem;
+ margin: 100px 0 150px 0;
+ }
+
+ p {
+ font-size: 1.4rem;
+ margin: 20px 0 40px 0;
+
+ &:before {
+ content: "/";
+ padding-right: 12px;
+ }
+ &:not(.highlight) {
+ &:before {
+ color: #1252A3;
+ }
+ }
+
+ b {
+ &:after {
+ content: "⟶";
+ font-size: 2rem;
+ padding-left: 6px;
+ }
+ }
+ }
+
+ blockquote {
+ font-size: 1.4rem;
+ margin: 20px 0 0 0;
+ padding-right: 40px;
+ }
+ }
+ }
+}
diff --git a/src/scss/plugins.scss b/src/scss/plugins.scss
index 3fc34da1..5415f801 100644
--- a/src/scss/plugins.scss
+++ b/src/scss/plugins.scss
@@ -1,107 +1,127 @@
-$fa-font-path: "~font-awesome/fonts";
-@import "~font-awesome/scss/font-awesome.scss";
-@import "~angular2-toaster/toaster";
-@import "~sweetalert2/src/sweetalert2.scss";
-
-.toast-container {
- &.toast-top-right {
- top: 76px;
+#duo-frame {
+ height: 330px;
+ @include themify($themes) {
+ background: themed('imgLoading') 0 0 no-repeat;
}
- .toast-close-button {
- margin-right: 4px;
- font-size: 18px;
+ iframe {
+ border: none;
+ height: 100%;
+ width: 100%;
+ }
+}
+
+#web-authn-frame {
+ height: 290px;
+ @include themify($themes) {
+ background: themed('imgLoading') 0 0 no-repeat;
}
- .toast {
- opacity: 1 !important;
- background-image: none !important;
- border-radius: $border-radius;
- box-shadow: 0 0 8px rgba(0, 0, 0, 0.35);
- display: flex;
- align-items: center;
+ iframe {
+ border: none;
+ height: 100%;
+ width: 100%;
+ }
+}
- &:hover {
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.6);
- }
+#hcaptcha_iframe {
+ border: none;
+ transition: height 0.25s linear;
+ width: 100%;
+}
- &:before {
- font-family: FontAwesome;
- font-size: 25px;
- line-height: 20px;
- float: left;
- color: #ffffff;
- margin: auto 0 auto 15px;
- }
+.list-group-2fa {
+ .logo-2fa {
+ min-width: 100px;
+ }
+}
- .toast-content {
- padding: 15px;
- }
+.progress {
+ @include themify($themes) {
+ background-color: themed('pwStrengthBackground');
+ }
+}
- .toaster-icon {
- display: none;
- }
+// Braintree
- .toast-message {
- p {
- margin-bottom: 0.5rem;
+#bt-dropin-container {
+ min-height: 50px;
+ @include themify($themes) {
+ background: themed("loadingSvg") center center no-repeat;
+ }
+}
- &:last-child {
- margin-bottom: 0;
- }
- }
- }
+.braintree-placeholder, .braintree-sheet__header {
+ display: none;
+}
- &.toast-danger, &.toast-error {
- background-image: none !important;
- background-color: $danger;
+.braintree-sheet__content--button {
+ min-height: 0;
+ padding: 0;
+ text-align: left;
+}
- &:before {
- content: "\f0e7";
- }
- }
+.braintree-sheet__container {
+ margin-bottom: 0;
+}
- &.toast-warning {
- background-image: none !important;
- background-color: $warning;
+.braintree-sheet {
+ border: none;
+}
- &:before {
- content: "\f071";
- }
- }
+[data-braintree-id="upper-container"]::before {
+ @include themify($themes) {
+ background-color: themed('backgroundColor');
+ }
+}
- &.toast-info {
- background-image: none !important;
- background-color: $info;
+.card [data-braintree-id="upper-container"]::before {
+ @include themify($themes) {
+ background-color: themed('foregroundColor');
+ }
+}
- &:before {
- content: "\f05a";
- }
- }
+[data-braintree-id="paypal-button"] {
+ @include themify($themes) {
+ background-color: themed('backgroundColor');
+ }
+}
- &.toast-success {
- background-image: none !important;
- background-color: $success;
+.card [data-braintree-id="paypal-button"] {
+ @include themify($themes) {
+ background-color: themed('foregroundColor');
+ }
+}
- &:before {
- content: "\f00C";
- }
- }
+.paypal-button-text {
+ @include themify($themes) {
+ color: themed('textColor');
}
}
// SweetAlert2
+[class*="swal2-"] {
+ &:not(.swal2-container, .swal2-confirm, .swal2-cancel, .swal2-deny) {
+ @include themify($themes) {
+ background-color: themed('backgroundColor');
+ color: themed('textColor');
+ }
+ }
+}
+
.swal2-container {
background-color: rgba(0,0,0,.3);
}
.swal2-popup {
- padding: 15px 0 0;
- background-color: $modal-content-bg;
- color: $body-color;
+ @include themify($themes) {
+ background-color: themed('backgroundColor');
+ color: themed('textColor');
+ }
border: $modal-content-border-width solid #9a9a9a;
@include border-radius($modal-content-border-radius);
+ padding: 15px 0 0;
width: 34em; // slightly bigger than the hardcoded 478px in v1.
.swal2-header {
@@ -118,7 +138,9 @@ $fa-font-path: "~font-awesome/fonts";
.swal2-content {
padding-bottom: 15px;
font-size: $font-size-base;
- border-bottom: $modal-footer-border-width solid $modal-footer-border-color;
+ @include themify($themes) {
+ border-bottom: $modal-footer-border-width solid themed('separator');
+ }
}
i.swal-custom-icon {
@@ -128,41 +150,37 @@ $fa-font-path: "~font-awesome/fonts";
}
.swal2-title {
- padding: 10px 0 15px;
- margin: 0;
font-size: $font-size-lg;
- color: $body-color;
+ margin: 0;
+ padding: 10px 0 15px;
+ @include themify($themes) {
+ color: themed('headingColor');
+ }
}
.swal2-content {
font-size: $font-size-base;
- color: $body-color;
padding: 0 15px 15px;
+ @include themify($themes) {
+ color: themed('textColor');
+ }
}
.swal2-actions {
- padding: 15px;
- margin: 0;
- background-color: $input-bg;
@include border-radius($modal-content-border-radius);
display: flex;
flex-direction: row;
- justify-content: flex-start;
font-size: $font-size-base;
+ justify-content: flex-start;
+ margin: 0;
+ padding: 15px;
+ @include themify($themes) {
+ background-color: themed('backgroundColor');
+ }
button {
margin-right: 10px;
@extend .btn;
-
- &.swal2-confirm {
- @extend .btn-primary;
- font-weight: bold;
- }
-
- &.swal2-cancel {
- @extend .btn-outline-secondary;
- background-color: #ffffff;
- }
}
}
diff --git a/src/scss/register-layout.scss b/src/scss/register-layout.scss
deleted file mode 100644
index 62af3e37..00000000
--- a/src/scss/register-layout.scss
+++ /dev/null
@@ -1,63 +0,0 @@
-.layout {
- &.enterprise2 {
-
- header {
- color: $secondary;
- background-color: $primary;
-
- &:before {
- content: "";
- position: absolute;
- z-index: -1;
- width: 100%;
- height: 340px;
- left: 0;
- transform: skewY(-3deg);
- background: $primary;
- }
-
- img.logo {
-
- margin: 12px 0 0;
- width: 284px;
- max-width: 284px;
- height: auto;
- }
- }
-
- h2 {
- color: #ffffff;
- font-size: 1.8rem;
- margin: 100px 0 150px 0;
- }
-
- p {
- margin: 20px 0 40px 0;
- font-size: 1.4rem;
-
- &:before {
- content: "/";
- padding-right: 12px;
- }
- &:not(.highlight) {
- &:before {
- color: $primary-accent;
- }
- }
-
- b {
- &:after {
- content: "⟶";
- font-size: 2rem;
- padding-left: 6px;
- }
- }
- }
-
- blockquote {
- margin: 20px 0 0 0;
- font-size: 1.4rem;
- padding-right: 40px;
- }
- }
-}
diff --git a/src/scss/styles.scss b/src/scss/styles.scss
index deadb006..45a91fe1 100644
--- a/src/scss/styles.scss
+++ b/src/scss/styles.scss
@@ -1,77 +1,5 @@
@import "../../jslib/angular/src/scss/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";
//@import "~bootstrap/scss/bootstrap";
@import "~bootstrap/scss/_functions";
@@ -111,780 +39,19 @@ $grid-breakpoints: (
@import "~bootstrap/scss/_spinners";
@import "~bootstrap/scss/_utilities";
@import "~bootstrap/scss/_print";
+
+@import "~angular2-toaster/toaster";
+@import "~font-awesome/scss/font-awesome.scss";
+@import "~sweetalert2/src/sweetalert2.scss";
+
+@import "./base";
+@import "./buttons";
+@import "./callouts";
+@import "./cards";
+@import "./forms";
+@import "./navigation";
+@import "./modals";
+@import "./pages";
@import "./plugins";
-
-html {
- font-size: 14px;
-}
-
-body {
- min-width: 1010px;
-
- &.layout_frontend {
- background-color: #ecf0f5;
- }
-
- &.full-width:not(.layout_frontend) {
- .container {
- min-width: 980px;
- width: 90%;
- }
- }
-}
-
-.page-header, .secondary-header {
- border-bottom: 1px solid $border-color;
- padding-bottom: 0.6rem;
- margin-bottom: 0.5rem;
-
- h1, h2, h3, h4 {
- margin: 0;
- }
-}
-
-h1, h2, h3, h4, h5 {
- small {
- font-size: 80%;
- }
-}
-
-input, select, textarea {
- &:required {
- box-shadow: none;
- }
-}
-
-.secondary-header, .spaced-header {
- margin-top: 4rem;
-}
-
-.navbar {
- padding-left: 0;
- padding-right: 0;
-
- .dropdown-menu {
- min-width: 200px;
- max-width: 300px;
-
- .dropdown-item-text {
- line-height: 1.3;
-
- span, small {
- display: block;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- }
- }
-
- .nav-link {
- font-weight: 600;
- }
-}
-
-.navbar-brand {
- margin-top: -20px;
- margin-bottom: -20px;
-}
-
-.dropdown-menu {
- button {
- cursor: pointer;
- }
-}
-
-.container {
- width: 980px;
- max-width: none !important;
- margin: 0 auto;
- padding: 0;
-}
-
-.page-content {
- margin-top: 20px;
-}
-
-.footer {
- margin-top: 40px;
- padding: 40px 0 40px 0;
- border-top: 1px solid $border-color;
-}
-
-.list-group-item.active {
- border-left: 3px solid theme-color("primary");
- font-weight: bold;
- padding-left: calc(#{$list-group-item-padding-x} - 3px);
-}
-
-.card-header, .modal-header {
- font-weight: bold;
- text-transform: uppercase;
-
- small {
- font-weight: normal;
- text-transform: none;
- @extend .text-muted;
- }
-}
-
-.card-body-header {
- font-size: $font-size-lg;
- @extend .mb-4
-}
-
-.card ul.fa-ul.card-ul {
- margin-left: 1.9em;
-
- li {
- word-break: break-all;
- }
-
- .fa-li {
- top: 4px;
- }
-
- &.carets {
- margin-left: 1.1em;
-
- .fa-li {
- left: -17px;
- width: 1.1em;
- }
- }
-
- ul {
- &.carets {
- margin-left: 0.85em;
- }
- }
-}
-
-.card-org-plans {
- h2 {
- font-size: $font-size-lg;
- }
-}
-
-.modal-dialog {
- width: $modal-md;
-}
-
-.modal-sm {
- width: $modal-sm;
-}
-
-.modal-lg {
- width: $modal-lg;
-}
-
-.modal-body {
- h3, .section-header > * {
- font-weight: normal;
- text-transform: uppercase;
- color: $text-muted;
- }
-}
-.modal .list-group-flush {
- :first-child {
- border-top: none;
- }
- :last-child {
- border-bottom: none;
- }
-}
-
-.modal-footer {
- justify-content: flex-start;
- background-color: $input-bg;
- @include border-radius($modal-content-border-radius);
-}
-
-label:not(.form-check-label):not(.btn), label.bold {
- font-weight: 600;
-}
-
-input[type="search"]::-webkit-search-cancel-button {
- -webkit-appearance: searchfield-cancel-button;
-}
-
-.btn[class*="btn-outline-"] {
- &:not(:hover) {
- border-color: $secondary;
- background-color: #fbfbfb;
- }
-}
-
-.btn-link {
- &:focus,
- &.focus {
- outline-color: -webkit-focus-ring-color;
- outline-offset: 1px;
- outline-style: auto;
- outline-width: 1px;
- }
-}
-
-.btn-outline-secondary {
- color: $text-muted;
-
- &:hover:not(:disabled) {
- color: $body-color;
- }
-
- &:disabled {
- opacity: 1;
- }
-
- &:focus,
- &.focus {
- box-shadow: 0 0 0 $btn-focus-width rgba(mix(color-yiq($primary), $primary, 15%), .5);
- }
-}
-
-.btn-submit {
- position: relative;
-
- .fa-spinner {
- position: absolute;
- display: none;
- align-items: center;
- justify-content: center;
- bottom: 0;
- top: 0;
- left: 0;
- right: 0;
- }
-
- &:disabled:not(.manual), &.loading {
- .fa-spinner {
- display: flex;
- }
-
- span {
- visibility: hidden;
- }
- }
-}
-
-.list-group-item {
- &:focus,
- &.focus {
- z-index: 100;
- }
-}
-
-.fa-icon-above-input {
- height: 1.5em;
-}
-
-.table.table-list {
- thead th {
- border-top: none;
- }
-
- tr:first-child {
- td {
- border: none;
- }
- }
-
- td {
- vertical-align: middle;
-
- &.reduced-lh {
- line-height: 1;
- small {
- font-size: 80%;
- }
- }
-
- small, > .fa, .icon {
- color: $text-muted;
- }
- }
-
- td.wrap {
- word-break: break-all;
- }
-
- td.table-list-options {
- width: 76px;
- max-width: 76px;
- text-align: right;
- height: 50px;
-
- &.wider {
- width: 100px;
- max-width: 100px;
- }
-
- .btn {
- line-height: 1;
- transition: initial;
- }
-
- .dropdown-menu {
- line-height: $line-height-base;
- }
- }
-
- tr:not(:hover) td.table-list-options {
- > .dropdown:not(.show) button:not(:focus):not(:active), > button:not(:focus):not(:active) {
- @extend .sr-only;
- }
- }
-
- td.table-list-icon {
- width: 45px;
- max-width: 45px;
- text-align: center;
-
- img {
- @extend .rounded;
- @extend .img-fluid;
- max-height: 24px;
- }
- }
-
- td.table-list-checkbox {
- width: 35px;
- max-width: 35px;
- }
-
- td.table-list-strike {
- color: $text-muted;
- text-decoration: line-through;
- }
-}
-
-.text-lg {
- font-size: $font-size-lg;
-}
-
-.text-strike {
- text-decoration: line-through;
-}
-
-.font-weight-semibold {
- font-weight: 600;
-}
-
-.password-wrapper {
- word-break: break-all;
- white-space: pre-wrap;
- min-width: 0;
-}
-
-.password-row {
- min-width: 0;
-}
-
-.password-number {
- color: #007fde;
-}
-
-.password-special {
- color: #c40800;
-}
-
-app-vault-groupings, app-org-vault-groupings, .groupings {
- .card {
- #search {
- margin-bottom: 1rem;
- }
-
- h3 {
- font-weight: normal;
- text-transform: uppercase;
- color: $text-muted;
- }
-
- ul:last-child {
- margin-bottom: 0;
- }
-
- .card-body a {
- color: $body-color;
-
- &:hover {
- &.text-muted {
- color: $body-color !important;
- }
- }
- }
-
- .show-active {
- display: none;
- }
-
- li {
- > .fa, > div > .fa {
- cursor: pointer;
- }
- }
-
- li.active {
- > .show-active, > div .show-active {
- display: inline;
- }
- }
-
- li.active {
- > a:first-of-type, > div a:first-of-type {
- font-weight: bold;
- color: theme-color("primary");
- }
-
- > .fa, > div > .fa {
- color: theme-color("primary");
- }
- }
- }
-}
-
-app-password-generator {
- #lengthRange {
- width: 100%;
- }
-
- .card-password {
- .card-body {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- justify-content: center;
- text-align: center;
- font-size: $font-size-lg;
- font-family: $font-family-monospace;
- }
- }
-}
-
-app-password-generator-history {
- .list-group-item {
- line-height: 1;
-
- .password {
- font-family: $font-family-monospace;
- }
- }
-}
-
-app-import {
- textarea {
- height: 150px;
- }
-}
-
-app-avatar {
- img {
- @extend .rounded;
- }
-}
-
-app-user-billing {
- .progress {
- height: 20px;
-
- .progress-bar {
- min-width: 50px;
- }
- }
-}
-
-#duo-frame {
- background: url('../images/loading.svg') 0 0 no-repeat;
- height: 330px;
-
- iframe {
- width: 100%;
- height: 100%;
- border: none;
- }
-}
-
-#web-authn-frame {
- background: url('../images/loading.svg') 0 0 no-repeat;
- height: 290px;
-
- iframe {
- width: 100%;
- height: 100%;
- border: none;
- }
-}
-
-#hcaptcha_iframe {
- width: 100%;
- border: none;
- transition: height 0.25s linear;
-}
-
-#bt-dropin-container {
- background: url('../images/loading.svg') 0 0 no-repeat;
- min-height: 50px;
-}
-
-.braintree-placeholder, .braintree-sheet__header {
- display: none;
-}
-
-.braintree-sheet__content--button {
- text-align: left;
- padding: 0;
- min-height: 0;
-}
-
-.braintree-sheet__container {
- margin-bottom: 0;
-}
-
-.braintree-sheet {
- border: none;
-}
-
-.totp {
- .totp-code {
- @extend .text-monospace;
- font-size: 1.2rem;
- }
-
- .totp-countdown {
- margin: 3px 3px 0 0;
- display: block;
- user-select: none;
-
- .totp-sec {
- font-size: 0.85em;
- position: absolute;
- line-height: 32px;
- width: 32px;
- text-align: center;
- }
-
- svg {
- width: 32px;
- height: 32px;
- transform: rotate(-90deg);
- }
-
- .totp-circle {
- fill: none;
- stroke: $primary;
-
- &.inner {
- stroke-width: 3;
- stroke-dasharray: 78.6;
- stroke-dashoffset: 0;
- }
-
- &.outer {
- stroke-width: 2;
- stroke-dasharray: 88;
- stroke-dashoffset: 0;
- }
- }
- }
-
- > .align-items-center {
- margin-bottom: -5px;
- }
-
- &.low {
- .totp-sec, .totp-code {
- color: $danger;
- }
-
- .totp-circle {
- stroke: $danger;
- }
- }
-}
-
-.callout {
- padding: $alert-padding-y $alert-padding-x;
- margin-bottom: $alert-margin-bottom;
- border: 1px solid $card-border-color;
- border-left-width: 5px;
- border-radius: $card-inner-border-radius;
- background-color: #fafafa;
-
- .callout-heading {
- margin-top: 0;
- }
-
- h3.callout-heading {
- font-weight: bold;
- text-transform: uppercase;
- }
-
- &.callout-primary {
- border-left-color: $primary;
-
- .callout-heading {
- color: $primary;
- }
- }
-
- &.callout-info {
- border-left-color: $gray-800;
-
- .callout-heading {
- color: $gray-800;
- }
- }
-
- &.callout-danger {
- border-left-color: $danger;
-
- .callout-heading {
- color: $danger;
- }
- }
-
- &.callout-success {
- border-left-color: $success;
-
- .callout-heading {
- color: $success;
- }
- }
-
- &.callout-warning {
- border-left-color: $warning;
-
- .callout-heading {
- color: $warning;
- }
- }
-
- .enforced-policy-options ul {
- margin-bottom: 0px;
- }
-}
-
-.list-group-2fa {
- .logo-2fa {
- min-width: 100px;
- }
-}
-
-.form-check-block {
- .form-check-label {
- font-weight: 600;
-
- > small {
- display: block;
- color: $text-muted;
- font-weight: normal;
- }
-
- > span {
- display: block;
- font-weight: normal;
- @extend .mt-2;
- }
- }
-}
-
-.form-check-block + .form-check-block {
- &:not(.mt-2) {
- @extend .mt-3;
- }
-}
-
-.form-inline {
- input[type='datetime-local'] {
- width: 200px;
- }
-}
-
-.form-control.stripe-form-control {
- padding-top: 0.55rem;
-
- &.is-focused {
- color: $input-focus-color;
- background-color: $input-focus-bg;
- border-color: $input-focus-border-color;
- outline: 0;
- box-shadow: $input-focus-box-shadow;
-
- &.is-invalid {
- box-shadow: 0 0 0 $input-focus-width rgba($form-feedback-invalid-color, .25);
- }
- }
-
- &.is-invalid {
- border-color: $form-feedback-invalid-color;
- }
-}
-
-.org-nav {
- background-color: $input-bg;
- border-bottom: 1px solid $border-color;
- height: 100px;
- min-height: 100px;
-
- .container {
- height: 100%;
- }
-
- .nav-tabs {
- border-bottom: none;
-
- a {
- color: $body-color;
-
- &:not(.active) {
- border-color: transparent;
- }
-
- &.active {
- border-top: 3px solid theme-color("primary");
- font-weight: bold;
- padding-top: calc(#{$nav-link-padding-y} - 2px);
- }
-
- &.disabled {
- color: $input-placeholder-color;
- }
- }
- }
-
- .org-name {
- line-height: 1;
- span {
- font-size: $font-size-lg;
- display: block;
- }
- }
-}
-
-img.logo {
- width: 284px;
- height: 43px;
- margin: 0 auto;
- display: block;
-}
-
-.min-height-fix {
- min-height: 1px;
-}
-
-.overflow-hidden {
- overflow: hidden;
-}
-
-.cdk-drag-preview {
- z-index: $zindex-tooltip !important;
- opacity: 0.8;
- background-color: $white;
- border-radius: $border-radius;
-}
-
-.cursor-move {
- cursor: move !important;
-}
-
-@import "./register-layout";
+@import "./tables";
+@import "./toasts";
diff --git a/src/scss/tables.scss b/src/scss/tables.scss
new file mode 100644
index 00000000..cb4fe220
--- /dev/null
+++ b/src/scss/tables.scss
@@ -0,0 +1,123 @@
+.table.table-list {
+ @include themify($themes) {
+ color: themed('textColor');
+ }
+
+ &.table td, .table th {
+ &:not(tr:first-child td) {
+ @include themify($themes) {
+ border-top: 1px solid themed('tableSeparator');
+ }
+ }
+ }
+
+ thead th {
+ border-top: none;
+ }
+
+ tr:first-child {
+ td {
+ border: none;
+ }
+ }
+
+ td {
+ vertical-align: middle;
+ @include themify($themes) {
+ color: themed('textColor');
+ }
+
+ & > a {
+ @include themify($themes) {
+ color: themed('tableLinkColor');
+ }
+ &:hover {
+ @include themify($themes) {
+ color: themed('tableLinkColorHover');
+ }
+ }
+ }
+
+ &.reduced-lh {
+ line-height: 1;
+
+ small {
+ font-size: 80%;
+ }
+ }
+
+ small, > .fa, .icon {
+ @include themify($themes) {
+ color: themed('textMuted');
+ }
+ }
+
+ .fa-globe {
+ @include themify($themes) {
+ color: themed('iconColor');
+ }
+ }
+ }
+
+ td.wrap {
+ word-break: break-all;
+ }
+
+ td.table-list-options {
+ height: 50px;
+ max-width: 76px;
+ text-align: right;
+ width: 76px;
+
+ &.wider {
+ max-width: 100px;
+ width: 100px;
+ }
+
+ .btn {
+ line-height: 1;
+ transition: initial;
+ }
+
+ .dropdown-menu {
+ line-height: $line-height-base;
+ }
+ }
+
+ tr:not(:hover) td.table-list-options {
+ > .dropdown:not(.show) button:not(:focus):not(:active), > button:not(:focus):not(:active) {
+ @extend .sr-only;
+ }
+ }
+
+ td.table-list-icon {
+ max-width: 45px;
+ text-align: center;
+ width: 45px;
+
+ img {
+ @extend .rounded;
+ @extend .img-fluid;
+ max-height: 24px;
+ }
+ }
+
+ td.table-list-checkbox {
+ max-width: 35px;
+ width: 35px;
+ }
+
+ td.table-list-strike {
+ text-decoration: line-through;
+ @include themify($themes) {
+ color: themed('textMuted');
+ }
+ }
+}
+
+.table-hover tbody tr:hover {
+ @include themify($themes) {
+ background-color: themed('tableRowHover');
+ color: themed('tableColorHover');
+ }
+}
diff --git a/src/scss/toasts.scss b/src/scss/toasts.scss
new file mode 100644
index 00000000..62adbcea
--- /dev/null
+++ b/src/scss/toasts.scss
@@ -0,0 +1,110 @@
+.toast-container {
+ &.toast-top-right {
+ top: 76px;
+ }
+
+ .toast-close-button {
+ font-size: 18px;
+ margin-right: 4px;
+ }
+
+ .toast {
+ align-items: center;
+ background-image: none !important;
+ border-radius: $border-radius;
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.35);
+ display: flex;
+ opacity: 1 !important;
+
+ &:hover {
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.6);
+ }
+
+ &:before {
+ color: #FFFFFF;
+ float: left;
+ font-family: FontAwesome;
+ font-size: 25px;
+ line-height: 20px;
+ margin: auto 0 auto 15px;
+ }
+
+ .toast-content {
+ padding: 15px;
+ }
+
+ .toaster-icon {
+ display: none;
+ }
+
+ .toast-message {
+ p {
+ margin-bottom: 0.5rem;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+ }
+
+ &.toast-danger, &.toast-error {
+ background-image: none !important;
+
+ &:before {
+ content: "\f0e7";
+ }
+ }
+
+ &.toast-warning {
+ background-image: none !important;
+
+ &:before {
+ content: "\f071";
+ }
+ }
+
+ &.toast-info {
+ background-image: none !important;
+
+ &:before {
+ content: "\f05a";
+ }
+ }
+
+ &.toast-success {
+ background-image: none !important;
+
+ &:before {
+ content: "\f00C";
+ }
+ }
+ }
+}
+
+.toast-error, .toast-container .toast-error:before, .toast-danger, .toast-container .toast-danger:before, .bg-danger {
+ @include themify($themes) {
+ background-color: themed('danger') !important;
+ color: themed('dangerTextColor') !important;
+ }
+}
+
+.toast-warning, .toast-container .toast-warning:before, .bg-warning {
+ @include themify($themes) {
+ background-color: themed('warning') !important;
+ color: themed('warningTextColor') !important;
+ }
+}
+
+.toast-success, .toast-container .toast-success:before, .bg-success {
+ @include themify($themes) {
+ background-color: themed('success') !important;
+ color: themed('successTextColor') !important;
+ }
+}
+
+.toast-info, .toast-container .toast-info:before, .bg-info {
+ @include themify($themes) {
+ background-color: themed('info') !important;
+ color: themed('infoTextColor') !important;
+ }
+}
diff --git a/src/scss/variables.scss b/src/scss/variables.scss
new file mode 100644
index 00000000..34947251
--- /dev/null
+++ b/src/scss/variables.scss
@@ -0,0 +1,349 @@
+$primary: #175DDC;
+$primary-accent: #1252A3;
+$secondary: #CED4DA;
+$secondary-alt: #1A3B66;
+$success: #00A65A;
+$info: #555555;
+$warning: #BF7E16;
+$danger: #DD4B39;
+$white: #FFFFFF;
+
+$theme-colors: (
+ "primary-accent": $primary-accent,
+ "secondary-alt": $secondary-alt,
+);
+
+$body-bg: $white;
+$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: $white;
+$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($white, .7);
+$navbar-dark-hover-color: rgba($white, .9);
+$navbar-nav-link-padding-x: 0.8rem;
+
+$input-bg: #FBFBFB;
+$input-focus-bg: $white;
+$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: $secondary;
+
+$fa-font-path: "~font-awesome/fonts";
+
+// Theme Variables
+
+$lightDangerHover: #C43421;
+
+$darkPrimary: #6A99F0;
+$darkPrimary-alt: #B4CCF9;
+$darkDanger: #FF8D85;
+$darkDangerHover: #FFBfBB;
+$darkSuccess: #52E07C;
+$darkWarning: #FFEB66;
+$darkInfo: #A4B0C6;
+$darkLinks: #6A99F0;
+$darkGrey1: #BAC0CE;
+$darkGrey2: #8D94A5;
+$darkBlue1: #4C525F;
+$darkBlue2: #3C424E;
+$darkDarkBlue1: #2F343D;
+$darkDarkBlue2: #1F242E;
+
+$lightInputColor: #465057;
+$lightInputPlaceholderColor: #B6B8B8;
+$darkInputColor: $white;
+$darkInputPlaceholderColor: $darkGrey1;
+
+$themes: (
+ light: (
+ logoSuffix: 'dark',
+ primary: $primary,
+ primaryAlt: $primary-accent,
+ danger: $danger,
+ success: $success,
+ warning: $warning,
+ info: #343A40,
+ textColor: $text-color,
+ headingColor: #333333,
+ darkTextColor: #495057,
+ warningTextColor: $white,
+ successTextColor: $white,
+ dangerTextColor: $white,
+ infoTextColor: $white,
+ textMuted: #6C757D,
+ backgroundColor: $body-bg,
+ badgeDangerBackground: $danger,
+ badgeDangerText: $white,
+ badgeInfoBackground: #555555,
+ badgeInfoText: $white,
+ badgePrimaryBackground: $primary,
+ badgePrimaryBackgroundHover: #134EB9,
+ badgePrimaryText: $white,
+ badgeSecondaryBackground: #CED4DA,
+ badgeSecondaryText: #212529,
+ bgPrimaryColor: $primary,
+ bgLightColor: #F8F9FA,
+ borderColor: $border-color,
+ borderPrimaryColor: $primary,
+ btnDanger: $danger,
+ btnDangerHover: $lightDangerHover,
+ btnDangerText: $white,
+ btnLinkTextColor: $primary,
+ btnLinkTextColorHover: #104097,
+ btnOutlineDangerBackground: $input-bg,
+ btnOutlineDangerBackgroundHover: $danger,
+ btnOutlineDangerBorder: #CED4DA,
+ btnOutlineDangerBorderHover: $danger,
+ btnOutlineDangerText: $danger,
+ btnOutlineDangerTextHover: $white,
+ btnOutlinePrimaryBackground: $input-bg,
+ btnOutlinePrimaryBackgroundHover: $primary,
+ btnOutlinePrimaryBorder: #CED4DA,
+ btnOutlinePrimaryBorderHover: $primary,
+ btnOutlinePrimaryText: $primary,
+ btnOutlinePrimaryTextHover: $white,
+ btnOutlineSecondaryBackground: $input-bg,
+ btnOutlineSecondaryBackgroundHover: #CED4DA,
+ btnOutlineSecondaryBorder: #CED4DA,
+ btnOutlineSecondaryBorderHover: #CED4DA,
+ btnOutlineSecondaryText: #6C757D,
+ btnOutlineSecondaryTextHover: #333333,
+ btnPrimary: $primary,
+ btnPrimaryBorderHover: #1249AE,
+ btnPrimaryHover: #134EB9,
+ btnPrimaryText: $white,
+ btnSecondary: $secondary,
+ btnSecondaryBorder: $secondary,
+ btnSecondaryBorderHover: #B1BBC4,
+ btnSecondaryHover: #B8C1CA,
+ btnSecondaryText: #212529,
+ btnSecondaryTextHover: #212529,
+ calloutBackground: #FAFAFA,
+ calloutColor: #212529,
+ cdkDraggingBackground: $white,
+ codeColor: #E83E8C,
+ dropdownBackground: $white,
+ dropdownDangerHover: #C43421,
+ dropdownHover: rgba(0,0,0,0.06),
+ dropdownTextColor: $text-color,
+ dropdownTextMuted: #6C757D,
+ focus: rgb(23 93 220 / 25%),
+ footerBackgroundColor: #FBFBFB,
+ foregroundColor: $white,
+ headerColor: rgba(0,0,0,0.03),
+ iconColor: #777777,
+ iconHover: $body-color,
+ imgFilter: invert(0) grayscale(0),
+ inputBackgroundColor: #FBFBFB,
+ inputBorderColor: $border-color,
+ inputDisabledBackground: #E0E0E0,
+ inputDisabledColor: #6C757D,
+ inputPlaceholderColor: $lightInputPlaceholderColor,
+ inputTextColor: $lightInputColor,
+ layoutFrontendColor: #ECF0F5,
+ learnMoreHover: #104097,
+ linkColor: $primary,
+ linkColorHover: #104097,
+ linkWeight: 400,
+ listItemActive: $text-color,
+ listItemBorder: rgba(0,0,0,.125),
+ listItemColor: $primary,
+ listItemColorHover: #104097,
+ loadingSvg: url('../images/loading.svg'),
+ navActiveBackground: $white,
+ navActiveWeight: 600,
+ navBackground: $primary,
+ navOrgBackgroundColor: #FBFBFB,
+ navWeight: 600,
+ pwLetter: $text-color,
+ pwNumber: #007FDE,
+ pwSpecial: #C40800,
+ pwStrengthBackground: #E9ECEF,
+ registerHeadingColor: $white,
+ separator: $secondary,
+ separatorHr: rgb(0,0,0,0.1),
+ tableColorHover: #333333,
+ tableLinkColor: $primary,
+ tableLinkColorHover: #104097,
+ tableRowHover: rgba(0,0,0,0.03),
+ tableSeparator: #DEE2E6
+ ),
+ dark: (
+ logoSuffix: 'white',
+ primary: $darkPrimary,
+ primaryAlt: $darkPrimary-alt,
+ danger: $darkDanger,
+ success: $darkSuccess,
+ warning: $darkWarning,
+ info: $darkInfo,
+ textColor: $darkGrey1,
+ headingColor: $white,
+ darkTextColor: $darkDarkBlue2,
+ warningTextColor: $darkDarkBlue1,
+ successTextColor: $darkDarkBlue1,
+ dangerTextColor: $white,
+ infoTextColor: $white,
+ textMuted: $darkGrey1,
+ backgroundColor: $darkDarkBlue2,
+ badgeDangerBackground: $darkDanger,
+ badgeDangerText: $darkDarkBlue2,
+ badgeInfoBackground: $darkInfo,
+ badgeInfoText: $darkDarkBlue2,
+ badgePrimaryBackground: $darkLinks,
+ badgePrimaryBackgroundHover: $darkPrimary-alt,
+ badgePrimaryText: $darkDarkBlue2,
+ badgeSecondaryBackground: $darkDarkBlue2,
+ badgeSecondaryText: $white,
+ bgPrimaryColor: $darkPrimary,
+ bgLightColor: $darkDarkBlue2,
+ borderColor: $darkBlue1,
+ borderPrimaryColor: $darkPrimary,
+ btnDanger: $darkDanger,
+ btnDangerHover: $darkDangerHover,
+ btnDangerText: $white,
+ btnLinkText: $darkGrey1,
+ btnLinkTextHover: $white,
+ btnOutlineDangerBackground: $darkDanger,
+ btnOutlineDangerBackgroundHover: $darkDangerHover,
+ btnOutlineDangerBorder: $darkDanger,
+ btnOutlineDangerBorderHover: $darkDangerHover,
+ btnOutlineDangerText: $darkDarkBlue2,
+ btnOutlineDangerTextHover: $darkDarkBlue2,
+ btnOutlinePrimaryBackground: $darkPrimary,
+ btnOutlinePrimaryBackgroundHover: $darkPrimary-alt,
+ btnOutlinePrimaryBorder: $darkPrimary,
+ btnOutlinePrimaryBorderHover: $darkPrimary-alt,
+ btnOutlinePrimaryText: $darkDarkBlue2,
+ btnOutlinePrimaryTextHover: $darkDarkBlue2,
+ btnOutlineSecondaryBackground: transparent,
+ btnOutlineSecondaryBackgroundHover: transparent,
+ btnOutlineSecondaryBorder: $darkGrey1,
+ btnOutlineSecondaryBorderHover: $darkGrey2,
+ btnOutlineSecondaryText: $white,
+ btnOutlineSecondaryTextHover: $darkGrey2,
+ btnPrimary: $darkLinks,
+ btnPrimaryBorderHover: $darkPrimary-alt,
+ btnPrimaryHover: $darkPrimary-alt,
+ btnPrimaryText: $darkDarkBlue2,
+ btnSecondary: transparent,
+ btnSecondaryBorder: $darkGrey1,
+ btnSecondaryBorderHover: $darkGrey2,
+ btnSecondaryHover: transparent,
+ btnSecondaryText: $white,
+ btnSecondaryTextHover: $darkGrey2,
+ calloutBackground: $darkBlue2,
+ calloutColor: $white,
+ cdkDraggingBackground: $darkDarkBlue1,
+ codeColor: #E83E8C,
+ dropdownBackground: $darkDarkBlue1,
+ dropdownDangerHover: $darkDangerHover,
+ dropdownHover: rgba(0,0,0,0.03),
+ dropdownTextColor: $white,
+ dropdownTextMuted: #BEC6CF,
+ focus: rgb(106 153 240 / 25%),
+ footerBackgroundColor: $darkBlue1,
+ foregroundColor: $darkDarkBlue1,
+ headerColor: $darkBlue1,
+ iconColor: #777777,
+ iconHover: $darkGrey2,
+ imgFilter: invert(1) grayscale(1),
+ inputBackgroundColor: $darkDarkBlue1,
+ inputBorderColor: $darkGrey1,
+ inputDisabledBackground: $darkBlue2,
+ inputDisabledColor: $darkGrey1,
+ inputPlaceholderColor: $darkInputPlaceholderColor,
+ inputTextColor: $darkInputColor,
+ layoutFrontendColor: $darkDarkBlue2,
+ learnMoreHover: $darkPrimary-alt,
+ linkColor: $darkLinks,
+ linkColorHover: $darkLinks,
+ linkWeight: 600,
+ listItemActive: $darkPrimary,
+ listItemBorder: $darkBlue1,
+ listItemColor: $white,
+ listItemColorHover: $white,
+ loadingSvg: url('../images/loading-white.svg'),
+ navActiveBackground: $darkDarkBlue1,
+ navActiveWeight: 600,
+ navBackground: $darkDarkBlue1,
+ navOrgBackgroundColor: #161C26,
+ navWeight: 400,
+ pwLetter: $white,
+ pwNumber: #52BDFB,
+ pwSpecial: #FF7C70,
+ pwStrengthBackground: $darkBlue2,
+ registerHeadingColor: $white,
+ separator: $darkBlue1,
+ separatorHr: $darkBlue1,
+ tableColorHover: $darkGrey1,
+ tableLinkColor: $white,
+ tableLinkColorHover: $white,
+ tableRowHover: rgba(0,0,0,0.03),
+ tableSeparator: $darkBlue1
+ ),
+);
+
+@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);
+};
diff --git a/src/services/htmlStorage.service.ts b/src/services/htmlStorage.service.ts
index ae855bc2..d1516987 100644
--- a/src/services/htmlStorage.service.ts
+++ b/src/services/htmlStorage.service.ts
@@ -7,7 +7,7 @@ export class HtmlStorageService implements StorageService {
ConstantsService.disableFaviconKey, 'rememberEmail', 'enableGravatars', 'enableFullWidth',
ConstantsService.localeKey, ConstantsService.autoConfirmFingerprints,
ConstantsService.vaultTimeoutKey, ConstantsService.vaultTimeoutActionKey, ConstantsService.ssoCodeVerifierKey,
- ConstantsService.ssoStateKey, 'ssoOrgIdentifier']);
+ ConstantsService.ssoStateKey, 'ssoOrgIdentifier', ConstantsService.themeKey]);
private localStorageStartsWithKeys = ['twoFactorToken_', ConstantsService.collapsedGroupingsKey + '_'];
private memoryStorageStartsWithKeys = ['ciphers_', 'folders_', 'collections_', 'settings_', 'lastSync_'];
private memoryStorage = new Map
();
diff --git a/src/services/webPlatformUtils.service.ts b/src/services/webPlatformUtils.service.ts
index 2a881600..d7764f1d 100644
--- a/src/services/webPlatformUtils.service.ts
+++ b/src/services/webPlatformUtils.service.ts
@@ -1,19 +1,24 @@
import Swal, { SweetAlertIcon } from 'sweetalert2';
import { DeviceType } from 'jslib-common/enums/deviceType';
+import { ThemeType } from 'jslib-common/enums/themeType';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { LogService } from 'jslib-common/abstractions/log.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
+import { StorageService } from 'jslib-common/abstractions/storage.service';
+
+import { ConstantsService } from 'jslib-common/services/constants.service';
export class WebPlatformUtilsService implements PlatformUtilsService {
identityClientId: string = 'web';
private browserCache: DeviceType = null;
+ private prefersColorSchemeDark = window.matchMedia('(prefers-color-scheme: dark)');
constructor(private i18nService: I18nService, private messagingService: MessagingService,
- private logService: LogService) { }
+ private logService: LogService, private storageService: () => StorageService) { }
getDevice(): DeviceType {
if (this.browserCache != null) {
@@ -283,11 +288,24 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
return false;
}
- getDefaultSystemTheme() {
- return Promise.resolve(null as 'light' | 'dark');
+ getDefaultSystemTheme(): Promise {
+ return Promise.resolve(this.prefersColorSchemeDark.matches ? ThemeType.Dark : ThemeType.Light);
}
- onDefaultSystemThemeChange() {
- /* noop */
+ async getEffectiveTheme(): Promise {
+ const theme = await this.storageService().get(ConstantsService.themeKey);
+ if (theme === ThemeType.Dark) {
+ return ThemeType.Dark;
+ } else if (theme === ThemeType.System) {
+ return this.getDefaultSystemTheme();
+ } else {
+ return ThemeType.Light;
+ }
+ }
+
+ onDefaultSystemThemeChange(callback: ((theme: ThemeType.Light | ThemeType.Dark) => unknown)) {
+ this.prefersColorSchemeDark.addEventListener('change', ({ matches }) => {
+ callback(matches ? ThemeType.Dark : ThemeType.Light);
+ });
}
}
diff --git a/webpack.config.js b/webpack.config.js
index 094a1d45..857685e5 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: {