-
+
diff --git a/src/scss/base.scss b/src/scss/base.scss
index b4ba0a1635..f7bad93b6d 100644
--- a/src/scss/base.scss
+++ b/src/scss/base.scss
@@ -64,8 +64,11 @@ img.logo {
height: 43px;
margin: 0 auto;
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 {
@include themify($themes) {
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 {
@include themify($themes) {
border-color: themed('borderPrimaryColor') !important;
diff --git a/src/scss/buttons.scss b/src/scss/buttons.scss
index 74ab57716c..ab54971017 100644
--- a/src/scss/buttons.scss
+++ b/src/scss/buttons.scss
@@ -154,11 +154,11 @@
.fa-spinner {
align-items: center;
+ bottom: 0;
display: none;
justify-content: center;
- position: absolute;
- bottom: 0;
left: 0;
+ position: absolute;
right: 0;
top: 0;
}
diff --git a/src/scss/export.module.scss b/src/scss/export.module.scss
index f727344c19..b40eb690db 100644
--- a/src/scss/export.module.scss
+++ b/src/scss/export.module.scss
@@ -1,8 +1,8 @@
@import 'variables';
:export {
- lightInputColor: $lightInputColor;
- lightInputPlaceholderColor: $lightInputPlaceholderColor;
darkInputColor: $darkInputColor;
darkInputPlaceholderColor: $darkInputPlaceholderColor;
+ lightInputColor: $lightInputColor;
+ lightInputPlaceholderColor: $lightInputPlaceholderColor;
}
diff --git a/src/scss/forms.scss b/src/scss/forms.scss
index 5d372aafa6..379efc67c5 100644
--- a/src/scss/forms.scss
+++ b/src/scss/forms.scss
@@ -118,23 +118,18 @@ input[type="radio"], input[type="checkbox"] {
}
.dropdown-item {
- &.text-danger {
- color: #FFFFFF !important;
- @include themify($themes) {
- background-color: themed('danger');
- }
+ @include themify($themes) {
+ color: themed('dropdownTextColor');
+ }
- &:hover {
- color: #FFFFFF !important;
- @include themify($themes){
- background-color: themed('dropdownDangerHover') !important;
- }
+ &.text-danger {
+ @include themify($themes) {
+ color: themed('danger') !important;
}
}
- &:hover:not(.text-danger) {
+ &:hover {
@include themify($themes) {
background-color: themed('dropdownHover');
- color: themed('dropdownTextColor');
}
}
&:active{
@@ -162,11 +157,6 @@ input[type="radio"], input[type="checkbox"] {
color: themed('listItemColor');
font-weight: themed('linkWeight');
}
- &:hover {
- @include themify($themes) {
- color: themed('listItemColorHover');
- }
- }
}
.list-group-item.active {
@@ -178,3 +168,10 @@ input[type="radio"], input[type="checkbox"] {
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');
+ }
+}
diff --git a/src/scss/navigation.scss b/src/scss/navigation.scss
index 230cdbef17..89d9e85adc 100644
--- a/src/scss/navigation.scss
+++ b/src/scss/navigation.scss
@@ -2,7 +2,13 @@
padding-left: 0;
padding-right: 0;
@include themify($themes) {
- background-color: themed('navBackground') !important;
+ background-color: themed('navBackground');
+ }
+
+ &.nav-background-alt {
+ @include themify($themes) {
+ background-color: themed('navBackgroundAlt');
+ }
}
.dropdown-menu {
@@ -106,4 +112,3 @@
}
}
}
-
diff --git a/src/scss/pages.scss b/src/scss/pages.scss
index aa19235cf2..610cc6e404 100644
--- a/src/scss/pages.scss
+++ b/src/scss/pages.scss
@@ -160,124 +160,65 @@ app-user-billing {
}
-/* Register Layout Page - Exempt from themify */
-body.theme_light_force {
- background-color: #ECF0F5;
- a, .btn-link {
- color: #175DDC;
- &:hover {
- color: #104097;
- }
- }
+/* Register Layout Page */
+.layout {
+ &.enterprise2 {
+ header {
+ background: #175DDC;
+ color: #CED4DA;
- .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 {
+ &:before {
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 {
- 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;
+ color: #1252A3;
}
}
- 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;
- }
+ b {
+ &:after {
+ content: "⟶";
+ font-size: 2rem;
+ padding-left: 6px;
}
}
+ }
- blockquote {
- font-size: 1.4rem;
- margin: 20px 0 0 0;
- padding-right: 40px;
- }
+ 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 5415f801fc..fd953646c1 100644
--- a/src/scss/plugins.scss
+++ b/src/scss/plugins.scss
@@ -129,15 +129,15 @@
}
.swal2-icon {
+ border: none;
+ height: auto;
margin: 0 auto;
width: auto;
- height: auto;
- border: none;
}
.swal2-content {
- padding-bottom: 15px;
font-size: $font-size-base;
+ padding-bottom: 15px;
@include themify($themes) {
border-bottom: $modal-footer-border-width solid themed('separator');
}
@@ -145,8 +145,8 @@
i.swal-custom-icon {
display: block;
- margin: 0 auto;
font-size: 35px;
+ margin: 0 auto;
}
.swal2-title {
diff --git a/src/scss/tables.scss b/src/scss/tables.scss
index cb4fe22022..b796cfdee3 100644
--- a/src/scss/tables.scss
+++ b/src/scss/tables.scss
@@ -1,26 +1,8 @@
-.table.table-list {
+.table {
@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) {
@@ -113,6 +95,26 @@
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 {
diff --git a/src/scss/toasts.scss b/src/scss/toasts.scss
index 62adbcea28..be6ffce94c 100644
--- a/src/scss/toasts.scss
+++ b/src/scss/toasts.scss
@@ -49,6 +49,15 @@
&.toast-danger, &.toast-error {
background-image: none !important;
+ @include themify($themes) {
+ background-color: themed('danger');
+ }
+
+ &, &:before, & .toast-close-button {
+ @include themify($themes) {
+ color: themed('dangerTextColor') !important;
+ }
+ }
&:before {
content: "\f0e7";
@@ -57,6 +66,15 @@
&.toast-warning {
background-image: none !important;
+ @include themify($themes) {
+ background-color: themed('warning');
+ }
+
+ &, &:before, & .toast-close-button {
+ @include themify($themes) {
+ color: themed('warningTextColor') !important;
+ }
+ }
&:before {
content: "\f071";
@@ -65,6 +83,15 @@
&.toast-info {
background-image: none !important;
+ @include themify($themes) {
+ background-color: themed('info');
+ }
+
+ &, &:before, & .toast-close-button {
+ @include themify($themes) {
+ color: themed('infoTextColor') !important;
+ }
+ }
&:before {
content: "\f05a";
@@ -73,6 +100,15 @@
&.toast-success {
background-image: none !important;
+ @include themify($themes) {
+ background-color: themed('success');
+ }
+
+ &, &:before, & .toast-close-button {
+ @include themify($themes) {
+ color: themed('successTextColor') !important;
+ }
+ }
&:before {
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;
- }
-}
diff --git a/src/scss/variables.scss b/src/scss/variables.scss
index 34947251f9..8e375a4eb1 100644
--- a/src/scss/variables.scss
+++ b/src/scss/variables.scss
@@ -200,6 +200,7 @@ $themes: (
navActiveBackground: $white,
navActiveWeight: 600,
navBackground: $primary,
+ navBackgroundAlt: $secondary-alt,
navOrgBackgroundColor: #FBFBFB,
navWeight: 600,
pwLetter: $text-color,
@@ -213,7 +214,8 @@ $themes: (
tableLinkColor: $primary,
tableLinkColorHover: #104097,
tableRowHover: rgba(0,0,0,0.03),
- tableSeparator: #DEE2E6
+ tableSeparator: #DEE2E6,
+ browserInputIconsFilter: invert(0),
),
dark: (
logoSuffix: 'white',
@@ -226,10 +228,10 @@ $themes: (
textColor: $darkGrey1,
headingColor: $white,
darkTextColor: $darkDarkBlue2,
- warningTextColor: $darkDarkBlue1,
- successTextColor: $darkDarkBlue1,
- dangerTextColor: $white,
- infoTextColor: $white,
+ warningTextColor: $darkDarkBlue2,
+ successTextColor: $darkDarkBlue2,
+ dangerTextColor: $darkDarkBlue2,
+ infoTextColor: $darkDarkBlue2,
textMuted: $darkGrey1,
backgroundColor: $darkDarkBlue2,
badgeDangerBackground: $darkDanger,
@@ -247,7 +249,7 @@ $themes: (
borderPrimaryColor: $darkPrimary,
btnDanger: $darkDanger,
btnDangerHover: $darkDangerHover,
- btnDangerText: $white,
+ btnDangerText: $darkDarkBlue2,
btnLinkText: $darkGrey1,
btnLinkTextHover: $white,
btnOutlineDangerBackground: $darkDanger,
@@ -284,7 +286,7 @@ $themes: (
codeColor: #E83E8C,
dropdownBackground: $darkDarkBlue1,
dropdownDangerHover: $darkDangerHover,
- dropdownHover: rgba(0,0,0,0.03),
+ dropdownHover: rgba(255, 255, 255, 0.03),
dropdownTextColor: $white,
dropdownTextMuted: #BEC6CF,
focus: rgb(106 153 240 / 25%),
@@ -313,6 +315,7 @@ $themes: (
navActiveBackground: $darkDarkBlue1,
navActiveWeight: 600,
navBackground: $darkDarkBlue1,
+ navBackgroundAlt: $darkDarkBlue1,
navOrgBackgroundColor: #161C26,
navWeight: 400,
pwLetter: $white,
@@ -325,8 +328,9 @@ $themes: (
tableColorHover: $darkGrey1,
tableLinkColor: $white,
tableLinkColorHover: $white,
- tableRowHover: rgba(0,0,0,0.03),
- tableSeparator: $darkBlue1
+ tableRowHover: rgba(255, 255, 255, 0.03),
+ tableSeparator: $darkBlue1,
+ browserInputIconsFilter: invert(1),
),
);
diff --git a/src/theme.js b/src/theme.js
new file mode 100644
index 0000000000..9e0aa4078c
--- /dev/null
+++ b/src/theme.js
@@ -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);
+ }
+})();
diff --git a/webpack.config.js b/webpack.config.js
index 857685e5ab..ddd922b73d 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,6 +3,7 @@ const fs = require('fs');
const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
+const HtmlWebpackInjector = require('html-webpack-injector');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
@@ -80,8 +81,9 @@ const plugins = [
new HtmlWebpackPlugin({
template: './src/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({
template: './src/connectors/duo.html',
filename: 'duo-connector.html',
@@ -222,6 +224,7 @@ const webpackConfig = {
'connectors/duo': './src/connectors/duo.ts',
'connectors/sso': './src/connectors/sso.ts',
'connectors/captcha': './src/connectors/captcha.ts',
+ 'theme_head': './src/theme.js',
},
externals: {
'u2f': 'u2f',