Various Dark Theme fixes per QA feedback (#1212)

* Fix CORS issue on in-line theming javascript

* Fix date picker icon color

* Add comment

* Fix table theming in dark mode

* Selfhosted navbar fix

* Rename selector to avoid clashing with bootstrap

* Do not set initial theme if default

* Fix .text-danger style in dropdown lists

* Fix toast style, restructure toast and card scss

* Fix table and dropdown list hover color

* Use callout component for Disable Send warning

* Remove unneeded theming for hovering over links

* Undo changes to register enterprise2 layout

* Apply theming to Safari input field icons

e.g. Caps lock, password autofill

* Selectively apply themed logo CSS

* Fix unrelated linting

* Fix webpack config to bundle theme.js

Co-authored-by: Danny Murphy <6512845+dltmurphy@users.noreply.github.com>
This commit is contained in:
Thomas Rittson 2021-10-05 20:03:24 +10:00 committed by GitHub
parent 0c02cfea2f
commit 7a43510cf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 235 additions and 235 deletions

View File

@ -1,6 +1,6 @@
<div class="mt-5 d-flex justify-content-center" *ngIf="loading"> <div class="mt-5 d-flex justify-content-center" *ngIf="loading">
<div> <div>
<img class="mb-4 logo" alt="Bitwarden"> <img class="mb-4 logo logo-themed" alt="Bitwarden">
<p class="text-center"> <p class="text-center">
<i class="fa fa-spinner fa-spin fa-2x text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i> <i class="fa fa-spinner fa-spin fa-2x text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span> <span class="sr-only">{{'loading' | i18n}}</span>

View File

@ -1,6 +1,6 @@
<div class="mt-5 d-flex justify-content-center" *ngIf="loading"> <div class="mt-5 d-flex justify-content-center" *ngIf="loading">
<div> <div>
<img class="mb-4 logo" alt="Bitwarden"> <img class="mb-4 logo logo-themed" alt="Bitwarden">
<p class="text-center"> <p class="text-center">
<i class="fa fa-spinner fa-spin fa-2x text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i> <i class="fa fa-spinner fa-spin fa-2x text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span> <span class="sr-only">{{'loading' | i18n}}</span>

14
package-lock.json generated
View File

@ -42,6 +42,7 @@
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"gh-pages": "^3.1.0", "gh-pages": "^3.1.0",
"html-loader": "^1.3.2", "html-loader": "^1.3.2",
"html-webpack-injector": "1.1.4",
"html-webpack-plugin": "^4.5.1", "html-webpack-plugin": "^4.5.1",
"mini-css-extract-plugin": "^1.5.0", "mini-css-extract-plugin": "^1.5.0",
"sass": "^1.32.10", "sass": "^1.32.10",
@ -89,6 +90,7 @@
} }
}, },
"jslib/common": { "jslib/common": {
"name": "@bitwarden/jslib-common",
"version": "0.0.0", "version": "0.0.0",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
@ -4785,6 +4787,12 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/html-webpack-injector": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/html-webpack-injector/-/html-webpack-injector-1.1.4.tgz",
"integrity": "sha512-R+HeAYzPeL3dKIr5/a7a2S6R4fy2yHetKiB7cz5rXjwlnU5tghuy58kCBsKA/Qoj94MAgCYwllHmvYqy2nJSdg==",
"dev": true
},
"node_modules/html-webpack-plugin": { "node_modules/html-webpack-plugin": {
"version": "4.5.2", "version": "4.5.2",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz",
@ -15299,6 +15307,12 @@
} }
} }
}, },
"html-webpack-injector": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/html-webpack-injector/-/html-webpack-injector-1.1.4.tgz",
"integrity": "sha512-R+HeAYzPeL3dKIr5/a7a2S6R4fy2yHetKiB7cz5rXjwlnU5tghuy58kCBsKA/Qoj94MAgCYwllHmvYqy2nJSdg==",
"dev": true
},
"html-webpack-plugin": { "html-webpack-plugin": {
"version": "4.5.2", "version": "4.5.2",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz",

View File

@ -47,6 +47,7 @@
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"gh-pages": "^3.1.0", "gh-pages": "^3.1.0",
"html-loader": "^1.3.2", "html-loader": "^1.3.2",
"html-webpack-injector": "1.1.4",
"html-webpack-plugin": "^4.5.1", "html-webpack-plugin": "^4.5.1",
"mini-css-extract-plugin": "^1.5.0", "mini-css-extract-plugin": "^1.5.0",
"sass": "^1.32.10", "sass": "^1.32.10",

View File

@ -1,6 +1,6 @@
<div class="mt-5 d-flex justify-content-center" *ngIf="loading"> <div class="mt-5 d-flex justify-content-center" *ngIf="loading">
<div> <div>
<img class="mb-4 logo" alt="Bitwarden"> <img class="mb-4 logo logo-themed" alt="Bitwarden">
<p class="text-center"> <p class="text-center">
<i class="fa fa-spinner fa-spin fa-2x text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i> <i class="fa fa-spinner fa-spin fa-2x text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span> <span class="sr-only">{{'loading' | i18n}}</span>

View File

@ -1,7 +1,7 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" class="container" ngNativeValidate> <form #form (ngSubmit)="submit()" [appApiAction]="formPromise" class="container" ngNativeValidate>
<div class="row justify-content-md-center mt-5"> <div class="row justify-content-md-center mt-5">
<div class="col-5"> <div class="col-5">
<img class="mb-2 logo" alt="Bitwarden"> <img class="mb-2 logo logo-themed" alt="Bitwarden">
<p class="lead text-center mx-4 mb-4">{{'loginOrCreateNewAccount' | i18n}}</p> <p class="lead text-center mx-4 mb-4">{{'loginOrCreateNewAccount' | i18n}}</p>
<div class="card d-block"> <div class="card d-block">
<div class="card-body"> <div class="card-body">

View File

@ -1,7 +1,7 @@
<form #form (ngSubmit)="submit()" class="container" [appApiAction]="initiateSsoFormPromise" ngNativeValidate> <form #form (ngSubmit)="submit()" class="container" [appApiAction]="initiateSsoFormPromise" ngNativeValidate>
<div class="row justify-content-md-center mt-5"> <div class="row justify-content-md-center mt-5">
<div class="col-5"> <div class="col-5">
<img class="logo mb-2" alt="Bitwarden"> <img class="logo mb-2 logo-themed" alt="Bitwarden">
<div class="card d-block mt-4"> <div class="card d-block mt-4">
<div class="card-body" *ngIf="loggingIn"> <div class="card-body" *ngIf="loggingIn">
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i> <i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>

View File

@ -1,6 +1,6 @@
<div class="mt-5 d-flex justify-content-center"> <div class="mt-5 d-flex justify-content-center">
<div> <div>
<img class="mb-4 logo" alt="Bitwarden"> <img class="mb-4 logo logo-themed" alt="Bitwarden">
<p class="text-center"> <p class="text-center">
<i class="fa fa-spinner fa-spin fa-2x text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i> <i class="fa fa-spinner fa-spin fa-2x text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
<span class="sr-only">{{'loading' | i18n}}</span> <span class="sr-only">{{'loading' | i18n}}</span>

View File

@ -1,4 +1,4 @@
<nav class="navbar navbar-expand navbar-dark" [ngClass]="{'bg-secondary-alt': selfHosted}"> <nav class="navbar navbar-expand navbar-dark" [ngClass]="{'nav-background-alt': selfHosted}">
<div class="container"> <div class="container">
<a class="navbar-brand" routerLink="/" appA11yTitle="{{'pageTitle' | i18n : 'Bitwarden'}}"> <a class="navbar-brand" routerLink="/" appA11yTitle="{{'pageTitle' | i18n : 'Bitwarden'}}">
<i class="fa fa-shield" aria-hidden="true"></i> <i class="fa fa-shield" aria-hidden="true"></i>

View File

@ -1,12 +1,7 @@
<div class="container page-content"> <div class="container page-content">
<div class="row card border-warning mb-4" *ngIf="disableSend"> <app-callout type="warning" title="{{'sendDisabled' | i18n}}" *ngIf="disableSend">
<div class="card-header bg-warning text-white"> <span>{{'sendDisabledWarning' | i18n}}</span>
<i class="fa fa-warning fa-fw" aria-hidden="true"></i> {{'sendDisabled' | i18n}} </app-callout>
</div>
<div class="card-body">
<span>{{'sendDisabledWarning' | i18n}}</span>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-3 groupings"> <div class="col-3 groupings">
<div class="card vault-filters"> <div class="card vault-filters">

View File

@ -7,8 +7,8 @@ import {
AddEditCustomFieldsComponent as BaseAddEditCustomFieldsComponent AddEditCustomFieldsComponent as BaseAddEditCustomFieldsComponent
} from 'jslib-angular/components/add-edit-custom-fields.component'; } from 'jslib-angular/components/add-edit-custom-fields.component';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { EventService } from 'jslib-common/abstractions/event.service'; import { EventService } from 'jslib-common/abstractions/event.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
@Component({ @Component({
selector: 'app-vault-add-edit-custom-fields', selector: 'app-vault-add-edit-custom-fields',

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html class="theme_light">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
@ -8,23 +8,6 @@
<title page-title>Bitwarden Web Vault</title> <title page-title>Bitwarden Web Vault</title>
<script>
(function () {
// Set theme on page load
// This is done outside the Angular app to avoid a flash of unthemed content before it loads
let theme = window.localStorage.getItem('theme');
if (theme?.indexOf('system') > -1) {
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
} else if (theme?.indexOf('dark') > -1) {
theme = 'dark';
}
else {
theme = 'light';
}
document.documentElement.classList.add('theme_' + theme);
})();
</script>
<link rel="apple-touch-icon" sizes="180x180" href="images/icons/apple-touch-icon.png"> <link rel="apple-touch-icon" sizes="180x180" href="images/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="images/icons/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="images/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="images/icons/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="16x16" href="images/icons/favicon-16x16.png">
@ -36,7 +19,7 @@
<app-root> <app-root>
<div class="mt-5 d-flex justify-content-center"> <div class="mt-5 d-flex justify-content-center">
<div> <div>
<img class="mb-4 logo" alt="Bitwarden"> <img class="mb-4 logo logo-themed" alt="Bitwarden">
<p class="text-center"> <p class="text-center">
<i class="fa fa-spinner fa-spin fa-2x text-muted" title="Loading" aria-hidden="true"></i> <i class="fa fa-spinner fa-spin fa-2x text-muted" title="Loading" aria-hidden="true"></i>
</p> </p>

View File

@ -64,8 +64,11 @@ img.logo {
height: 43px; height: 43px;
margin: 0 auto; margin: 0 auto;
width: 284px; width: 284px;
@include themify($themes) {
content: url('../images/logo-' + themed('logoSuffix') + '@2x.png'); &.logo-themed {
@include themify($themes) {
content: url('../images/logo-' + themed('logoSuffix') + '@2x.png');
}
} }
} }
@ -129,12 +132,6 @@ a {
} }
} }
a:hover {
@include themify($themes) {
color: themed('linkColorHover');
}
}
code { code {
@include themify($themes) { @include themify($themes) {
color: themed('codeColor'); color: themed('codeColor');
@ -177,6 +174,34 @@ code {
} }
} }
.bg-success {
@include themify($themes) {
background-color: themed('success') !important;
color: themed('successTextColor') !important;
}
}
.bg-warning {
@include themify($themes) {
background-color: themed('warning') !important;
color: themed('warningTextColor') !important;
}
}
.bg-error, .bg-danger {
@include themify($themes) {
background-color: themed('danger') !important;
color: themed('dangerTextColor') !important;
}
}
.bg-info {
@include themify($themes) {
background-color: themed('info') !important;
color: themed('infoTextColor') !important;
}
}
.border-primary { .border-primary {
@include themify($themes) { @include themify($themes) {
border-color: themed('borderPrimaryColor') !important; border-color: themed('borderPrimaryColor') !important;

View File

@ -154,11 +154,11 @@
.fa-spinner { .fa-spinner {
align-items: center; align-items: center;
bottom: 0;
display: none; display: none;
justify-content: center; justify-content: center;
position: absolute;
bottom: 0;
left: 0; left: 0;
position: absolute;
right: 0; right: 0;
top: 0; top: 0;
} }

View File

@ -1,8 +1,8 @@
@import 'variables'; @import 'variables';
:export { :export {
lightInputColor: $lightInputColor;
lightInputPlaceholderColor: $lightInputPlaceholderColor;
darkInputColor: $darkInputColor; darkInputColor: $darkInputColor;
darkInputPlaceholderColor: $darkInputPlaceholderColor; darkInputPlaceholderColor: $darkInputPlaceholderColor;
lightInputColor: $lightInputColor;
lightInputPlaceholderColor: $lightInputPlaceholderColor;
} }

View File

@ -118,23 +118,18 @@ input[type="radio"], input[type="checkbox"] {
} }
.dropdown-item { .dropdown-item {
&.text-danger { @include themify($themes) {
color: #FFFFFF !important; color: themed('dropdownTextColor');
@include themify($themes) { }
background-color: themed('danger');
}
&:hover { &.text-danger {
color: #FFFFFF !important; @include themify($themes) {
@include themify($themes){ color: themed('danger') !important;
background-color: themed('dropdownDangerHover') !important;
}
} }
} }
&:hover:not(.text-danger) { &:hover {
@include themify($themes) { @include themify($themes) {
background-color: themed('dropdownHover'); background-color: themed('dropdownHover');
color: themed('dropdownTextColor');
} }
} }
&:active{ &:active{
@ -162,11 +157,6 @@ input[type="radio"], input[type="checkbox"] {
color: themed('listItemColor'); color: themed('listItemColor');
font-weight: themed('linkWeight'); font-weight: themed('linkWeight');
} }
&:hover {
@include themify($themes) {
color: themed('listItemColorHover');
}
}
} }
.list-group-item.active { .list-group-item.active {
@ -178,3 +168,10 @@ input[type="radio"], input[type="checkbox"] {
color: themed('listItemActive'); color: themed('listItemActive');
} }
} }
// Browser specific icons overlayed on input fields. e.g. caps lock indicator on password field
::-webkit-calendar-picker-indicator, input::-webkit-caps-lock-indicator, input::-webkit-credentials-auto-fill-button {
@include themify($themes) {
filter: themed('browserInputIconsFilter');
}
}

View File

@ -2,7 +2,13 @@
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
@include themify($themes) { @include themify($themes) {
background-color: themed('navBackground') !important; background-color: themed('navBackground');
}
&.nav-background-alt {
@include themify($themes) {
background-color: themed('navBackgroundAlt');
}
} }
.dropdown-menu { .dropdown-menu {
@ -106,4 +112,3 @@
} }
} }
} }

