From 8d8f21368d0366b26d18d8befe50bb6709a2350b Mon Sep 17 00:00:00 2001
From: Richard Wei <54336863+WaysonWei@users.noreply.github.com>
Date: Wed, 8 Sep 2021 11:06:18 +1200
Subject: [PATCH] feat(frontend): dark and high contrast theme supported EE-909
 (#5353)

* feat dark theme & high contrast theme supported
---
 api/http/handler/users/user_update.go         |   5 +
 api/portainer.go                              |   2 +
 app/assets/css/app.css                        | 134 ++--
 app/assets/css/index.js                       |   2 +
 app/assets/css/rdash.css                      |  21 +-
 app/assets/css/theme.css                      | 576 ++++++++++++++++++
 app/assets/css/vendor-override.css            | 399 ++++++++++++
 .../containerNetworksDatatable.html           |   6 +-
 .../networks-datatable/networksDatatable.html |   2 +-
 .../serviceTasksDatatable.html                |   6 +-
 .../applicationsPortsDatatable.html           |  12 +-
 .../applicationsStacksDatatable.html          |  10 +-
 .../placements-datatable/template.html        |  28 +-
 .../ingresses-datatable/template.html         |  10 +-
 app/kubernetes/views/summary/summary.html     |   2 +-
 .../volumes-storages-datatable/template.html  |  10 +-
 .../components/box-selector/box-selector.css  |  13 +-
 .../customTemplatesList.html                  |   2 +-
 .../components/datatables/datatable.css       |  19 +-
 app/portainer/components/sidebar/sidebar.css  |  20 +-
 .../template-list/templateList.html           |   2 +-
 .../theme/theme-settings.controller.js        |  70 +++
 .../components/theme/theme-settings.html      |  19 +
 .../components/theme/theme-settings.js        |   7 +
 app/portainer/models/user.js                  |   1 +
 app/portainer/rest/user.js                    |   1 +
 app/portainer/services/api/userService.js     |   4 +
 app/portainer/services/authentication.js      |  12 +-
 app/portainer/services/stateManager.js        |   5 +
 app/portainer/services/themeManager.js        |  23 +
 app/portainer/views/account/account.html      |   1 +
 .../views/account/accountController.js        |  29 +-
 .../views/logout/logoutController.js          |   5 +-
 app/portainer/views/main/mainController.js    |   1 +
 34 files changed, 1352 insertions(+), 107 deletions(-)
 create mode 100644 app/assets/css/theme.css
 create mode 100644 app/assets/css/vendor-override.css
 create mode 100644 app/portainer/components/theme/theme-settings.controller.js
 create mode 100644 app/portainer/components/theme/theme-settings.html
 create mode 100644 app/portainer/components/theme/theme-settings.js
 create mode 100644 app/portainer/services/themeManager.js

diff --git a/api/http/handler/users/user_update.go b/api/http/handler/users/user_update.go
index fdd7e1a65..02e337f3b 100644
--- a/api/http/handler/users/user_update.go
+++ b/api/http/handler/users/user_update.go
@@ -17,6 +17,7 @@ import (
 type userUpdatePayload struct {
 	Username string `validate:"required" example:"bob"`
 	Password string `validate:"required" example:"cg9Wgky3"`
+	UserTheme string `example:"dark"`
 	// User role (1 for administrator account and 2 for regular account)
 	Role int `validate:"required" enums:"1,2" example:"2"`
 }
@@ -104,6 +105,10 @@ func (handler *Handler) userUpdate(w http.ResponseWriter, r *http.Request) *http
 		user.Role = portainer.UserRole(payload.Role)
 	}
 
+	if payload.UserTheme != "" {
+		user.UserTheme = payload.UserTheme
+	}
+
 	err = handler.DataStore.User().UpdateUser(user.ID, user)
 	if err != nil {
 		return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist user changes inside the database", err}
diff --git a/api/portainer.go b/api/portainer.go
index 87903d4b9..96af4f17e 100644
--- a/api/portainer.go
+++ b/api/portainer.go
@@ -1003,6 +1003,8 @@ type (
 		ID       UserID `json:"Id" example:"1"`
 		Username string `json:"Username" example:"bob"`
 		Password string `json:"Password,omitempty" example:"passwd"`
+		// User Theme
+		UserTheme string `example:"dark"`
 		// User role (1 for administrator account and 2 for regular account)
 		Role UserRole `json:"Role" example:"1"`
 
diff --git a/app/assets/css/app.css b/app/assets/css/app.css
index d88bfc110..74b1788b3 100644
--- a/app/assets/css/app.css
+++ b/app/assets/css/app.css
@@ -59,10 +59,10 @@ body,
 }
 
 .form-section-title {
-  border-bottom: 1px solid #777;
+  border-bottom: 1px solid var(--border-form-section-title-color);
   margin-top: 5px;
   margin-bottom: 15px;
-  color: #777;
+  color: var(--text-form-section-title-color);
   padding-left: 0;
 }
 
@@ -108,12 +108,33 @@ a[ng-click] {
   pointer-events: none;
 }
 
+.datatable-highlighted {
+  background-color: var(--bg-item-highlighted-color);
+}
+
+.datatable-unhighlighted {
+  background-color: var(--bg-item-highlighted-null-color);
+}
+
+.service-datatable {
+  background-color: var(--bg-item-highlighted-color);
+  padding: 2px;
+}
+
+.service-datatable thead {
+  background-color: var(--bg-service-datatable-thead) !important;
+}
+
+.service-datatable tbody {
+  background-color: var(--bg-service-datatable-tbody);
+}
+
 .tooltip.portainer-tooltip .tooltip-inner {
   font-family: Montserrat;
-  background-color: #ffffff;
+  background-color: var(--bg-tooltip-color);
   padding: 0.833em 1em;
-  color: #333333;
-  border: 1px solid #d4d4d5;
+  color: var(--text-tooltip-color);
+  border: 1px solid var(--border-tooltip-color);
   border-radius: 0.14285714rem;
   box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15);
 }
@@ -145,7 +166,7 @@ a[ng-click] {
 
 .fa.blue-icon,
 .fab.blue-icon {
-  color: #337ab7;
+  color: var(--blue-2);
 }
 
 .text-warning {
@@ -207,25 +228,25 @@ a[ng-click] {
   padding: 0.7rem;
   margin-bottom: 0.7rem;
   cursor: pointer;
-  border: 1px solid #cccccc;
+  border: 1px solid var(--border-blocklist-color);
   border-radius: 2px;
-  box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5);
+  box-shadow: var(--shadow-box-color);
 }
 
 .blocklist-item--disabled {
   cursor: auto;
-  background-color: #ececec;
+  background-color: var(--grey-12);
 }
 
 .blocklist-item--selected {
-  border: 2px solid #bbbbbb;
-  background-color: #ececec;
-  color: #2d3e63;
+  background-color: var(--bg-blocklist-item-selected-color);
+  border: 2px solid var(--border-blocklist-item-selected-color);
+  color: var(--text-blocklist-item-selected-color);
 }
 
 .blocklist-item:hover {
-  background-color: #ececec;
-  color: #2d3e63;
+  background-color: var(--bg-blocklist-hover-color);
+  color: var(--text-blocklist-hover-color);
 }
 
 .blocklist-item-box {
@@ -360,7 +381,7 @@ a[ng-click] {
 
 .panel-body {
   padding-top: 30px;
-  background-color: #ffffff;
+  background-color: var(--white-color) fff;
 }
 
 .pagination-controls {
@@ -443,8 +464,8 @@ a[ng-click] {
   display: inline-block;
   padding: 0px 6px;
   margin-left: 10px;
-  color: #555555;
-  background-color: #fff;
+  color: var(--text-small-select-color);
+  background-color: var(--bg-small-select-color);
   background-image: none;
   border-radius: 4px;
   font-size: 14px;
@@ -462,11 +483,11 @@ a[ng-click] {
 }
 
 .visualizer_container .node {
-  border: 1px dashed #337ab7;
+  border: 1px dashed var(--blue-2);
   background-color: rgb(51, 122, 183);
   background-color: rgba(51, 122, 183, 0.1);
   border-radius: 4px;
-  box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5);
+  box-shadow: 0 3px 10px -2px var(--grey-50);
   padding: 15px;
   margin: 5px;
 }
@@ -476,7 +497,7 @@ a[ng-click] {
   flex-direction: column;
   justify-content: center;
   text-align: center;
-  border-bottom: 1px solid #777;
+  border-bottom: 1px solid var(--grey-26);
   padding-bottom: 10px;
 }
 
@@ -486,7 +507,7 @@ a[ng-click] {
 }
 
 .visualizer_container .node .node_info .node_labels {
-  border-top: 1px solid #777;
+  border-top: 1px solid var(--grey-26);
   padding-top: 10px;
   margin-top: 10px;
 }
@@ -503,9 +524,9 @@ a[ng-click] {
 }
 
 .visualizer_container .node .tasks .task {
-  border: 1px solid #333333;
+  border: 1px solid var(--grey-6);
   border-radius: 2px;
-  box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5);
+  box-shadow: 0 3px 10px -2px var(--grey-50);
   padding: 10px;
   margin: 5px;
   font-size: 10px;
@@ -547,9 +568,9 @@ a[ng-click] {
 .log_viewer {
   height: 100%;
   overflow-y: scroll;
-  color: black;
+  color: var(--text-log-viewer-color);
   font-size: 0.85em;
-  background-color: white;
+  background-color: var(--bg-log-viewer-color);
 }
 
 .log_viewer.wrap_lines {
@@ -568,7 +589,7 @@ a[ng-click] {
 }
 
 .log_viewer .line_selected {
-  background-color: #c5cae9;
+  background-color: var(--bg-log-line-selected-color);
 }
 
 .row.header .meta .page {
@@ -578,7 +599,7 @@ a[ng-click] {
 .tag {
   padding: 2px 6px;
   color: white;
-  background-color: #337ab7;
+  background-color: var(--blue-2);
   border: 1px solid #2e6da4;
   border-radius: 4px;
 }
@@ -588,7 +609,7 @@ a[ng-click] {
 }
 
 .line-separator {
-  border-bottom: 1px solid #777;
+  border-bottom: 1px solid var(--grey-26);
   width: 50%;
   margin: 20px auto 10px auto;
 }
@@ -613,7 +634,7 @@ a[ng-click] {
 }
 
 .striked::after {
-  border-bottom: 0.2em solid #777777;
+  border-bottom: 0.2em solid var(--grey-26);
   content: '';
   left: 0;
   margin-top: calc(0.2em / 2 * -1);
@@ -625,7 +646,7 @@ a[ng-click] {
 
 .striketext:before,
 .striketext:after {
-  background-color: #777777;
+  background-color: var(--grey-26);
   content: '';
   display: inline-block;
   height: 1px;
@@ -657,20 +678,51 @@ a[ng-click] {
 /*angular-multi-select override*/
 .multiSelect > button {
   min-height: 30px !important;
-  background-color: unset;
-  background-image: unset;
+  background-image: var(--bg-image-multiselect-button);
+  border-color: var(--border-multiselect);
+  color: var(--text-multiselect);
+  background-color: var(--bg-multiselect-color);
+}
+
+.multiSelect > button:hover {
+  background-image: var(--bg-image-multiselect-hover);
+}
+
+.multiSelect .checkboxLayer {
+  border-color: var(--border-multiselect-checkboxlayer);
+}
+
+.multiSelect .checkBoxContainer {
+  background-color: var(--bg-multiselect-checkboxcontainer);
+}
+
+.multiSelect .multiSelectItem {
+  color: var(--text-multiselect-item);
+}
+
+.multiSelect .helperContainer {
+  background-color: var(--bg-multiselect-helpercontainer);
+}
+
+.multiSelect .multiSelectFocus {
+  background-image: var(--bg-image-multiselect);
 }
 
 .multiSelect .multiSelectItem:not(.multiSelectGroup).selected {
-  background-image: linear-gradient(#337ab7, #337ab7);
-  color: #fff;
+  background-image: var(--bg-image-multiselect);
+  color: var(--white-color);
   border: none;
 }
 
 .multiSelect .multiSelectItem:hover,
 .multiSelect .multiSelectGroup:hover {
-  background-image: linear-gradient(#337ab7, #337ab7) !important;
-  color: #fff !important;
+  border-color: var(--grey-3);
+}
+
+.multiSelect .multiSelectItem:hover,
+.multiSelect .multiSelectGroup:hover {
+  background-image: var(--bg-image-multiselect) !important;
+  color: var(--white-color) !important;
 }
 
 .multiSelect .tickMark,
@@ -696,7 +748,7 @@ a[ng-click] {
 #loading-bar .bar {
   position: relative;
   height: 3px;
-  background: #738bc0;
+  background: var(--blue-3);
 }
 /*!angular-loading-bar override*/
 
@@ -708,11 +760,11 @@ a[ng-click] {
 /* json-tree override */
 json-tree {
   font-size: 13px;
-  color: #30426a;
+  color: var(--blue-5);
 }
 
 json-tree .key {
-  color: #738bc0;
+  color: var(--blue-3);
   padding-right: 5px;
 }
 
@@ -725,7 +777,7 @@ json-tree .branch-preview {
 
 /* uib-progressbar override */
 .progress-bar {
-  color: #4e4e4e;
+  color: var(--text-progress-bar-color);
 }
 /* !uib-progressbar override */
 
@@ -756,7 +808,7 @@ json-tree .branch-preview {
 }
 
 .sk-fold-cube:before {
-  background-color: #337ab7;
+  background-color: var(--blue-2);
 }
 /* !spinkit override */
 
diff --git a/app/assets/css/index.js b/app/assets/css/index.js
index c035bf022..b812316ab 100644
--- a/app/assets/css/index.js
+++ b/app/assets/css/index.js
@@ -1,2 +1,4 @@
 import './rdash.css';
 import './app.css';
+import './theme.css';
+import './vendor-override.css';
diff --git a/app/assets/css/rdash.css b/app/assets/css/rdash.css
index a9dc4ff48..e885c51f3 100644
--- a/app/assets/css/rdash.css
+++ b/app/assets/css/rdash.css
@@ -52,7 +52,8 @@
  * Header
  */
 .row.header {
-  background: #fff;
+  height: 60px;
+  background: var(--bg-row-header-color);
   margin-bottom: 15px;
 }
 .row.header > div:last-child {
@@ -202,9 +203,9 @@ html {
   overflow-y: scroll;
 }
 body {
-  background: #f3f3f3;
+  background: var(--bg-body-color);
   font-family: 'Montserrat';
-  color: #333333 !important;
+  color: var(--text-body-color) !important;
 }
 .row {
   margin-left: 0 !important;
@@ -236,7 +237,7 @@ body {
   background: #23ae89 !important;
 }
 .blue {
-  background: #2361ae !important;
+  background: var(--blue-color) !important;
 }
 .orange {
   background: #d3a938 !important;
@@ -258,20 +259,20 @@ div.input-mask {
   -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
   -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
   box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
-  background: #ffffff;
+  background: var(--bg-widget-color);
   border: 1px solid transparent;
   border-radius: 2px;
-  border-color: #e9e9e9;
+  border-color: var(--border-widget-color);
 }
 .widget .widget-header .pagination,
 .widget .widget-footer .pagination {
   margin: 0;
 }
 .widget .widget-header {
-  color: #767676;
-  background-color: #f6f6f6;
+  color: var(--text-widget-header-color);
+  background-color: var(--bg-widget-header-color);
   padding: 10px 15px;
-  border-bottom: 1px solid #e9e9e9;
+  border-bottom: 1px solid var(--border-widget-color);
   line-height: 30px;
 }
 .widget .widget-header i {
@@ -281,7 +282,7 @@ div.input-mask {
   padding: 20px;
 }
 .widget .widget-body table thead {
-  background: #fafafa;
+  background: var(--bg-widget-table-color);
 }
 .widget .widget-body table thead * {
   font-size: 14px !important;
diff --git a/app/assets/css/theme.css b/app/assets/css/theme.css
new file mode 100644
index 000000000..b186f81c6
--- /dev/null
+++ b/app/assets/css/theme.css
@@ -0,0 +1,576 @@
+/* Color Variable */
+html {
+  --black-color: #000;
+  --white-color: #fff;
+
+  --grey-1: #212121;
+  --grey-2: #181818;
+  --grey-3: #383838;
+  --grey-4: #585858;
+  --grey-5: #323c48;
+  --grey-6: #333333;
+  --grey-7: #767676;
+  --grey-8: #aaa;
+  --grey-9: #f3f3f3;
+  --grey-10: #f6f6f6;
+  --grey-11: #eeeeee;
+  --grey-12: #ececec;
+  --grey-13: #fafafa;
+  --grey-14: #f5f5f5;
+  --grey-15: #f9f2f4;
+  --grey-16: #eee;
+  --grey-17: #f7f7f7;
+  --grey-18: #c5cae9;
+  --grey-19: #ddd;
+  --grey-20: #dae3f3;
+  --grey-21: #d5e8f3;
+  --grey-22: #c3c3e4;
+  --grey-23: #e7f6ff;
+  --grey-24: #f1f9fd;
+  --grey-25: #555555;
+  --grey-26: #777777;
+  --grey-27: #4e4e4e;
+  --grey-28: #262626;
+  --grey-29: #555;
+  --grey-30: #444;
+  --grey-31: #868686;
+  --grey-32: #65798e;
+  --grey-34: #314252;
+  --grey-35: #546477;
+  --grey-36: #55637d;
+  --grey-37: #2d3e63;
+  --grey-38: #434343;
+  --grey-39: #194973;
+  --grey-40: #cfddfc;
+  --grey-41: #b4b4b4;
+  --grey-42: #d2d1d1;
+  --grey-43: #e9e9e9;
+  --grey-44: #ccc;
+  --grey-45: #e5e5e5;
+  --grey-46: #bbbbbb;
+  --grey-47: #d4d4d5;
+  --grey-48: #c6c6c6;
+  --grey-49: rgba(0, 0, 0, 0.54);
+  --grey-50: rgba(161, 170, 166, 0.5);
+  --grey-51: rgba(0, 0, 0, 0.15);
+  --grey-52: rgba(255, 255, 255, 0.3);
+  --grey-53: rgba(255, 255, 255, 0.6);
+  --grey-54: rgb(54, 54, 54);
+  --grey-55: rgba(255, 255, 255, 0.8);
+  --grey-56: #b2bfdc;
+  --grey-57: #999;
+  --grey-58: #ebf4f8;
+  --grey-59: #e6e6e6;
+  --grey-60: #cacaca;
+
+  --blue-1: #219;
+  --blue-2: #337ab7;
+  --blue-3: #738bc0;
+  --blue-4: #23527c;
+  --blue-5: #30426a;
+  --blue-6: #577bc9;
+  --blue-7: #6b9aff;
+  --blue-8: #90ccff;
+  --blue-9: #3ea6ff;
+  --blue-10: #61b6ff;
+  --blue-11: #3ea5ff;
+  --blue-12: #41a6ff;
+  --blue-13: #2361ae;
+  --blue-14: #357ebd;
+
+  --red-1: #a94442;
+  --red-2: #c7254e;
+  --red-3: #a11;
+  --red-4: #d9534f;
+  --red-5: #ff2727;
+  --red-6: #ff00e0;
+  --red-7: #f00;
+
+  --green-1: #164;
+  --green-2: #1ec863;
+}
+
+:root {
+  --bg-card-color: var(--grey-10);
+  --bg-main-color: var(--white-color);
+  --bg-body-color: var(--grey-9);
+  --bg-checkbox-border-color: var(--grey-49);
+  --bg-sidebar-color: var(--grey-37);
+  --bg-sidebar-header-color: var(--grey-37);
+  --bg-widget-color: var(--white-color);
+  --bg-widget-header-color: var(--grey-10);
+  --bg-widget-table-color: var(--grey-13);
+  --bg-header-color: var(--white-color);
+  --bg-hover-table-color: var(--grey-14);
+  --bg-switch-box-color: var(--white-color);
+  --bg-input-group-addon-color: var(--grey-11);
+  --bg-btn-default-color: var(--white-color);
+  --bg-blocklist-hover-color: var(--grey-12);
+  --bg-boxselector-color: var(--white-color);
+  --bg-table-color: var(--white-color);
+  --bg-md-checkbox-color: var(--grey-12);
+  --bg-form-control-disabled-color: var(--grey-11);
+  --bg-modal-content-color: var(--white-color);
+  --bg-code-color: var(--grey-15);
+  --bg-navtabs-color: var(--white-color);
+  --bg-navtabs-hover-color: var(--grey-16);
+  --bg-table-selected-color: var(--grey-14);
+  --bg-codemirror-gutters-color: var(--grey-17);
+  --bg-dropdown-menu-color: var(--white-color);
+  --bg-log-viewer-color: var(--white-color);
+  --bg-log-line-selected-color: var(--grey-18);
+  --bg-pre-color: var(--grey-14);
+  --bg-blocklist-item-selected-color: var(--grey-12);
+  --bg-progress-color: var(--grey-14);
+  --bg-pagination-color: var(--white-color);
+  --bg-pagination-span-color: var(--white-color);
+  --bg-pagination-hover-color: var(--grey-11);
+  --bg-ui-select-hover-color: var(--grey-14);
+  --bg-motd-body-color: var(--grey-20);
+  --bg-item-highlighted-color: var(--grey-21);
+  --bg-item-highlighted-null-color: var(--grey-14);
+  --bg-row-header-color: var(--white-color);
+  --bg-image-multiselect-button: linear-gradient(var(--white-color), var(--grey-17));
+  --bg-multiselect-checkbox-color: var(--white-color);
+  --bg-sidebar-wrapper-color: var(--blue-5);
+  --bg-panel-body-color: var(--white-color);
+  --bg-codemirror-color: var(--white-color);
+  --bg-codemirror-selected-color: var(--grey-22);
+  --bg-multiselect-color: var(--white-color);
+  --bg-daterangepicker-color: var(--white-color);
+  --bg-calendar-color: var(--white-color);
+  --bg-calendar-table-color: var(--white-color);
+  --bg-daterangepicker-end-date: var(--white-color);
+  --bg-daterangepicker-hover: var(--grey-16);
+  --bg-daterangepicker-in-range: var(--grey-58);
+  --bg-daterangepicker-active: var(--blue-14);
+  --bg-tooltip-color: var(--white-color);
+  --bg-input-autofill-color: var(--white-color);
+  --bg-btn-default-hover-color: var(--grey-59);
+  --bg-btn-focus: var(--grey-59);
+  --bg-boxselector-disabled-color: var(--white-color);
+  --bg-small-select-color: var(--white-color);
+
+  --text-main-color: var(--grey-7);
+  --text-body-color: var(--grey-6);
+  --text-sidebar-title-color: var(--blue-3);
+  --text-widget-header-color: var(--grey-7);
+  --text-form-control-color: var(--grey-25);
+  --text-muted-color: var(--grey-26);
+  --text-link-color: var(--blue-2);
+  --text-link-hover-color: var(--blue-4);
+  --text-input-group-addon-color: var(--grey-25);
+  --text-btn-default-color: var(--grey-6);
+  --text-blocklist-hover-color: var(--grey-37);
+  --text-dashboard-item-color: var(--grey-32);
+  --text-danger-color: var(--red-1);
+  --text-code-color: var(--red-2);
+  --text-navtabs-color: var(--grey-25);
+  --text-form-section-title-color: var(--grey-26);
+  --text-cm-default-color: var(--blue-1);
+  --text-cm-meta-color: var(--black-color);
+  --text-cm-string-color: var(--red-3);
+  --text-cm-number-color: var(--green-1);
+  --text-codemirror-color: var(--black-color);
+  --text-dropdown-menu-color: var(--grey-6);
+  --text-log-viewer-color: var(--black-color);
+  --text-json-tree-color: var(--blue-3);
+  --text-json-tree-leaf-color: var(--blue-5);
+  --text-json-tree-branch-preview-color: var(--blue-5);
+  --text-pre-color: var(--grey-6);
+  --text-blocklist-item-selected-color: var(--grey-37);
+  --text-progress-bar-color: var(--grey-27);
+  --text-pagination-color: var(--grey-26);
+  --text-pagination-span-color: var(--blue-2);
+  --text-pagination-span-hover-color: var(--blue-4);
+  --text-ui-select-color: var(--grey-6);
+  --text-ui-select-hover-color: var(--grey-28);
+  --text-summary-color: var(--black-color);
+  --text-multiselect-button-color: var(--grey-29);
+  --text-multiselect-item-color: var(--grey-30);
+  --text-sidebar-list-color: var(--grey-56);
+  --text-rzslider-color: var(--grey-36);
+  --text-rzslider-limit-color: var(--grey-36);
+  --text-daterangepicker-end-date: var(--grey-57);
+  --text-daterangepicker-in-range: var(--black-color);
+  --text-daterangepicker-active: var(--white-color);
+  --text-tooltip-color: var(--grey-6);
+  --text-input-autofill-color: var(--black-color);
+  --text-button-hover-color: var(--grey-6);
+  --text-small-select-color: var(--grey-25);
+
+  --border-color: var(--grey-42);
+  --border-widget-color: var(--grey-43);
+  --border-sidebar-color: var(--white-color);
+  --border-form-control-color: var(--grey-44);
+  --border-table-color: var(--grey-19);
+  --border-table-top-color: var(--grey-19);
+  --border-datatable-top-color: var(--grey-10);
+  --border-blocklist-color: var(--grey-44) ccc;
+  --border-input-group-addon-color: var(--grey-44);
+  --border-btn-default-color: var(--grey-44);
+  --border-boxselector-color: var(--grey-6);
+  --border-md-checkbox-color: var(--grey-19);
+  --border-modal-header-color: var(--grey-45);
+  --border-navtabs-color: var(--grey-19);
+  --border-form-section-title-color: var(--grey-26);
+  --border-codemirror-cursor-color: var(--black-color);
+  --border-codemirror-gutters-color: var(--grey-19);
+  --border-pre-color: var(--grey-43);
+  --border-blocklist-item-selected-color: var(--grey-46);
+  --border-pagination-color: var(--grey-19);
+  --border-pagination-span-color: var(--grey-19);
+  --border-pagination-hover-color: var(--grey-19);
+  --border-multiselect-button-color: var(--grey-48);
+  --border-searchbar-color: var(--grey-10);
+  --border-panel-color: var(--white-color);
+  --border-daterangepicker-color: var(--grey-19);
+  --border-calendar-table: var(--white-color);
+  --border-daterangepicker: var(--grey-19);
+  --border-pre-next-month: var(--black-color);
+  --border-daterangepicker-after: var(--white-color);
+  --border-tooltip-color: var(--grey-47);
+  --border-modal: 0px;
+
+  --hover-sidebar-color: var(--grey-37);
+  --shadow-box-color: 0 3px 10px -2px var(--grey-50);
+  --shadow-boxselector-color: 0 3px 10px -2px var(--grey-50);
+  --blue-color: var(--blue-13);
+  --button-close-color: var(--black-color);
+  --button-opacity: 0.2;
+  --button-opacity-hover: 0.5;
+  --bg-boxselector-wrapper-color: var(--grey-6);
+
+  --bg-image-multiselect: linear-gradient(var(--blue-2), var(--blue-2));
+  --bg-image-multiselect-button: linear-gradient(var(--white-color), var(--grey-17));
+  --bg-image-multiselect-hover: linear-gradient(var(--white-color), var(--grey-43));
+  --border-multiselect: var(--grey-48);
+  --border-multiselect-checkboxlayer: var(--grey-51);
+  --text-multiselect: var(--grey-29);
+  --text-multiselect-selectitem: var(--white-color);
+  --bg-multiselect-checkboxcontainer: var(--white-color);
+  --text-multiselect-item: var(--grey-30);
+  --bg-multiselect-helpercontainer: var(--white-color);
+  --text-input-textarea: var(--white-color);
+  --bg-service-datatable-thead: var(--grey-23);
+  --bg-service-datatable-tbody: var(--grey-24);
+}
+
+:root[theme='dark'] {
+  --bg-card-color: var(--grey-1);
+  --bg-main-color: var(--grey-2);
+  --bg-body-color: var(--grey-2);
+  --bg-checkbox-border-color: var(--grey-8);
+  --bg-sidebar-color: var(--grey-3);
+  --bg-widget-color: var(--grey-1);
+  --bg-widget-header-color: var(--grey-1);
+  --bg-widget-table-color: var(--grey-1);
+  --bg-header-color: var(--grey-2);
+  --bg-hover-table-color: var(--grey-3);
+  --bg-switch-box-color: var(--grey-53);
+  --bg-input-group-addon-color: var(--grey-3);
+  --bg-btn-default-color: var(--grey-3);
+  --bg-blocklist-hover-color: var(--grey-3);
+  --bg-boxselector-color: var(--grey-54);
+  --bg-table-color: var(--grey-1);
+  --bg-md-checkbox-color: var(--grey-31);
+  --bg-form-control-disabled-color: var(--grey-3);
+  --bg-modal-content-color: var(--grey-1);
+  --bg-code-color: var(--red-4);
+  --bg-navtabs-color: var(--grey-3);
+  --bg-navtabs-hover-color: var(--grey-3);
+  --bg-table-selected-color: var(--grey-3);
+  --bg-codemirror-color: var(--grey-2);
+  --bg-codemirror-gutters-color: var(--grey-2);
+  --bg-dropdown-menu-color: var(--grey-1);
+  --bg-log-viewer-color: var(--grey-2);
+  --bg-log-line-selected-color: var(--grey-3);
+  --bg-pre-color: var(--grey-2);
+  --bg-blocklist-item-selected-color: var(--grey-3);
+  --bg-progress-color: var(--grey-3);
+  --bg-pagination-color: var(--grey-3);
+  --bg-pagination-span-color: var(--grey-3);
+  --bg-pagination-hover-color: var(--grey-4);
+  --bg-ui-select-hover-color: var(--grey-3);
+  --bg-motd-body-color: var(--grey-1);
+  --bg-item-highlighted-color: var(--grey-2);
+  --bg-item-highlighted-null-color: var(--grey-2);
+  --bg-row-header-color: var(--grey-2);
+  --bg-multiselect-button-color: var(--grey-3);
+  --bg-image-multiselect-button: none !important;
+  --bg-multiselect-checkbox-color: var(--grey-3);
+  --bg-sidebar-wrapper-color: var(--grey-1);
+  --bg-panel-body-color: var(--grey-1);
+  --bg-boxselector-wrapper-disabled-color: var(--grey-39);
+  --bg-codemirror-selected-color: var(--grey-3);
+  --bg-sidebar-header-color: var(--grey-1);
+  --bg-multiselect-color: var(--grey-1);
+  --bg-daterangepicker-color: var(--grey-3);
+  --bg-calendar-color: var(--grey-3);
+  --bg-calendar-table-color: var(--grey-3);
+  --bg-daterangepicker-end-date: var(--grey-4);
+  --bg-daterangepicker-hover: var(--grey-4);
+  --bg-daterangepicker-in-range: var(--grey-2);
+  --bg-daterangepicker-active: var(--blue-14);
+  --bg-tooltip-color: var(--grey-3);
+  --bg-input-autofill-color: var(--grey-2);
+  --bg-btn-default-hover-color: var(--grey-3);
+  --bg-btn-focus: var(--grey-3);
+  --bg-boxselector-disabled-color: var(--grey-54);
+  --bg-small-select-color: var(--grey-2);
+
+  --text-main-color: var(--white-color);
+  --text-body-color: var(--white-color);
+  --text-sidebar-title-color: var(--grey-8);
+  --text-widget-header-color: var(--white-color);
+  --text-form-control-color: var(--grey-8);
+  --text-muted-color: var(--grey-8);
+  --text-link-color: var(--blue-9);
+  --text-link-hover-color: var(--blue-2);
+  --text-input-group-addon-color: var(--grey-8);
+  --text-btn-default-color: var(--grey-8);
+  --text-blocklist-hover-color: var(--white-color);
+  --text-dashboard-item-color: var(--blue-2);
+  --text-danger-color: var(--red-4);
+  --text-code-color: var(--white-color);
+  --text-navtabs-color: var(--white-color);
+  --text-form-section-title-color: var(--grey-8);
+  --text-cm-default-color: var(--blue-10);
+  --text-cm-meta-color: var(--white-color);
+  --text-cm-string-color: var(--red-5);
+  --text-cm-number-color: var(--green-2);
+  --text-codemirror-color: var(--white-color);
+  --text-dropdown-menu-color: var(--white-color);
+  --text-log-viewer-color: var(--white-color);
+  --text-json-tree-color: var(--grey-40);
+  --text-json-tree-leaf-color: var(--blue-6);
+  --text-json-tree-branch-preview-color: var(--blue-7);
+  --text-pre-color: var(--white-color);
+  --text-blocklist-item-selected-color: var(--white-color);
+  --text-progress-bar-color: var(--white-color);
+  --text-pagination-color: var(--white-color);
+  --text-pagination-span-color: var(--white-color);
+  --text-pagination-span-hover-color: var(--white-color);
+  --text-ui-select-color: var(--white-color);
+  --text-ui-select-hover-color: var(--white-color);
+  --text-summary-color: var(--white-color);
+  --text-multiselect-button-color: var(--white-color);
+  --text-multiselect-item-color: var(--white-color);
+  --text-sidebar-list-color: var(--white-color);
+  --text-boxselector-wrapper-color: var(--white-color);
+  --text-daterangepicker-end-date: var(--grey-7);
+  --text-daterangepicker-in-range: var(--white-color);
+  --text-daterangepicker-active: var(--white-color);
+  --text-tooltip-color: var(--white-color);
+  --text-btn-default-color: var(--white-color);
+  --text-input-autofill-color: var(--grey-8);
+  --text-button-hover-color: var(--white-color);
+  --text-small-select-color: var(--grey-7);
+
+  --border-color: var(--grey-3);
+  --border-widget-color: var(--grey-1);
+  --border-sidebar-color: var(--blue-9);
+  --border-form-control-color: var(--grey-54);
+  --border-table-color: var(--grey-3);
+  --border-table-top-color: var(--grey-3);
+  --border-datatable-top-color: var(--grey-3);
+  --border-blocklist-color: var(--grey-3);
+  --border-input-group-addon-color: var(--grey-38);
+  --border-btn-default-color: var(--grey-38);
+  --border-boxselector-color: var(--grey-1);
+  --border-md-checkbox-color: var(--grey-41);
+  --border-modal-header-color: var(--grey-1);
+  --border-navtabs-color: var(--grey-38);
+  --border-form-section-title-color: var(--grey-8);
+  --border-codemirror-cursor-color: var(--white-color);
+  --border-codemirror-gutters-color: var(--grey-26);
+  --border-pre-color: var(--grey-3);
+  --border-blocklist-item-selected-color: var(--grey-38);
+  --border-pagination-color: var(--grey-3);
+  --border-pagination-span-color: var(--grey-3);
+  --border-pagination-hover-color: var(--grey-3);
+  --border-pagination-hover-color: var(--grey-3);
+  --border-multiselect-button-color: var(--grey-3);
+  --border-searchbar-color: var(--grey-1);
+  --border-panel-color: var(--grey-2);
+  --border-daterangepicker-color: var(--grey-3);
+  --border-calendar-table: var(--grey-3);
+  --border-daterangepicker: var(--grey-4);
+  --border-pre-next-month: var(--white-color);
+  --border-daterangepicker-after: var(--grey-3);
+  --border-tooltip-color: var(--grey-3);
+  --border-modal: 0px;
+
+  --hover-sidebar-color: var(--grey-3);
+  --blue-color: var(--blue-2);
+  --button-close-color: var(--white-color);
+  --button-opacity: 0.6;
+  --button-opacity-hover: 0.3;
+  --shadow-box-color: none;
+  --shadow-boxselector-color: none;
+
+  --bg-image-multiselect: linear-gradient(var(--grey-38), var(--grey-38));
+  --bg-image-multiselect-button: linear-gradient(var(--grey-1), var(--grey-1));
+  --bg-image-multiselect-hover: linear-gradient(var(--grey-3), var(--grey-3));
+  --border-multiselect: var(--grey-3);
+  --border-multiselect-checkboxlayer: var(--grey-3);
+  --text-multiselect: var(--white-color);
+  --bg-multiselect-checkboxcontainer: var(--grey-3);
+  --text-multiselect-item: var(--white-color);
+  --bg-multiselect-helpercontainer: var(--grey-1);
+  --text-input-textarea: var(--grey-1);
+  --bg-service-datatable-thead: var(--grey-1);
+  --bg-service-datatable-tbody: var(--grey-1);
+}
+
+:root[theme='highcontrast'] {
+  --bg-card-color: var(--black-color);
+  --bg-main-color: var(--black-color);
+  --bg-body-color: var(--black-color);
+  --bg-checkbox-border-color: var(--grey-8);
+  --bg-sidebar-color: var(--black-color);
+  --bg-widget-color: var(--black-color);
+  --bg-widget-header-color: var(--black-color);
+  --bg-widget-table-color: var(--black-color);
+  --bg-header-color: var(--black-color);
+  --bg-hover-table-color: var(--grey-3);
+  --bg-switch-box-color: var(--grey-53);
+  --bg-panel-body-color: var(--black-color);
+  --bg-boxselector-wrapper-disabled-color: var(--grey-39);
+  --bg-dropdown-menu-color: var(--black-color);
+  --bg-codemirror-selected-color: var(--grey-3);
+  --bg-row-header-color: var(--black-color);
+  --bg-sidebar-wrapper-color: var(--black-color);
+  --bg-motd-body-color: var(--black-color);
+  --bg-blocklist-hover-color: var(--black-color);
+  --bg-blocklist-item-selected-color: var(--black-color);
+  --bg-input-group-addon-color: var(--grey-1);
+  --bg-table-color: var(--black-color);
+  --bg-codemirror-gutters-color: var(--black-color);
+  --bg-codemirror-color: var(--black-color);
+  --bg-codemirror-selected-color: var(--grey-3);
+  --bg-log-viewer-color: var(--black-color);
+  --bg-log-line-selected-color: var(--grey-3);
+  --bg-sidebar-header-color: var(--black-color);
+  --bg-modal-content-color: var(--black-color);
+  --bg-form-control-disabled-color: var(--grey-1);
+  --bg-input-sm-color: var(--black-color);
+  --bg-item-highlighted-color: var(--black-color);
+  --bg-service-datatable-thead: var(--black-color);
+  --bg-service-datatable-tbody: var(--black-color);
+  --bg-pagination-color: var(--grey-3);
+  --bg-pagination-span-color: var(--grey-3);
+  --bg-multiselect-color: var(--grey-1);
+  --bg-daterangepicker-color: var(--black-color);
+  --bg-calendar-color: var(--black-color);
+  --bg-calendar-table-color: var(--black-color);
+  --bg-daterangepicker-end-date: var(--grey-3);
+  --bg-daterangepicker-hover: var(--grey-3);
+  --bg-daterangepicker-in-range: var(--grey-2);
+  --bg-daterangepicker-active: var(--blue-14);
+  --bg-tooltip-color: var(--black-color);
+  --bg-table-selected-color: var(--grey-3);
+  --bg-pre-color: var(--grey-2);
+  --bg-navtabs-hover-color: var(--grey-3);
+  --bg-btn-default-color: var(--black-color);
+  --bg-code-color: var(--red-4);
+  --bg-navtabs-color: var(--black-color);
+  --bg-input-autofill-color: var(--black-color);
+  --bg-code-color: var(--grey-2);
+  --bg-navtabs-color: var(--grey-2);
+  --bg-navtabs-hover-color: var(--grey-3);
+  --bg-btn-default-hover-color: var(--grey-3);
+  --bg-btn-default-color: var(--black-color);
+  --bg-btn-focus: var(--black-color);
+  --bg-boxselector-color: var(--black-color);
+  --bg-boxselector-disabled-color: var(--black-color);
+  --bg-small-select-color: var(--black-color);
+
+  --text-main-color: var(--white-color);
+  --text-body-color: var(--white-color);
+  --text-sidebar-title-color: var(--grey-8);
+  --text-widget-header-color: var(--white-color);
+  --text-link-color: var(--blue-9);
+  --text-link-hover-color: var(--blue-9);
+  --text-danger-color: var(--red-7);
+  --text-code-color: var(--red-7);
+  --text-form-control-color: var(--white-color);
+  --text-blocklist-hover-color: var(--blue-11);
+  --text-boxselector-wrapper-color: var(--white-color);
+  --text-dashboard-item-color: var(--blue-12);
+  --text-form-section-title-color: var(--white-color);
+  --text-muted-color: var(--white-color);
+  --text-tooltip-color: var(--white-color);
+  --text-blocklist-item-selected-color: var(--blue-9);
+  --text-input-group-addon-color: var(--white-color);
+  --text-codemirror-color: var(--white-color);
+  --text-log-viewer-color: var(--white-color);
+  --text-summary-color: var(--white-color);
+  --text-rzslider-color: var(--white-color);
+  --text-rzslider-limit-color: var(--white-color);
+  --text-pagination-color: var(--white-color);
+  --text-daterangepicker-end-date: var(--grey-7);
+  --text-daterangepicker-in-range: var(--white-color);
+  --text-daterangepicker-active: var(--white-color);
+  --text-sidebar-list-color: var(--white-color);
+  --text-ui-select-color: var(--white-color);
+  --text-btn-default-color: var(--white-color);
+  --text-json-tree-color: var(--white-color);
+  --text-json-tree-leaf-color: var(--white-color);
+  --text-json-tree-branch-preview-color: var(--white-color);
+  --text-pre-color: var(--white-color);
+  --text-navtabs-color: var(--white-color);
+  --text-input-autofill-color: var(--white-color);
+  --text-navtabs-color: var(--white-color);
+  --text-button-hover-color: var(--white-color);
+  --text-btn-default-color: var(--white-color);
+  --text-small-select-color: var(--white-color);
+
+  --border-color: var(--grey-55);
+  --border-widget-color: var(--white-color);
+  --border-sidebar-color: var(--blue-9);
+  --border-form-control-color: var(--grey-54);
+  --border-table-color: var(--grey-55);
+  --border-table-top-color: var(--grey-55);
+  --border-datatable-top-color: var(--grey-55);
+  --border-sidebar-high-contrast: 1px solid var(--blue-9);
+  --border-code-high-contrast: 1px solid var(--white-color);
+  --border-boxselector-wrapper: 3px solid var(--blue-2);
+  --border-boxselector-wrapper-hover: 3px solid var(--blue-8);
+  --border-panel-color: var(--white-color);
+  --border-input-group-addon-color: var(--grey-54);
+  --border-modal-header-color: var(--grey-3);
+  --border-input-sm-color: var(--white-color);
+  --border-pagination-color: var(--grey-3);
+  --border-pagination-span-color: var(--grey-3);
+  --border-daterangepicker-color: var(--white-color);
+  --border-calendar-table: var(--black-color);
+  --border-daterangepicker: var(--black-color);
+  --border-pre-next-month: var(--white-color);
+  --border-daterangepicker-after: var(--black-color);
+  --border-tooltip-color: var(--white-color);
+  --border-pre-color: var(--grey-3);
+  --border-codemirror-cursor-color: var(--white-color);
+  --border-modal: 1px solid var(--white-color);
+
+  --hover-sidebar-color: var(--blue-9);
+  --hover-sidebar-color: var(--black-color);
+  --shadow-box-color: none;
+  --shadow-boxselector-color: none;
+
+  --bg-image-multiselect: linear-gradient(var(--black-color), var(--black-color));
+  --bg-image-multiselect-button: linear-gradient(var(--grey-1), var(--grey-1));
+  --bg-image-multiselect-hover: linear-gradient(var(--grey-3), var(--grey-3));
+  --border-multiselect: var(--black-color);
+  --border-multiselect-checkboxlayer: var(--grey-3);
+  --text-multiselect: var(--white-color);
+  --bg-multiselect-checkboxcontainer: var(--grey-3);
+  --text-multiselect-item: var(--white-color);
+  --bg-multiselect-helpercontainer: var(--grey-1);
+  --text-input-textarea: var(--black-color);
+  --bg-item-highlighted-null-color: var(--grey-2);
+  --text-cm-default-color: var(--blue-9);
+  --text-cm-meta-color: var(--white-color);
+  --text-cm-string-color: var(--red-7);
+  --text-progress-bar-color: var(--black-color);
+}
diff --git a/app/assets/css/vendor-override.css b/app/assets/css/vendor-override.css
new file mode 100644
index 000000000..21572a7dd
--- /dev/null
+++ b/app/assets/css/vendor-override.css
@@ -0,0 +1,399 @@
+/* Overide Vendor CSS */
+.form-control {
+  background-color: var(--bg-main-color) !important;
+  border: 1px solid var(--border-form-control-color);
+  color: var(--text-form-control-color);
+}
+
+.text-muted {
+  color: var(--text-muted-color);
+}
+
+.table > thead > tr > th {
+  border-bottom: 2px solid var(--border-table-color);
+}
+
+.table-hover > tbody > tr:hover {
+  background-color: var(--bg-hover-table-color);
+}
+
+.switch i,
+.bootbox-form .checkbox i {
+  background: var(--bg-switch-box-color);
+}
+
+.table > thead > tr > th,
+.table > tbody > tr > th,
+.table > tfoot > tr > th,
+.table > thead > tr > td,
+.table > tbody > tr > td,
+.table > tfoot > tr > td {
+  border-top: 1px solid var(--border-table-top-color);
+}
+
+a {
+  color: var(--text-link-color);
+}
+
+a:hover,
+a:focus {
+  color: var(--text-link-hover-color);
+}
+
+.input-group-addon {
+  color: var(--text-input-group-addon-color);
+  background-color: var(--bg-input-group-addon-color);
+  border: 1px solid var(--border-input-group-addon-color);
+}
+
+.btn-default {
+  color: var(--text-btn-default-color);
+  background-color: var(--bg-btn-default-color);
+  border-color: var(--border-btn-default-color);
+}
+
+.text-danger {
+  color: var(--text-danger-color);
+}
+
+.table .table {
+  background-color: var(--bg-table-color);
+}
+
+.table-bordered {
+  border-color: var(--border-table-top-color);
+}
+
+.table-bordered > thead > tr > th,
+.table-bordered > tbody > tr > th,
+.table-bordered > tfoot > tr > th,
+.table-bordered > thead > tr > td,
+.table-bordered > tbody > tr > td,
+.table-bordered > tfoot > tr > td {
+  border-color: var(--border-table-top-color);
+}
+
+.md-checkbox input[type='checkbox']:disabled + label:before {
+  background: var(--bg-md-checkbox-color) !important;
+  border-color: var(--border-md-checkbox-color) !important;
+}
+
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+  background-color: var(--bg-form-control-disabled-color) !important;
+}
+
+.modal.in .modal-dialog {
+  border: var(--border-modal);
+}
+
+.modal-content {
+  background-color: var(--bg-modal-content-color);
+}
+
+.modal-header {
+  border-bottom: 1px solid var(--border-modal-header-color);
+}
+
+.modal-footer {
+  border-top: 1px solid var(--border-modal-header-color);
+}
+
+.close {
+  color: var(--button-close-color);
+  opacity: var(--button-opacity);
+}
+
+.close:hover,
+.close:focus {
+  color: var(--button-close-color);
+  opacity: var(--button-opacity-hover);
+}
+
+code {
+  color: var(--text-code-color);
+  background-color: var(--bg-code-color);
+}
+
+.nav-tabs > li.active > a,
+.nav-tabs > li.active > a:hover,
+.nav-tabs > li.active > a:focus {
+  color: var(--text-navtabs-color);
+  background-color: var(--bg-navtabs-color);
+  border: 1px solid var(--border-navtabs-color);
+}
+
+.nav-tabs {
+  border-bottom: 1px solid var(--border-navtabs-color);
+}
+
+.nav-tabs > li > a:hover {
+  border-color: var(--border-navtabs-color);
+}
+
+.nav > li > a:hover,
+.nav > li > a:focus {
+  background-color: var(--bg-navtabs-hover-color);
+}
+
+.table > thead > tr > td.active,
+.table > tbody > tr > td.active,
+.table > tfoot > tr > td.active,
+.table > thead > tr > th.active,
+.table > tbody > tr > th.active,
+.table > tfoot > tr > th.active,
+.table > thead > tr.active > td,
+.table > tbody > tr.active > td,
+.table > tfoot > tr.active > td,
+.table > thead > tr.active > th,
+.table > tbody > tr.active > th,
+.table > tfoot > tr.active > th {
+  background-color: var(--bg-table-selected-color);
+}
+.table-hover > tbody > tr > td.active:hover,
+.table-hover > tbody > tr > th.active:hover,
+.table-hover > tbody > tr.active:hover > td,
+.table-hover > tbody > tr:hover > .active,
+.table-hover > tbody > tr.active:hover > th {
+  background-color: var(--bg-table-selected-color);
+}
+
+.CodeMirror-gutters {
+  background: var(--bg-codemirror-gutters-color);
+  border-right: 1px solid var(--border-codemirror-gutters-color);
+}
+
+.CodeMirror {
+  background: var(--bg-codemirror-color);
+  color: var(--text-codemirror-color);
+}
+
+.CodeMirror-selected {
+  background: var(--bg-codemirror-selected-color) !important;
+}
+
+.CodeMirror-cursor {
+  border-left: 1px solid var(--border-codemirror-cursor-color);
+}
+
+.cm-s-default .cm-atom {
+  color: var(--text-cm-default-color);
+}
+
+.cm-s-default .cm-meta {
+  color: var(--text-cm-meta-color);
+}
+
+.cm-s-default .cm-string {
+  color: var(--text-cm-string-color);
+}
+
+.cm-s-default .cm-number {
+  color: var(--text-cm-number-color);
+}
+
+.dropdown-menu {
+  background: var(--bg-dropdown-menu-color);
+}
+
+.dropdown-menu > li > a {
+  color: var(--text-dropdown-menu-color);
+}
+
+pre {
+  border: 1px solid var(--border-pre-color);
+  background-color: var(--bg-pre-color);
+  color: var(--text-pre-color);
+}
+json-tree .key {
+  color: var(--text-json-tree-color);
+}
+
+json-tree .leaf-value {
+  color: var(--text-json-tree-leaf-color);
+}
+
+json-tree .branch-preview {
+  color: var(--text-json-tree-branch-preview-color);
+}
+
+.progress {
+  background-color: var(--bg-progress-color);
+}
+
+.pagination > .disabled > span,
+.pagination > .disabled > span:hover,
+.pagination > .disabled > span:focus,
+.pagination > .disabled > a,
+.pagination > .disabled > a:hover,
+.pagination > .disabled > a:focus {
+  color: var(--text-pagination-color);
+  background-color: var(--bg-pagination-color);
+  border-color: var(--border-pagination-color);
+}
+
+.pagination > li > a,
+.pagination > li > span {
+  background-color: var(--bg-pagination-span-color);
+  border-color: var(--border-pagination-span-color);
+  color: var(--text-pagination-span-color);
+}
+
+.pagination > li > a:hover,
+.pagination > li > span:hover,
+.pagination > li > a:focus,
+.pagination > li > span:focus {
+  background-color: var(--bg-pagination-hover-color);
+  border-color: var(--border-pagination-hover-color);
+}
+
+.pagination > li > a:hover,
+.pagination > li > span:hover,
+.pagination > li > a:focus,
+.pagination > li > span:focus {
+  color: var(--text-pagination-span-hover-color);
+}
+
+.ui-select-bootstrap .ui-select-choices-row > span {
+  color: var(--text-ui-select-color);
+}
+
+.ui-select-bootstrap .ui-select-choices-row > span:hover,
+.ui-select-bootstrap .ui-select-choices-row > span:focus {
+  background-color: var(--bg-ui-select-hover-color);
+  color: var(--text-ui-select-hover-color);
+}
+
+.motd-body {
+  background-color: var(--bg-motd-body-color) !important;
+}
+
+.panel-body {
+  background-color: var(--bg-panel-body-color) !important;
+}
+
+.panel {
+  border: 1px solid var(--border-panel-color);
+}
+
+.theme-information .col-sm-12 {
+  padding-left: 0px;
+  padding-right: 0px;
+  margin-top: 15px;
+}
+
+.theme-panel {
+  margin-top: 15px;
+}
+
+.summary {
+  color: var(--text-summary-color);
+  font-weight: 700;
+}
+
+.input-sm {
+  background-color: var(--bg-input-sm-color);
+  border: 1px solid var(--border-input-sm-color);
+}
+
+.rzslider .rz-bubble {
+  color: var(--text-rzslider-color);
+}
+
+.rzslider .rz-bubble.rz-limit {
+  color: var(--text-rzslider-limit-color);
+}
+input,
+button,
+select,
+textarea {
+  background: var(--text-input-textarea);
+}
+
+.daterangepicker {
+  background-color: var(--bg-daterangepicker-color);
+  border: 1px solid var(--border-daterangepicker-color);
+}
+
+.daterangepicker .drp-calendar.left {
+  background: var(--bg-calendar-color);
+}
+
+.daterangepicker .drp-calendar.left .calendar-table {
+  background: var(--bg-calendar-table-color);
+}
+
+.daterangepicker .drp-calendar.right {
+  background: var(--bg-calendar-color);
+}
+
+.daterangepicker .drp-calendar.right .calendar-table {
+  background: var(--bg-calendar-table-color);
+}
+
+.daterangepicker .calendar-table {
+  border: 1px solid var(--border-calendar-table);
+}
+
+.daterangepicker td.off,
+.daterangepicker td.off.in-range,
+.daterangepicker td.off.start-date,
+.daterangepicker td.off.end-date {
+  background-color: var(--bg-daterangepicker-end-date);
+  color: var(--text-daterangepicker-end-date);
+}
+
+.daterangepicker td.available:hover,
+.daterangepicker th.available:hover {
+  background-color: var(--bg-daterangepicker-hover);
+}
+
+.daterangepicker td.in-range {
+  background-color: var(--bg-daterangepicker-in-range);
+  color: var(--text-daterangepicker-in-range);
+}
+
+.daterangepicker td.active,
+.daterangepicker td.active:hover {
+  background-color: var(--bg-daterangepicker-active);
+  color: var(--text-daterangepicker-active);
+}
+
+.daterangepicker .drp-buttons {
+  border-top: 1px solid var(--border-daterangepicker);
+}
+
+.daterangepicker .calendar-table .next span,
+.daterangepicker .calendar-table .prev span {
+  border-color: var(--border-pre-next-month);
+}
+
+.daterangepicker:after {
+  border-bottom: 6px solid var(--border-daterangepicker-after);
+}
+
+input:-webkit-autofill,
+input:-webkit-autofill:hover,
+input:-webkit-autofill:focus,
+input:-webkit-autofill:active {
+  -webkit-box-shadow: 0 0 0 30px var(--bg-input-autofill-color) inset !important;
+  box-shadow: 0 0 0 30px var(--bg-input-autofill-color) inset !important;
+}
+
+input:-webkit-autofill {
+  -webkit-text-fill-color: var(--text-input-autofill-color) !important;
+}
+
+.btn:hover {
+  color: var(--text-button-hover-color);
+}
+
+.btn-default:hover {
+  background-color: var(--bg-btn-default-hover-color);
+}
+
+.btn-primary:hover {
+  color: var(--white-color) !important;
+}
+/* Overide Vendor CSS */
diff --git a/app/docker/components/datatables/container-networks-datatable/containerNetworksDatatable.html b/app/docker/components/datatables/container-networks-datatable/containerNetworksDatatable.html
index 77ce34153..f1a81acaf 100644
--- a/app/docker/components/datatables/container-networks-datatable/containerNetworksDatatable.html
+++ b/app/docker/components/datatables/container-networks-datatable/containerNetworksDatatable.html
@@ -73,7 +73,11 @@
                 </button>
               </td>
             </tr>
-            <tr dir-paginate-end ng-show="$ctrl.itemCanExpand(value) && value.Expanded" ng-style="{ background: value.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
+            <tr
+              dir-paginate-end
+              ng-show="$ctrl.itemCanExpand(value) && value.Expanded"
+              ng-class="{ 'datatable-highlighted': value.Highlighted, 'datatable-unhighlighted': !value.Highlighted }"
+            >
               <td colspan="1"></td>
               <td colspan="1">
                 {{ value.GlobalIPv6Address }}
diff --git a/app/docker/components/datatables/networks-datatable/networksDatatable.html b/app/docker/components/datatables/networks-datatable/networksDatatable.html
index bf54ca837..22aeb605f 100644
--- a/app/docker/components/datatables/networks-datatable/networksDatatable.html
+++ b/app/docker/components/datatables/networks-datatable/networksDatatable.html
@@ -171,7 +171,7 @@
               allow-checkbox="true"
             >
             </tr>
-            <tr dir-paginate-end ng-show="item.Expanded" ng-repeat="it in item.Subs" style="background: #d5e8f3;" network-row-content item="it" parent-ctrl="$ctrl"> </tr>
+            <tr dir-paginate-end ng-show="item.Expanded" ng-repeat="it in item.Subs" class="datatable-highlighted" network-row-content item="it" parent-ctrl="$ctrl"> </tr>
             <tr ng-if="!$ctrl.dataset">
               <td colspan="9" class="text-center text-muted">Loading...</td>
             </tr>
diff --git a/app/docker/components/datatables/service-tasks-datatable/serviceTasksDatatable.html b/app/docker/components/datatables/service-tasks-datatable/serviceTasksDatatable.html
index 285f2263e..521e324a9 100644
--- a/app/docker/components/datatables/service-tasks-datatable/serviceTasksDatatable.html
+++ b/app/docker/components/datatables/service-tasks-datatable/serviceTasksDatatable.html
@@ -1,6 +1,6 @@
-<div style="background-color: #d5e8f3; padding: 2px;">
+<div class="service-datatable">
   <table class="table table-condensed table-hover nowrap-cells">
-    <thead style="background-color: #e7f6ff;">
+    <thead>
       <tr>
         <th uib-dropdown dropdown-append-to-body auto-close="disabled" is-open="$ctrl.filters.state.open" style="width: 10%;">
           <a ng-click="$ctrl.changeOrderBy('Status.State')">
@@ -54,7 +54,7 @@
         </th>
       </tr>
     </thead>
-    <tbody style="background-color: #f1f9fd;">
+    <tbody>
       <tr
         ng-repeat="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter: $ctrl.applyFilters | filter:$ctrl.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder))"
       >
diff --git a/app/kubernetes/components/datatables/applications-ports-datatable/applicationsPortsDatatable.html b/app/kubernetes/components/datatables/applications-ports-datatable/applicationsPortsDatatable.html
index 814ff9ebf..ec593a0a0 100644
--- a/app/kubernetes/components/datatables/applications-ports-datatable/applicationsPortsDatatable.html
+++ b/app/kubernetes/components/datatables/applications-ports-datatable/applicationsPortsDatatable.html
@@ -105,8 +105,7 @@
             <!-- dir-paginate-start track by $index -->
             <tr
               dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | filter: $ctrl.isDisplayed | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
-              ng-class="{ active: item.Checked }"
-              ng-style="{ background: item.Highlighted ? '#d5e8f3' : '' }"
+              ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
               ng-click="$ctrl.expandItem(item, !item.Expanded)"
               pagination-id="$ctrl.tableKey"
             >
@@ -177,7 +176,7 @@
               </td>
             </tr>
             <!-- sub rows -->
-            <tr ng-show="item.Expanded" ng-repeat-start="port in item.Ports" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
+            <tr ng-show="item.Expanded" ng-repeat-start="port in item.Ports" ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }">
               <td ng-if="!$ctrl.portHasIngressRules(port)"></td>
               <td ng-if="!$ctrl.portHasIngressRules(port)">-</td>
               <td ng-if="!$ctrl.portHasIngressRules(port)">-</td>
@@ -190,7 +189,12 @@
               <td ng-if="!$ctrl.portHasIngressRules(port)">{{ port.TargetPort }}/{{ port.Protocol }}</td>
               <td ng-if="!$ctrl.portHasIngressRules(port)">-</td>
             </tr>
-            <tr ng-show="item.Expanded" ng-repeat-end ng-repeat="rule in port.IngressRules" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
+            <tr
+              ng-show="item.Expanded"
+              ng-repeat-end
+              ng-repeat="rule in port.IngressRules"
+              ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
+            >
               <td></td>
               <td>-</td>
               <td>-</td>
diff --git a/app/kubernetes/components/datatables/applications-stacks-datatable/applicationsStacksDatatable.html b/app/kubernetes/components/datatables/applications-stacks-datatable/applicationsStacksDatatable.html
index d27aa586a..324a75644 100644
--- a/app/kubernetes/components/datatables/applications-stacks-datatable/applicationsStacksDatatable.html
+++ b/app/kubernetes/components/datatables/applications-stacks-datatable/applicationsStacksDatatable.html
@@ -109,8 +109,7 @@
           <tbody>
             <tr
               dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | filter: $ctrl.isDisplayed | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
-              ng-class="{ active: item.Checked }"
-              ng-style="{ background: item.Highlighted ? '#d5e8f3' : '' }"
+              ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
               ng-click="$ctrl.expandItem(item, !item.Expanded)"
               pagination-id="$ctrl.tableKey"
             >
@@ -141,7 +140,12 @@
                 <a ui-sref="kubernetes.stacks.stack.logs({ namespace: item.ResourcePool, name: item.Name })"> <i class="fa fa-file-alt" aria-hidden="true"></i> Logs </a>
               </td>
             </tr>
-            <tr dir-paginate-end ng-show="item.Expanded" ng-repeat="app in item.Applications" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
+            <tr
+              dir-paginate-end
+              ng-show="item.Expanded"
+              ng-repeat="app in item.Applications"
+              ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
+            >
               <td></td>
               <td colspan="4">
                 <a ui-sref="kubernetes.applications.application({ name: app.Name, namespace: app.ResourcePool })">{{ app.Name }}</a>
diff --git a/app/kubernetes/views/applications/edit/components/placements-datatable/template.html b/app/kubernetes/views/applications/edit/components/placements-datatable/template.html
index 50bd58670..9622f2225 100644
--- a/app/kubernetes/views/applications/edit/components/placements-datatable/template.html
+++ b/app/kubernetes/views/applications/edit/components/placements-datatable/template.html
@@ -75,8 +75,7 @@
           <tbody>
             <tr
               dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | filter: $ctrl.isDisplayed | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
-              ng-class="{ active: item.Checked }"
-              ng-style="{ background: item.Highlighted ? '#d5e8f3' : '' }"
+              ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
               ng-click="$ctrl.expandItem(item, !item.Expanded)"
               pagination-id="$ctrl.tableKey"
             >
@@ -92,14 +91,23 @@
               </td>
             </tr>
             <!-- ADMIN + UNMET TAINTS -->
-            <tr ng-if="$ctrl.isAdmin" ng-show="item.Expanded" ng-repeat="taint in item.UnmetTaints" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
+            <tr
+              ng-if="$ctrl.isAdmin"
+              ng-show="item.Expanded"
+              ng-repeat="taint in item.UnmetTaints"
+              ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
+            >
               <td colspan="2">
                 This application is missing a toleration for the taint <code>{{ taint.Key }}{{ taint.Value ? '=' + taint.Value : '' }}:{{ taint.Effect }}</code>
               </td>
             </tr>
             <!-- !ADMIN + UNMET TAINTS -->
             <!-- USER + UNMET TAINTS -->
-            <tr ng-if="!$ctrl.isAdmin && item.UnmetTaints.length" ng-show="item.Expanded" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
+            <tr
+              ng-if="!$ctrl.isAdmin && item.UnmetTaints.length"
+              ng-show="item.Expanded"
+              ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
+            >
               <td colspan="2">
                 Placement constraint not respected for that node.
               </td>
@@ -110,7 +118,7 @@
               ng-if="$ctrl.isAdmin"
               ng-show="item.Expanded"
               ng-repeat="label in item.UnmatchedNodeSelectorLabels"
-              ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }"
+              ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
             >
               <td colspan="2">
                 This application can only be scheduled on a node where the label <code>{{ label.key }}</code> is set to <code>{{ label.value }}</code>
@@ -121,7 +129,7 @@
             <tr
               ng-if="!$ctrl.isAdmin && (item.UnmatchedNodeSelectorLabels.length || item.UnmatchedNodeAffinities.length)"
               ng-show="item.Expanded"
-              ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }"
+              ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
             >
               <td colspan="2">
                 Placement label not respected for that node.
@@ -129,7 +137,11 @@
             </tr>
             <!-- ! USER + UNMET NODE SELECTOR LABELS || UNMET NODE AFFINITIES -->
             <!-- ADMIN + UNMET NODE AFFINITIES -->
-            <tr ng-if="$ctrl.isAdmin" ng-show="item.Expanded && item.UnmatchedNodeAffinities.length" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
+            <tr
+              ng-if="$ctrl.isAdmin"
+              ng-show="item.Expanded && item.UnmatchedNodeAffinities.length"
+              ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
+            >
               <td colspan="2">
                 This application can only be scheduled on nodes respecting one of the following labels combination:
               </td>
@@ -139,7 +151,7 @@
               ng-if="$ctrl.isAdmin"
               ng-show="item.Expanded"
               ng-repeat="aff in item.UnmatchedNodeAffinities"
-              ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }"
+              ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
             >
               <td></td>
               <td>
diff --git a/app/kubernetes/views/resource-pools/edit/components/ingresses-datatable/template.html b/app/kubernetes/views/resource-pools/edit/components/ingresses-datatable/template.html
index ef78e3734..27a359130 100644
--- a/app/kubernetes/views/resource-pools/edit/components/ingresses-datatable/template.html
+++ b/app/kubernetes/views/resource-pools/edit/components/ingresses-datatable/template.html
@@ -73,8 +73,7 @@
           <tbody>
             <tr
               dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
-              ng-class="{ active: item.Checked }"
-              ng-style="{ background: item.Highlighted ? '#d5e8f3' : '' }"
+              ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
               ng-click="$ctrl.expandItem(item, !item.Expanded)"
               pagination-id="$ctrl.tableKey"
             >
@@ -84,7 +83,12 @@
                 >{{ item.Name }}
               </td>
             </tr>
-            <tr dir-paginate-end ng-show="item.Expanded" ng-repeat="path in item.Paths" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
+            <tr
+              dir-paginate-end
+              ng-show="item.Expanded"
+              ng-repeat="path in item.Paths"
+              ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
+            >
               <td>
                 <a style="margin-left: 15px;" ng-href="http://{{ path.Host ? path.Host : path.IP }}{{ path.Path }}" target="_blank">
                   {{ path.Host ? path.Host : path.IP }}{{ path.Path }}
diff --git a/app/kubernetes/views/summary/summary.html b/app/kubernetes/views/summary/summary.html
index 8ab3e4f16..cb62834f9 100644
--- a/app/kubernetes/views/summary/summary.html
+++ b/app/kubernetes/views/summary/summary.html
@@ -22,7 +22,7 @@
       <li ng-repeat="summary in $ctrl.state.resources" ng-if="summary.action && summary.kind && summary.name">
         {{ summary.action }}
         {{ $ctrl.getArticle(summary.kind, summary.action) }}
-        <span style="color: black; font-weight: 700;">{{ summary.kind }}</span> named <code>{{ summary.name }}</code>
+        <span class="summary">{{ summary.kind }}</span> named <code>{{ summary.name }}</code>
         <span ng-if="summary.type">
           of type <code>{{ summary.type }}</code></span
         >
diff --git a/app/kubernetes/views/volumes/components/volumes-storages-datatable/template.html b/app/kubernetes/views/volumes/components/volumes-storages-datatable/template.html
index b0130ab5c..b912d3f41 100644
--- a/app/kubernetes/views/volumes/components/volumes-storages-datatable/template.html
+++ b/app/kubernetes/views/volumes/components/volumes-storages-datatable/template.html
@@ -82,8 +82,7 @@
           <tbody>
             <tr
               dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
-              ng-class="{ active: item.Checked }"
-              ng-style="{ background: item.Highlighted ? '#d5e8f3' : '' }"
+              ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
               ng-click="$ctrl.expandItem(item, !item.Expanded)"
               pagination-id="$ctrl.tableKey"
             >
@@ -95,7 +94,12 @@
               <td>{{ item.Name }}</td>
               <td>{{ item.Size }}</td>
             </tr>
-            <tr dir-paginate-end ng-show="item.Expanded" ng-repeat="vol in item.Volumes" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
+            <tr
+              dir-paginate-end
+              ng-show="item.Expanded"
+              ng-repeat="vol in item.Volumes"
+              ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
+            >
               <td></td>
               <td>
                 <a ui-sref="kubernetes.volumes.volume({ name: vol.PersistentVolumeClaim.Name, namespace: vol.PersistentVolumeClaim.Namespace })">
diff --git a/app/portainer/components/box-selector/box-selector.css b/app/portainer/components/box-selector/box-selector.css
index b359cb4df..97091a4fa 100644
--- a/app/portainer/components/box-selector/box-selector.css
+++ b/app/portainer/components/box-selector/box-selector.css
@@ -27,22 +27,27 @@
 
 .boxselector_wrapper input[type='radio']:not(:disabled) ~ label {
   cursor: pointer;
+  background-color: var(--bg-boxselector-wrapper-disabled-color);
+}
+
+.boxselector_wrapper input[type='radio']:not(:disabled):hover ~ label:hover {
+  cursor: pointer;
 }
 
 .boxselector_wrapper label {
   font-weight: normal;
   font-size: 12px;
   display: block;
-  background: white;
-  border: 1px solid #333333;
+  background: var(--bg-boxselector-color);
+  border: 1px solid var(--border-boxselector-color);
   border-radius: 2px;
   padding: 10px 10px 0 10px;
   text-align: center;
-  box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5);
+  box-shadow: var(--shadow-boxselector-color);
   position: relative;
 }
 .boxselector_wrapper label.boxselector_disabled {
-  background: #cacaca;
+  background: var(--bg-boxselector-disabled-color) !important;
   border-color: #787878;
   color: #787878;
   cursor: not-allowed;
diff --git a/app/portainer/components/custom-templates-list/customTemplatesList.html b/app/portainer/components/custom-templates-list/customTemplatesList.html
index 5d7c9228b..73b96911f 100644
--- a/app/portainer/components/custom-templates-list/customTemplatesList.html
+++ b/app/portainer/components/custom-templates-list/customTemplatesList.html
@@ -8,7 +8,7 @@
         <button type="button" class="btn btn-sm btn-primary" ui-state="$ctrl.createPath"> <i class="fa fa-plus space-right" aria-hidden="true"></i>Add Custom Template </button>
       </div>
 
-      <div class="searchBar" style="border-top: 2px solid #f6f6f6;">
+      <div class="searchBar">
         <i class="fa fa-search searchIcon" aria-hidden="true"></i>
         <input
           type="text"
diff --git a/app/portainer/components/datatables/datatable.css b/app/portainer/components/datatables/datatable.css
index 4f7c1b9f1..c7f17b4c5 100644
--- a/app/portainer/components/datatables/datatable.css
+++ b/app/portainer/components/datatables/datatable.css
@@ -1,6 +1,6 @@
 .datatable .toolBar {
-  background-color: #f6f6f6;
-  color: #767676;
+  background-color: var(--bg-card-color);
+  color: var(--text-main-color);
   overflow: auto;
   padding: 10px;
 }
@@ -30,9 +30,10 @@
 }
 
 .datatable .searchBar {
-  border-top: 1px solid #d2d1d1;
-  border-bottom: 1px solid #d2d1d1;
+  border-top: 1px solid var(--border-color);
+  border-bottom: 1px solid var(--border-color);
   padding: 8px;
+  background: var(--bg-main-color);
 }
 
 .datatable .searchInput {
@@ -60,9 +61,10 @@
 }
 
 .datatable .footer {
-  background-color: #f6f6f6;
-  color: #767676;
+  background-color: var(--bg-card-color);
+  color: var(--text-main-color);
   overflow: auto;
+  border-top: 1px solid var(--border-datatable-top-color);
 }
 
 .datatable .footer .infoBar {
@@ -152,9 +154,8 @@
 }
 
 .md-checkbox label:before {
-  background: #fff;
-  border: 2px solid black;
-  border: 2px solid rgba(0, 0, 0, 0.54);
+  background: var(--bg-main-color);
+  border: 2px solid var(--bg-checkbox-border-color);
   border-radius: 2px;
   cursor: pointer;
   height: 16px;
diff --git a/app/portainer/components/sidebar/sidebar.css b/app/portainer/components/sidebar/sidebar.css
index e5d27df9b..ad6016ddb 100644
--- a/app/portainer/components/sidebar/sidebar.css
+++ b/app/portainer/components/sidebar/sidebar.css
@@ -13,7 +13,7 @@
  * Sidebar
  */
 #sidebar-wrapper {
-  background: #30426a;
+  background: var(--bg-sidebar-wrapper-color);
 }
 
 ul.sidebar .sidebar-main a,
@@ -21,7 +21,7 @@ ul.sidebar .sidebar-main a,
 ul.sidebar .sidebar-list a:hover,
 #page-wrapper:not(.open) ul.sidebar .sidebar-title.separator {
   /* Sidebar header and footer color */
-  background: #2d3e63;
+  background: var(--hover-sidebar-color);
 }
 
 ul.sidebar {
@@ -63,7 +63,7 @@ ul.sidebar .sidebar-main .menu-icon {
 }
 
 ul.sidebar .sidebar-title {
-  color: #738bc0;
+  color: var(--text-sidebar-title-color);
   font-size: 12px;
   height: 35px;
   line-height: 40px;
@@ -74,7 +74,7 @@ ul.sidebar .sidebar-title {
 ul.sidebar .sidebar-list a {
   text-indent: 25px;
   font-size: 15px;
-  color: #b2bfdc;
+  color: var(--text-sidebar-list-color);
   line-height: 40px;
   padding-left: 5px;
   border-left: 3px solid transparent;
@@ -132,7 +132,7 @@ ul.sidebar .sidebar-list .menu-icon {
 }
 
 .sidebar-footer div a {
-  color: #b2bfdc;
+  color: var(--text-sidebar-list-color);
   font-size: 12px;
   line-height: 43px;
 }
@@ -172,8 +172,8 @@ ul.sidebar .sidebar-list a {
 
 ul.sidebar .sidebar-list a.active {
   color: #fff;
-  border-left-color: #fff;
-  background: #2d3e63;
+  border-left-color: var(--border-sidebar-color);
+  background: var(--hover-sidebar-color);
 }
 
 .sidebar-header {
@@ -181,7 +181,7 @@ ul.sidebar .sidebar-list a.active {
   list-style: none;
   text-indent: 20px;
   font-size: 18px;
-  background: #2d3e63;
+  background: var(--bg-sidebar-header-color);
 }
 
 .sidebar-header a {
@@ -255,7 +255,7 @@ ul.sidebar .sidebar-list a.active .menu-icon {
 ul.sidebar .sidebar-list .sidebar-sublist a {
   text-indent: 35px;
   font-size: 12px;
-  color: #b2bfdc;
+  color: var(--text-sidebar-list-color);
   line-height: 36px;
 }
 
@@ -280,7 +280,7 @@ ul.sidebar .sidebar-list .menu-icon {
 ul.sidebar .sidebar-list .sidebar-sublist a.active {
   color: #fff;
   border-left: 3px solid #fff;
-  background: #2d3e63;
+  background: var(--bg-sidebar-color);
 }
 
 @media (max-height: 785px) {
diff --git a/app/portainer/components/template-list/templateList.html b/app/portainer/components/template-list/templateList.html
index d323b2700..d516651d5 100644
--- a/app/portainer/components/template-list/templateList.html
+++ b/app/portainer/components/template-list/templateList.html
@@ -32,7 +32,7 @@
         </div>
       </div>
 
-      <div class="searchBar" style="border-top: 2px solid #f6f6f6;">
+      <div class="searchBar">
         <i class="fa fa-search searchIcon" aria-hidden="true"></i>
         <input
           type="text"
diff --git a/app/portainer/components/theme/theme-settings.controller.js b/app/portainer/components/theme/theme-settings.controller.js
new file mode 100644
index 000000000..b65e4553a
--- /dev/null
+++ b/app/portainer/components/theme/theme-settings.controller.js
@@ -0,0 +1,70 @@
+import { buildOption } from '@/portainer/components/box-selector';
+
+export default class ThemeSettingsController {
+  /* @ngInject */
+  constructor($async, Authentication, ThemeManager, StateManager, UserService, Notifications) {
+    this.$async = $async;
+    this.Authentication = Authentication;
+    this.ThemeManager = ThemeManager;
+    this.StateManager = StateManager;
+    this.UserService = UserService;
+    this.Notifications = Notifications;
+
+    this.setTheme = this.setTheme.bind(this);
+  }
+
+  /** Theme Settings Panel */
+  async updateTheme() {
+    try {
+      await this.UserService.updateUserTheme(this.state.userId, this.state.userTheme);
+      this.state.themeInProgress = false;
+      this.Notifications.success('Success', 'User theme successfully updated');
+    } catch (err) {
+      this.Notifications.error('Failure', err, 'Unable to update user theme');
+    }
+  }
+
+  setTheme(theme) {
+    this.ThemeManager.setTheme(theme);
+    this.state.themeInProgress = true;
+  }
+
+  $onInit() {
+    return this.$async(async () => {
+      this.state = {
+        userId: null,
+        userTheme: '',
+        initTheme: '',
+        defaultTheme: 'light',
+        themeInProgress: false,
+      };
+
+      this.state.availableThemes = [
+        buildOption('light', 'fas fa-sun', 'Light Theme', 'Default color mode', 'light'),
+        buildOption('dark', 'fas fa-moon', 'Dark Theme', 'Dark color mode', 'dark'),
+        buildOption('highcontrast', 'fas fa-adjust', 'High Contrast', 'High contrast color mode', 'highcontrast'),
+      ];
+
+      this.state.availableTheme = {
+        light: 'light',
+        dark: 'dark',
+        highContrast: 'highcontrast',
+      };
+
+      try {
+        this.state.userId = await this.Authentication.getUserDetails().ID;
+        const data = await this.UserService.user(this.state.userId);
+        this.state.userTheme = data.UserTheme || this.state.defaultTheme;
+        this.state.initTheme = this.state.userTheme;
+      } catch (err) {
+        this.Notifications.error('Failure', err, 'Unable to get user details');
+      }
+    });
+  }
+
+  $onDestroy() {
+    if (this.state.themeInProgress) {
+      this.ThemeManager.setTheme(this.state.initTheme);
+    }
+  }
+}
diff --git a/app/portainer/components/theme/theme-settings.html b/app/portainer/components/theme/theme-settings.html
new file mode 100644
index 000000000..1d5e8b348
--- /dev/null
+++ b/app/portainer/components/theme/theme-settings.html
@@ -0,0 +1,19 @@
+<information-panel class="theme-information" title-text="Information">
+  <span class="small text-muted">
+    <p>
+      <i class="fa fa-flask orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
+      Dark and High-contrast theme are experimental. Some UI components might not display properly.
+    </p>
+  </span>
+</information-panel>
+<rd-widget>
+  <rd-widget-header icon="fa-palette" title-text="Change user theme"></rd-widget-header>
+  <rd-widget-body>
+    <form class="theme-panel">
+      <!-- Theme -->
+      <box-selector radio-name="theme" ng-model="$ctrl.state.userTheme" options="$ctrl.state.availableThemes" on-change="($ctrl.setTheme)"></box-selector>
+      <button ng-click="$ctrl.updateTheme()" class="btn btn-primary btn-sm">Update theme</button>
+      <!-- !Theme -->
+    </form>
+  </rd-widget-body>
+</rd-widget>
diff --git a/app/portainer/components/theme/theme-settings.js b/app/portainer/components/theme/theme-settings.js
new file mode 100644
index 000000000..d8d57beb5
--- /dev/null
+++ b/app/portainer/components/theme/theme-settings.js
@@ -0,0 +1,7 @@
+import angular from 'angular';
+import controller from './theme-settings.controller';
+
+angular.module('portainer.app').component('themeSettings', {
+  templateUrl: './theme-settings.html',
+  controller,
+});
diff --git a/app/portainer/models/user.js b/app/portainer/models/user.js
index e86be99f0..4090e9100 100644
--- a/app/portainer/models/user.js
+++ b/app/portainer/models/user.js
@@ -2,6 +2,7 @@ export function UserViewModel(data) {
   this.Id = data.Id;
   this.Username = data.Username;
   this.Role = data.Role;
+  this.UserTheme = data.UserTheme;
   if (data.Role === 1) {
     this.RoleName = 'administrator';
   } else {
diff --git a/app/portainer/rest/user.js b/app/portainer/rest/user.js
index f87314103..2f2bb24c6 100644
--- a/app/portainer/rest/user.js
+++ b/app/portainer/rest/user.js
@@ -12,6 +12,7 @@ angular.module('portainer.app').factory('Users', [
         get: { method: 'GET', params: { id: '@id' } },
         update: { method: 'PUT', params: { id: '@id' }, ignoreLoadingBar: true },
         updatePassword: { method: 'PUT', params: { id: '@id', entity: 'passwd' } },
+        updateTheme: { method: 'PUT', params: { id: '@id' } },
         remove: { method: 'DELETE', params: { id: '@id' } },
         queryMemberships: { method: 'GET', isArray: true, params: { id: '@id', entity: 'memberships' } },
         checkAdminUser: { method: 'GET', params: { id: 'admin', entity: 'check' }, isArray: true, ignoreLoadingBar: true },
diff --git a/app/portainer/services/api/userService.js b/app/portainer/services/api/userService.js
index ef23fffc3..bb6e8622c 100644
--- a/app/portainer/services/api/userService.js
+++ b/app/portainer/services/api/userService.js
@@ -91,6 +91,10 @@ angular.module('portainer.app').factory('UserService', [
       return Users.updatePassword({ id: id }, payload).$promise;
     };
 
+    service.updateUserTheme = function (id, userTheme) {
+      return Users.updateTheme({ id }, { userTheme }).$promise;
+    };
+
     service.userMemberships = function (id) {
       var deferred = $q.defer();
 
diff --git a/app/portainer/services/authentication.js b/app/portainer/services/authentication.js
index c4bdcca9d..3a5073301 100644
--- a/app/portainer/services/authentication.js
+++ b/app/portainer/services/authentication.js
@@ -9,7 +9,9 @@ angular.module('portainer.app').factory('Authentication', [
   'LocalStorage',
   'StateManager',
   'EndpointProvider',
-  function AuthenticationFactory($async, $state, Auth, OAuth, jwtHelper, LocalStorage, StateManager, EndpointProvider) {
+  'UserService',
+  'ThemeManager',
+  function AuthenticationFactory($async, $state, Auth, OAuth, jwtHelper, LocalStorage, StateManager, EndpointProvider, UserService, ThemeManager) {
     'use strict';
 
     var service = {};
@@ -82,12 +84,20 @@ angular.module('portainer.app').factory('Authentication', [
       return user;
     }
 
+    async function setUserTheme() {
+      const data = await UserService.user(user.ID);
+      // Initialize user theme base on Usertheme from database
+      const userTheme = data.UserTheme;
+      ThemeManager.setTheme(userTheme);
+    }
+
     async function setUser(jwt) {
       LocalStorage.storeJWT(jwt);
       var tokenPayload = jwtHelper.decodeToken(jwt);
       user.username = tokenPayload.username;
       user.ID = tokenPayload.id;
       user.role = tokenPayload.role;
+      await setUserTheme();
     }
 
     function isAdmin() {
diff --git a/app/portainer/services/stateManager.js b/app/portainer/services/stateManager.js
index 9e9dbd383..047a29813 100644
--- a/app/portainer/services/stateManager.js
+++ b/app/portainer/services/stateManager.js
@@ -54,6 +54,11 @@ angular.module('portainer.app').factory('StateManager', [
       LocalStorage.storeApplicationState(state.application);
     };
 
+    manager.updateTheme = function (theme) {
+      state.application.theme = theme;
+      LocalStorage.storeApplicationState(state.application);
+    };
+
     manager.updateSnapshotInterval = function (interval) {
       state.application.snapshotInterval = interval;
       LocalStorage.storeApplicationState(state.application);
diff --git a/app/portainer/services/themeManager.js b/app/portainer/services/themeManager.js
new file mode 100644
index 000000000..9cf18137b
--- /dev/null
+++ b/app/portainer/services/themeManager.js
@@ -0,0 +1,23 @@
+angular.module('portainer.app').service('ThemeManager', ThemeManager);
+
+/* @ngInject */
+
+export function ThemeManager(StateManager) {
+  return {
+    setTheme,
+    defaultTheme,
+  };
+
+  function setTheme(theme) {
+    if (!theme) {
+      document.documentElement.removeAttribute('theme');
+    } else {
+      document.documentElement.setAttribute('theme', theme);
+    }
+    StateManager.updateTheme(theme);
+  }
+
+  function defaultTheme() {
+    document.documentElement.removeAttribute('theme');
+  }
+}
diff --git a/app/portainer/views/account/account.html b/app/portainer/views/account/account.html
index db2a4d078..f387f9f21 100644
--- a/app/portainer/views/account/account.html
+++ b/app/portainer/views/account/account.html
@@ -78,5 +78,6 @@
         </form>
       </rd-widget-body>
     </rd-widget>
+    <theme-settings></theme-settings>
   </div>
 </div>
diff --git a/app/portainer/views/account/accountController.js b/app/portainer/views/account/accountController.js
index 7be63dc95..2bde28460 100644
--- a/app/portainer/views/account/accountController.js
+++ b/app/portainer/views/account/accountController.js
@@ -5,11 +5,14 @@ angular.module('portainer.app').controller('AccountController', [
   'UserService',
   'Notifications',
   'SettingsService',
-  function ($scope, $state, Authentication, UserService, Notifications, SettingsService) {
+  'StateManager',
+  'ThemeManager',
+  function ($scope, $state, Authentication, UserService, Notifications, SettingsService, StateManager, ThemeManager) {
     $scope.formValues = {
       currentPassword: '',
       newPassword: '',
       confirmPassword: '',
+      userTheme: '',
     };
 
     $scope.updatePassword = function () {
@@ -23,8 +26,30 @@ angular.module('portainer.app').controller('AccountController', [
         });
     };
 
-    function initView() {
+    // Update DOM for theme attribute & LocalStorage
+    $scope.setTheme = function (theme) {
+      ThemeManager.setTheme(theme);
+      StateManager.updateTheme(theme);
+    };
+
+    // Rest API Call to update theme with userID in DB
+    $scope.updateTheme = function () {
+      UserService.updateUserTheme($scope.userID, $scope.formValues.userTheme)
+        .then(function success() {
+          Notifications.success('Success', 'User theme successfully updated');
+          $state.reload();
+        })
+        .catch(function error(err) {
+          Notifications.error('Failure', err, err.msg);
+        });
+    };
+
+    async function initView() {
       $scope.userID = Authentication.getUserDetails().ID;
+
+      const data = await UserService.user($scope.userID);
+
+      $scope.formValues.userTheme = data.Usertheme;
       SettingsService.publicSettings()
         .then(function success(data) {
           $scope.AuthenticationMethod = data.AuthenticationMethod;
diff --git a/app/portainer/views/logout/logoutController.js b/app/portainer/views/logout/logoutController.js
index 5db7c8ba5..43c9d8878 100644
--- a/app/portainer/views/logout/logoutController.js
+++ b/app/portainer/views/logout/logoutController.js
@@ -2,7 +2,7 @@ import angular from 'angular';
 
 class LogoutController {
   /* @ngInject */
-  constructor($async, $state, $transition$, $window, Authentication, StateManager, Notifications, LocalStorage, SettingsService) {
+  constructor($async, $state, $transition$, $window, Authentication, StateManager, Notifications, LocalStorage, SettingsService, ThemeManager) {
     this.$async = $async;
     this.$state = $state;
     this.$transition$ = $transition$;
@@ -13,6 +13,7 @@ class LogoutController {
     this.Notifications = Notifications;
     this.LocalStorage = LocalStorage;
     this.SettingsService = SettingsService;
+    this.ThemeManager = ThemeManager;
 
     this.logo = this.StateManager.getState().application.logo;
     this.logoutAsync = this.logoutAsync.bind(this);
@@ -28,6 +29,8 @@ class LogoutController {
     const performApiLogout = this.$transition$.params().performApiLogout;
     const settings = await this.SettingsService.publicSettings();
 
+    this.ThemeManager.defaultTheme();
+
     try {
       await this.Authentication.logout(performApiLogout);
     } finally {
diff --git a/app/portainer/views/main/mainController.js b/app/portainer/views/main/mainController.js
index 00fb4436c..a942b62f3 100644
--- a/app/portainer/views/main/mainController.js
+++ b/app/portainer/views/main/mainController.js
@@ -3,6 +3,7 @@ angular.module('portainer.app').controller('MainController', [
   'LocalStorage',
   'StateManager',
   'EndpointProvider',
+  'ThemeManager',
   function ($scope, LocalStorage, StateManager, EndpointProvider) {
     /**
      * Sidebar Toggle & Cookie Control