View File

@ -160,124 +160,65 @@ app-user-billing {
} }
/* Register Layout Page - Exempt from themify */ /* Register Layout Page */
body.theme_light_force { .layout {
background-color: #ECF0F5; &.enterprise2 {
a, .btn-link { header {
color: #175DDC; background: #175DDC;
&:hover { color: #CED4DA;
color: #104097;
}
}
.btn-outline-secondary { &:before {
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; background: #175DDC;
color: #CED4DA; content: "";
height: 416px;
left: 0;
position: absolute;
top: -76px;
transform: skewY(-3deg);
width: 100%;
z-index: -1;
}
img.logo {
height: 57px;
margin: 12px 0 0;
max-width: 284px;
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 { &:before {
background: #175DDC; color: #1252A3;
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 { b {
color: #FFFFFF; &:after {
font-size: 1.8rem; content: "";
margin: 100px 0 150px 0; font-size: 2rem;
} padding-left: 6px;
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 { blockquote {
font-size: 1.4rem; font-size: 1.4rem;
margin: 20px 0 0 0; margin: 20px 0 0 0;
padding-right: 40px; padding-right: 40px;
}
} }
} }
} }

View File

@ -129,15 +129,15 @@
} }
.swal2-icon { .swal2-icon {
border: none;
height: auto;
margin: 0 auto; margin: 0 auto;
width: auto; width: auto;
height: auto;
border: none;
} }
.swal2-content { .swal2-content {
padding-bottom: 15px;
font-size: $font-size-base; font-size: $font-size-base;
padding-bottom: 15px;
@include themify($themes) { @include themify($themes) {
border-bottom: $modal-footer-border-width solid themed('separator'); border-bottom: $modal-footer-border-width solid themed('separator');
} }
@ -145,8 +145,8 @@
i.swal-custom-icon { i.swal-custom-icon {
display: block; display: block;
margin: 0 auto;
font-size: 35px; font-size: 35px;
margin: 0 auto;
} }
.swal2-title { .swal2-title {

View File

@ -1,26 +1,8 @@
.table.table-list { .table {
@include themify($themes) { @include themify($themes) {
color: themed('textColor'); 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 { td {
vertical-align: middle; vertical-align: middle;
@include themify($themes) { @include themify($themes) {
@ -113,6 +95,26 @@
color: themed('textMuted'); color: themed('textMuted');
} }
} }
&.table-list {
&.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;
}
}
}
} }
.table-hover tbody tr:hover { .table-hover tbody tr:hover {

View File

@ -49,6 +49,15 @@
&.toast-danger, &.toast-error { &.toast-danger, &.toast-error {
background-image: none !important; background-image: none !important;
@include themify($themes) {
background-color: themed('danger');
}
&, &:before, & .toast-close-button {
@include themify($themes) {
color: themed('dangerTextColor') !important;
}
}
&:before { &:before {
content: "\f0e7"; content: "\f0e7";
@ -57,6 +66,15 @@
&.toast-warning { &.toast-warning {
background-image: none !important; background-image: none !important;
@include themify($themes) {
background-color: themed('warning');
}
&, &:before, & .toast-close-button {
@include themify($themes) {
color: themed('warningTextColor') !important;
}
}
&:before { &:before {
content: "\f071"; content: "\f071";
@ -65,6 +83,15 @@
&.toast-info { &.toast-info {
background-image: none !important; background-image: none !important;
@include themify($themes) {
background-color: themed('info');
}
&, &:before, & .toast-close-button {
@include themify($themes) {
color: themed('infoTextColor') !important;
}
}
&:before { &:before {
content: "\f05a"; content: "\f05a";
@ -73,6 +100,15 @@
&.toast-success { &.toast-success {
background-image: none !important; background-image: none !important;
@include themify($themes) {
background-color: themed('success');
}
&, &:before, & .toast-close-button {
@include themify($themes) {
color: themed('successTextColor') !important;
}
}
&:before { &:before {
content: "\f00C"; content: "\f00C";
@ -80,31 +116,3 @@
} }
} }
} }
.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;
}
}

View File

@ -200,6 +200,7 @@ $themes: (
navActiveBackground: $white, navActiveBackground: $white,
navActiveWeight: 600, navActiveWeight: 600,
navBackground: $primary, navBackground: $primary,
navBackgroundAlt: $secondary-alt,
navOrgBackgroundColor: #FBFBFB, navOrgBackgroundColor: #FBFBFB,
navWeight: 600, navWeight: 600,
pwLetter: $text-color, pwLetter: $text-color,
@ -213,7 +214,8 @@ $themes: (
tableLinkColor: $primary, tableLinkColor: $primary,
tableLinkColorHover: #104097, tableLinkColorHover: #104097,
tableRowHover: rgba(0,0,0,0.03), tableRowHover: rgba(0,0,0,0.03),
tableSeparator: #DEE2E6 tableSeparator: #DEE2E6,
browserInputIconsFilter: invert(0),
), ),
dark: ( dark: (
logoSuffix: 'white', logoSuffix: 'white',
@ -226,10 +228,10 @@ $themes: (
textColor: $darkGrey1, textColor: $darkGrey1,
headingColor: $white, headingColor: $white,
darkTextColor: $darkDarkBlue2, darkTextColor: $darkDarkBlue2,
warningTextColor: $darkDarkBlue1, warningTextColor: $darkDarkBlue2,
successTextColor: $darkDarkBlue1, successTextColor: $darkDarkBlue2,
dangerTextColor: $white, dangerTextColor: $darkDarkBlue2,
infoTextColor: $white, infoTextColor: $darkDarkBlue2,
textMuted: $darkGrey1, textMuted: $darkGrey1,
backgroundColor: $darkDarkBlue2, backgroundColor: $darkDarkBlue2,
badgeDangerBackground: $darkDanger, badgeDangerBackground: $darkDanger,
@ -247,7 +249,7 @@ $themes: (
borderPrimaryColor: $darkPrimary, borderPrimaryColor: $darkPrimary,
btnDanger: $darkDanger, btnDanger: $darkDanger,
btnDangerHover: $darkDangerHover, btnDangerHover: $darkDangerHover,
btnDangerText: $white, btnDangerText: $darkDarkBlue2,
btnLinkText: $darkGrey1, btnLinkText: $darkGrey1,
btnLinkTextHover: $white, btnLinkTextHover: $white,
btnOutlineDangerBackground: $darkDanger, btnOutlineDangerBackground: $darkDanger,
@ -284,7 +286,7 @@ $themes: (
codeColor: #E83E8C, codeColor: #E83E8C,
dropdownBackground: $darkDarkBlue1, dropdownBackground: $darkDarkBlue1,
dropdownDangerHover: $darkDangerHover, dropdownDangerHover: $darkDangerHover,
dropdownHover: rgba(0,0,0,0.03), dropdownHover: rgba(255, 255, 255, 0.03),
dropdownTextColor: $white, dropdownTextColor: $white,
dropdownTextMuted: #BEC6CF, dropdownTextMuted: #BEC6CF,
focus: rgb(106 153 240 / 25%), focus: rgb(106 153 240 / 25%),
@ -313,6 +315,7 @@ $themes: (
navActiveBackground: $darkDarkBlue1, navActiveBackground: $darkDarkBlue1,
navActiveWeight: 600, navActiveWeight: 600,
navBackground: $darkDarkBlue1, navBackground: $darkDarkBlue1,
navBackgroundAlt: $darkDarkBlue1,
navOrgBackgroundColor: #161C26, navOrgBackgroundColor: #161C26,
navWeight: 400, navWeight: 400,
pwLetter: $white, pwLetter: $white,
@ -325,8 +328,9 @@ $themes: (
tableColorHover: $darkGrey1, tableColorHover: $darkGrey1,
tableLinkColor: $white, tableLinkColor: $white,
tableLinkColorHover: $white, tableLinkColorHover: $white,
tableRowHover: rgba(0,0,0,0.03), tableRowHover: rgba(255, 255, 255, 0.03),
tableSeparator: $darkBlue1 tableSeparator: $darkBlue1,
browserInputIconsFilter: invert(1),
), ),
); );

22
src/theme.js Normal file
View File

@ -0,0 +1,22 @@
// Set theme on page load
// This is done outside the Angular app to avoid a flash of unthemed content before it loads
// The defaultTheme is also set in the html itself to make sure that some theming is always applied
(function () {
const defaultTheme = 'light'
const htmlEl = document.documentElement;
let theme = defaultTheme;
const savedTheme = window.localStorage.getItem('theme');
if (savedTheme != null) {
if (savedTheme.indexOf('system') > -1) {
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
} else if (savedTheme.indexOf('dark') > -1) {
theme = 'dark';
}
}
if (!htmlEl.classList.contains('theme_' + theme)) {
htmlEl.classList.remove('theme_' + defaultTheme);
htmlEl.classList.add('theme_' + theme);
}
})();

View File

@ -3,6 +3,7 @@ const fs = require('fs');
const webpack = require('webpack'); const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackInjector = require('html-webpack-injector');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin');
@ -80,8 +81,9 @@ const plugins = [
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
template: './src/index.html', template: './src/index.html',
filename: 'index.html', filename: 'index.html',
chunks: ['app/polyfills', 'app/vendor', 'app/main'], chunks: ['theme_head', 'app/polyfills', 'app/vendor', 'app/main'],
}), }),
new HtmlWebpackInjector(),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
template: './src/connectors/duo.html', template: './src/connectors/duo.html',
filename: 'duo-connector.html', filename: 'duo-connector.html',
@ -222,6 +224,7 @@ const webpackConfig = {
'connectors/duo': './src/connectors/duo.ts', 'connectors/duo': './src/connectors/duo.ts',
'connectors/sso': './src/connectors/sso.ts', 'connectors/sso': './src/connectors/sso.ts',
'connectors/captcha': './src/connectors/captcha.ts', 'connectors/captcha': './src/connectors/captcha.ts',
'theme_head': './src/theme.js',
}, },
externals: { externals: {
'u2f': 'u2f', 'u2f': 'u2f',