diff --git a/docs/README.md b/docs/README.md
index e343ff6a3..55a74d9e7 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -200,6 +200,11 @@ In all cases, a tooltip is added to a date+time field that shows the full repres
When using very old browsers, the required date/time functions may not be present. In that case SaltGUI reverts to simply displaying the reported time from the Salt system. The tooltip is then not shown.
+## Theme
+SaltGUI follows the browser color-scheme preference.
+When SaltGUI is embedded, it also uses theme hints from the parent frame when those are available.
+
+
## Templates
SaltGUI supports command templates for easier command entry into the command-box.
The menu item for that becomes visible there when you define one or more templates
diff --git a/saltgui/index.html b/saltgui/index.html
index c455b85af..22ff90cc2 100644
--- a/saltgui/index.html
+++ b/saltgui/index.html
@@ -3,6 +3,8 @@
SaltGUI
+
+
@@ -26,8 +28,8 @@
SaltGUI
-
- >_
+
+ >_
diff --git a/saltgui/static/scripts/Character.js b/saltgui/static/scripts/Character.js
index 46243d45e..4cfb7aa53 100644
--- a/saltgui/static/scripts/Character.js
+++ b/saltgui/static/scripts/Character.js
@@ -64,6 +64,6 @@ export class Character {
}
static buttonInText (txt) {
- return " " + txt + " ";
+ return " " + txt + " ";
}
}
diff --git a/saltgui/static/scripts/Router.js b/saltgui/static/scripts/Router.js
index 017b57a4a..86b43cc94 100644
--- a/saltgui/static/scripts/Router.js
+++ b/saltgui/static/scripts/Router.js
@@ -40,7 +40,7 @@ export class Router {
this.pages = [];
Router.currentPage = undefined;
- this._registerPage(new LoginPage(this));
+ this._registerPage(Router.loginPage = new LoginPage(this));
this._registerPage(Router.minionsPage = new MinionsPage(this));
this._registerPage(Router.keysPage = new KeysPage(this));
this._registerPage(Router.grainsPage = new GrainsPage(this));
@@ -272,7 +272,7 @@ export class Router {
// perform the hiding/showing
for (let nr = 1; nr <= 2; nr++) {
const item = document.getElementById("button-" + pPage.path + nr);
- item.style.color = !visible && hasVisibleChild ? "lightgray" : "black";
+ item.classList.toggle("menu-item-dimmed", !visible && hasVisibleChild);
if (!visible) {
// hide the shortcut indicator
item.classList.remove("menu-item-first-letter");
diff --git a/saltgui/static/scripts/output/OutputDocumentation.js b/saltgui/static/scripts/output/OutputDocumentation.js
index 839c86bc0..5def31632 100644
--- a/saltgui/static/scripts/output/OutputDocumentation.js
+++ b/saltgui/static/scripts/output/OutputDocumentation.js
@@ -270,12 +270,12 @@ export class OutputDocumentation {
// replace ``......``
// e.g. in "sys.doc state.apply"
// named groups are only introduced in ES9/2018
- out = out.replace(/``([^`]*)``/g, "$1 ");
+ out = out.replace(/``([^`]*)``/g, "$1 ");
// replace `......`
// e.g. in "sys.doc state.apply"
// named groups are only introduced in ES9/2018
- out = out.replace(/`([^`]*)`/g, "$1 ");
+ out = out.replace(/`([^`]*)`/g, "$1 ");
// remove whitespace at end of lines
out = out.replace(/ *\n/gm, "");
diff --git a/saltgui/static/scripts/output/OutputHighstateSummaryOriginal.js b/saltgui/static/scripts/output/OutputHighstateSummaryOriginal.js
index 5aada5b86..cc9f18cf2 100644
--- a/saltgui/static/scripts/output/OutputHighstateSummaryOriginal.js
+++ b/saltgui/static/scripts/output/OutputHighstateSummaryOriginal.js
@@ -11,8 +11,7 @@ export class OutputHighstateSummaryOriginal {
let txt = "\nSummary for " + pMinionId;
txt += "\n------------";
- const summarySpan = Utils.createSpan("", txt);
- summarySpan.style.color = "aqua";
+ const summarySpan = Utils.createSpan("text-info", txt);
pDiv.append(summarySpan);
const total = pSucceeded + pSkipped + pFailed;
@@ -24,7 +23,6 @@ export class OutputHighstateSummaryOriginal {
if (pChangesSummary > 0) {
txt = " (";
const oSpan = Utils.createSpan("", txt);
- oSpan.style.color = "white";
pDiv.append(oSpan);
txt = "changed=" + pChangesSummary;
@@ -33,7 +31,6 @@ export class OutputHighstateSummaryOriginal {
txt = ")";
const cSpan = Utils.createSpan("", txt);
- cSpan.style.color = "white";
pDiv.append(cSpan);
}
@@ -42,7 +39,7 @@ export class OutputHighstateSummaryOriginal {
if (pFailed > 0) {
failedSpan.classList.add("task-failure");
} else {
- failedSpan.style.color = "aqua";
+ failedSpan.classList.add("text-info");
}
pDiv.append(failedSpan);
@@ -57,7 +54,7 @@ export class OutputHighstateSummaryOriginal {
if (pFailed > 0) {
failureSpan.classList.add("task-failure");
} else {
- failureSpan.style.color = "aqua";
+ failureSpan.classList.add("text-info");
}
pDiv.append(failureSpan);
}
@@ -65,8 +62,7 @@ export class OutputHighstateSummaryOriginal {
txt = "\n------------";
txt += "\nTotal states run: " + total;
txt += "\nTotal run time: " + Output.getDuration(pTotalMilliSeconds);
- const totalsSpan = Utils.createSpan("", txt);
- totalsSpan.style.color = "aqua";
+ const totalsSpan = Utils.createSpan("text-info", txt);
pDiv.append(totalsSpan);
pDiv.style.cursor = "pointer";
}
diff --git a/saltgui/static/scripts/panels/HighState.js b/saltgui/static/scripts/panels/HighState.js
index 15ebe9394..2bef3bd16 100644
--- a/saltgui/static/scripts/panels/HighState.js
+++ b/saltgui/static/scripts/panels/HighState.js
@@ -440,7 +440,6 @@ export class HighStatePanel extends Panel {
// for information (keys.length > this._maxHighstateStates)
const span = Utils.createSpan("task");
- span.style.backgroundColor = "black";
// this also sets the span's class(es)
Output._setTaskToolTip(span, data);
@@ -516,7 +515,6 @@ export class HighStatePanel extends Panel {
// remove the priority indicator from the key
const itemSpan = Utils.createSpan(["tasksummary", className], character);
- itemSpan.style.backgroundColor = "black";
summarySpan.append(itemSpan);
Utils.addToolTip(itemSpan, className.replace("task-", "").replace("-", " with "));
}
diff --git a/saltgui/static/scripts/panels/Jobs.js b/saltgui/static/scripts/panels/Jobs.js
index 081a120b7..0de70a741 100644
--- a/saltgui/static/scripts/panels/Jobs.js
+++ b/saltgui/static/scripts/panels/Jobs.js
@@ -302,12 +302,13 @@ export class JobsPanel extends Panel {
}
if (newLevel > oldLevel) {
span.dataset.level = newLevel;
+ span.classList.remove("text-success", "text-warning", "text-error");
if (newLevel === 1) {
- span.style.color = "green";
+ span.classList.add("text-success");
} else if (newLevel === 2) {
- span.style.color = "orange";
+ span.classList.add("text-warning");
} else if (newLevel === 3) {
- span.style.color = "red";
+ span.classList.add("text-error");
}
}
span.style.removeProperty("display");
diff --git a/saltgui/static/scripts/panels/JobsDetails.js b/saltgui/static/scripts/panels/JobsDetails.js
index aab538a7e..7dcd9ed07 100644
--- a/saltgui/static/scripts/panels/JobsDetails.js
+++ b/saltgui/static/scripts/panels/JobsDetails.js
@@ -266,16 +266,16 @@ export class JobsDetailsPanel extends JobsPanel {
if (pData.Minions.length === 0) {
detailsHTML += "";
} else if (keyCount === pData.Minions.length) {
- detailsHTML += "";
+ detailsHTML += "";
} else {
- detailsHTML += "";
+ detailsHTML += "";
}
detailsHTML += Utils.txtZeroOneMany(keyCount,
"no results", "{0} result", "{0} results");
detailsHTML += " ";
if (keyCount < pData.Minions.length) {
- detailsHTML += ", ";
+ detailsHTML += ", ";
detailsHTML += pData.Minions.length - keyCount;
detailsHTML += " missing ";
}
@@ -299,13 +299,13 @@ export class JobsDetailsPanel extends JobsPanel {
for (const key of keys) {
detailsHTML += ", ";
if (key === "0-0") {
- detailsHTML += "";
+ detailsHTML += "";
detailsHTML += Utils.txtZeroOneMany(summary[key], "", "{0} success", "{0} successes");
} else if (key.startsWith("0-")) {
- detailsHTML += "";
+ detailsHTML += "";
detailsHTML += Utils.txtZeroOneMany(summary[key], "", "{0} success", "{0} successes");
} else if (key.startsWith("1-")) {
- detailsHTML += "";
+ detailsHTML += "";
detailsHTML += Utils.txtZeroOneMany(summary[key], "", "{0} failure", "{0} failures");
} else {
// if (key.startsWith("2-"))
diff --git a/saltgui/static/scripts/panels/Login.js b/saltgui/static/scripts/panels/Login.js
index 4d324c991..ebdf726a6 100644
--- a/saltgui/static/scripts/panels/Login.js
+++ b/saltgui/static/scripts/panels/Login.js
@@ -260,21 +260,21 @@ export class LoginPanel extends Panel {
break;
case "no-session":
// gray because we cannot prove that the user was/wasnt logged in
- this._showNoticeText("gray", "Not logged in", "notice_not_logged_in");
+ this._showNoticeText("var(--color-notice-muted)", "Not logged in", "notice_not_logged_in");
break;
case "session-cancelled":
- this._showNoticeText("#F44336", "Session cancelled", "notice-session-cancelled");
+ this._showNoticeText("var(--color-notice-danger)", "Session cancelled", "notice-session-cancelled");
break;
case "session-expired":
- this._showNoticeText("#F44336", "Session expired", "notice-session-expired");
+ this._showNoticeText("var(--color-notice-danger)", "Session expired", "notice-session-expired");
break;
case "logout":
// gray because this is the result of a user action
- this._showNoticeText("gray", "Logout", "notice_logout");
+ this._showNoticeText("var(--color-notice-muted)", "Logout", "notice_logout");
break;
default:
// should not occur
- this._showNoticeText("#F44336", reason, "notice_other:" + reason);
+ this._showNoticeText("var(--color-notice-danger)", reason, "notice_other:" + reason);
}
this._enableLoginControls(true);
@@ -303,11 +303,40 @@ export class LoginPanel extends Panel {
}
_onLoginSuccess () {
- this._showNoticeText("#4CAF50", "Please wait" + Character.HORIZONTAL_ELLIPSIS, "notice_please_wait");
+ this._showNoticeText("var(--color-text-accent)", "Please wait" + Character.HORIZONTAL_ELLIPSIS, "notice_please_wait");
Utils.setStorageItem("local", "salt-motd-txt", "");
Utils.setStorageItem("local", "salt-motd-html", "");
+ this.bootstrapSession();
+
+ // allow the success message to be seen
+ window.setTimeout(() => {
+ // erase credentials since we don't do page-refresh
+ this.usernameField.value = "";
+ this.passwordField.value = "";
+ if (Utils.getStorageItem("session", "login_response") !== null) {
+ // we might have been logged out in this first second
+ // e.g. when clock between client and server differs more than the session timout
+ const urlParams = new URLSearchParams(window.location.search);
+ if (urlParams.get("page")) {
+ // a redirect page is specified
+ const params = {};
+ for (const pair of urlParams.entries()) {
+ params[pair[0]] = pair[1];
+ }
+ const page = params["page"];
+ delete params["page"];
+ this.router.goTo(page, params);
+ } else {
+ this.router.goTo("");
+ }
+ }
+ }, 1000);
+
+ }
+
+ bootstrapSession () {
// We need these functions to populate the dropdown boxes
const wheelConfigValuesPromise = this.api.getWheelConfigValues();
const runnerStateOrchestrateShowSlsPromise = this.api.getRunnerStateOrchestrateShowSls();
@@ -339,30 +368,6 @@ export class LoginPanel extends Panel {
});
/* eslint-enable no-unused-vars */
- // allow the success message to be seen
- window.setTimeout(() => {
- // erase credentials since we don't do page-refresh
- this.usernameField.value = "";
- this.passwordField.value = "";
- if (Utils.getStorageItem("session", "login_response") !== null) {
- // we might have been logged out in this first second
- // e.g. when clock between client and server differs more than the session timout
- const urlParams = new URLSearchParams(window.location.search);
- if (urlParams.get("page")) {
- // a redirect page is specified
- const params = {};
- for (const pair of urlParams.entries()) {
- params[pair[0]] = pair[1];
- }
- const page = params["page"];
- delete params["page"];
- this.router.goTo(page, params);
- } else {
- this.router.goTo("");
- }
- }
- }, 1000);
-
BeaconsMinionPanel.getAvailableBeacons(this.api);
}
@@ -517,19 +522,19 @@ export class LoginPanel extends Panel {
_onLoginFailure (error) {
if (typeof error === "string") {
// something detected before trying to login
- this._showNoticeText("#F44336", error, "notice_login_string_error");
+ this._showNoticeText("var(--color-notice-danger)", error, "notice_login_string_error");
} else if (error && error.status === 503) {
// Service Unavailable
// e.g. salt-api running but salt-master not running
- this._showNoticeText("#F44336", error.message, "notice_login_service_unavailable");
+ this._showNoticeText("var(--color-notice-danger)", error.message, "notice_login_service_unavailable");
} else if (error && error.status === -1) {
// No permissions: login valid, but no api functions executable
// e.g. PAM says OK and /etc/salt/master says NO
- this._showNoticeText("#F44336", error.message, "notice_login_other_error");
+ this._showNoticeText("var(--color-notice-danger)", error.message, "notice_login_other_error");
} else if (error.toString().startsWith("TypeError: NetworkError")) {
- this._showNoticeText("#F44336", "Network Error", "notice_login_other_error");
+ this._showNoticeText("var(--color-notice-danger)", "Network Error", "notice_login_other_error");
} else {
- this._showNoticeText("#F44336", "Authentication failed", "notice_auth_failed");
+ this._showNoticeText("var(--color-notice-danger)", "Authentication failed", "notice_auth_failed");
}
this._enableLoginControls(true);
diff --git a/saltgui/static/scripts/panels/Nodegroups.js b/saltgui/static/scripts/panels/Nodegroups.js
index 4506571f9..c141207a4 100644
--- a/saltgui/static/scripts/panels/Nodegroups.js
+++ b/saltgui/static/scripts/panels/Nodegroups.js
@@ -308,7 +308,7 @@ export class NodegroupsPanel extends Panel {
_addNodegroupRow (pNodegroup, pAllNodegroups) {
const tr = Utils.createTr("no-search", null, "ng-" + pNodegroup);
- tr.style.borderTop = "4px double #ddd";
+ tr.style.borderTop = "4px double var(--color-border-default)";
const menuTd = Utils.createTd();
tr.dropdownmenu = new DropDownMenu(menuTd, "smaller");
diff --git a/saltgui/static/scripts/panels/Stats.js b/saltgui/static/scripts/panels/Stats.js
index a51bd5183..445ebeb57 100644
--- a/saltgui/static/scripts/panels/Stats.js
+++ b/saltgui/static/scripts/panels/Stats.js
@@ -69,7 +69,7 @@ export class StatsPanel extends Panel {
_handleStats (pStatsData) {
if (this.showErrorRowInstead(pStatsData)) {
- this.statsTd.innerHTML = "this error is typically caused by using the collect_stats: True setting in the master configuration file, which is broken in at least the recent versions of salt-api ";
+ this.statsTd.innerHTML = "this error is typically caused by using the collect_stats: True setting in the master configuration file, which is broken in at least the recent versions of salt-api ";
window.clearInterval(this.updateStatsInterval);
this.updateStatsInterval = null;
return;
diff --git a/saltgui/static/scripts/theme.js b/saltgui/static/scripts/theme.js
new file mode 100644
index 000000000..a3c383b39
--- /dev/null
+++ b/saltgui/static/scripts/theme.js
@@ -0,0 +1,20 @@
+(function initializeTheme () {
+ const context = globalThis;
+ const root = document.documentElement;
+ const mediaQuery = context.matchMedia ? context.matchMedia("(prefers-color-scheme: dark)") : null;
+
+ function wantsDarkTheme () {
+ return mediaQuery ? mediaQuery.matches : false;
+ }
+
+ function applyTheme () {
+ root.dataset.theme = wantsDarkTheme() ? "dark" : "light";
+ }
+
+ applyTheme();
+ if (mediaQuery && typeof mediaQuery.addEventListener === "function") {
+ mediaQuery.addEventListener("change", applyTheme);
+ } else if (mediaQuery && typeof mediaQuery.addListener === "function") {
+ mediaQuery.addListener(applyTheme);
+ }
+})();
diff --git a/saltgui/static/stylesheets/beacons.css b/saltgui/static/stylesheets/beacons.css
index 0a6c3f902..9f606e139 100644
--- a/saltgui/static/stylesheets/beacons.css
+++ b/saltgui/static/stylesheets/beacons.css
@@ -16,5 +16,5 @@
.beacon-disabled,
.beacon-waiting {
- color: gray;
+ color: var(--color-text-muted);
}
diff --git a/saltgui/static/stylesheets/controls.css b/saltgui/static/stylesheets/controls.css
index 1e473f6e5..ec2b7ce50 100644
--- a/saltgui/static/stylesheets/controls.css
+++ b/saltgui/static/stylesheets/controls.css
@@ -2,7 +2,8 @@
input,
select {
- color: #272727;
+ background-color: var(--color-background-control);
+ color: var(--color-text-strong);
padding: 7px 10px;
display: inline-block;
margin-bottom: 15px;
@@ -14,24 +15,24 @@ select {
input[type="text"],
input[type="password"],
select {
- border: 2px solid #e2e2e2;
+ border: 2px solid var(--color-border-control);
border-radius: 2px;
}
input[type="text"]:focus,
input[type="password"]:focus,
select:focus {
- border: 2px solid #4caf50;
+ border: 2px solid var(--color-border-accent);
}
input[type="submit"] {
- background-color: #4caf50;
- color: white;
+ background-color: var(--color-text-accent);
+ color: var(--color-text-inverse);
border: 0;
margin-top: 5px;
margin-bottom: 0;
cursor: pointer;
- box-shadow: 0 0 5px rgba(33, 33, 33, 50%);
+ box-shadow: var(--color-shadow-button);
width: 20%;
min-width: 200px;
}
diff --git a/saltgui/static/stylesheets/dropdown.css b/saltgui/static/stylesheets/dropdown.css
index 49b8d7c59..7cee2e488 100644
--- a/saltgui/static/stylesheets/dropdown.css
+++ b/saltgui/static/stylesheets/dropdown.css
@@ -13,12 +13,13 @@ div.search-box .menu-dropdown {
position: absolute;
max-height: 300px;
overflow-y: auto;
- background: #fff;
- box-shadow: 0 3px 10px -2px rgba(0, 0, 0, 30%);
- border: 1px solid rgba(0, 0, 0, 10%);
+ background: var(--color-background-panel);
+ box-shadow: var(--color-shadow-dropdown);
+ border: 1px solid var(--color-border-default);
z-index: 5;
cursor: pointer;
text-align: left;
+ color: var(--color-text-primary);
}
/* Links inside the menu-dropdown */
@@ -37,7 +38,7 @@ div.search-box .menu-dropdown {
/* Change color of menu-dropdown links on hover */
.run-command-button .menu-dropdown-content div.run-command-button:hover {
- background: rgba(0, 0, 0, 15%);
+ background: var(--color-background-hover);
cursor: pointer;
}
@@ -60,8 +61,8 @@ pre.output .run-command-button {
}
pre.output .run-command-button:hover .menu-dropdown {
- background-color: #f9f9f9;
- color: black;
+ background-color: var(--color-background-control-soft);
+ color: var(--color-text-default);
}
.dropdown {
@@ -72,8 +73,8 @@ pre.output .run-command-button:hover .menu-dropdown {
.dropdown-content {
display: none;
position: absolute;
- background-color: #f9f9f9;
- box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 20%);
+ background-color: var(--color-background-control-soft);
+ box-shadow: var(--color-shadow-dropdown-large);
z-index: 3;
}
diff --git a/saltgui/static/stylesheets/job.css b/saltgui/static/stylesheets/job.css
index 6a38a4e0d..8c3adf262 100644
--- a/saltgui/static/stylesheets/job.css
+++ b/saltgui/static/stylesheets/job.css
@@ -17,7 +17,7 @@
}
.highlight-task {
- background-color: gray;
+ background-color: var(--color-background-highlight);
}
#summary-list-job {
diff --git a/saltgui/static/stylesheets/login.css b/saltgui/static/stylesheets/login.css
index d5529bb35..77e44881b 100644
--- a/saltgui/static/stylesheets/login.css
+++ b/saltgui/static/stylesheets/login.css
@@ -9,14 +9,14 @@
@media not print {
#page-login {
- background-color: #263238;
+ background-color: var(--color-background-page);
}
}
#login-panel {
align-self: center;
- background-color: white;
- box-shadow: 0 0 24px rgba(0, 0, 0, 70%);
+ background-color: var(--color-background-panel);
+ box-shadow: var(--color-shadow-panel);
border-radius: 2px;
/* 1px needed to prevent bottom margin to disappear when using small screens */
@@ -30,7 +30,7 @@
font-weight: lighter;
font-size: 60px;
width: 100%;
- color: #505050;
+ color: var(--color-text-primary);
}
#login-panel input {
@@ -39,13 +39,26 @@
font-size: 18px;
}
+#login-panel input,
+#login-panel select {
+ background-color: var(--color-background-control);
+ color: var(--color-text-strong);
+ caret-color: var(--color-text-strong);
+ -webkit-text-fill-color: var(--color-text-strong);
+}
+
+#login-panel input::placeholder {
+ color: var(--color-text-muted);
+ opacity: 1;
+}
+
#login-panel select {
width: 100%;
font-size: 14px;
}
#login-panel select option#eauth-default {
- color: gray;
+ color: var(--color-text-muted);
}
.attribution {
@@ -64,7 +77,7 @@
#notice {
height: 0;
overflow-y: hidden;
- color: white;
+ color: var(--color-text-inverse);
padding: 0;
border-radius: 2px;
text-align: center;
diff --git a/saltgui/static/stylesheets/main.css b/saltgui/static/stylesheets/main.css
index fccba7f1a..ee8e39ede 100644
--- a/saltgui/static/stylesheets/main.css
+++ b/saltgui/static/stylesheets/main.css
@@ -10,23 +10,25 @@ body {
margin: 0;
padding: 0;
font-family: Roboto, Helvetica, Arial, sans-serif;
+ color: var(--color-text-primary);
}
@media not print {
body {
- background-color: #263238;
+ background-color: var(--color-background-page);
}
}
header {
- border-top: 2px solid #4caf50;
- background-color: white;
+ border-top: 2px solid var(--color-border-accent);
+ background-color: var(--color-background-header);
+ box-shadow: var(--color-shadow-header);
margin-top: 0;
}
.logo {
cursor: pointer;
- color: #4caf50;
+ color: var(--color-text-accent);
font-size: 30px;
font-weight: normal;
display: inline-block;
@@ -42,7 +44,7 @@ header {
.docu {
cursor: pointer;
- color: gray;
+ color: var(--color-text-muted);
font-size: 30px;
font-weight: normal;
display: inline-block;
@@ -58,11 +60,11 @@ header {
}
.docu:hover {
- color: #4caf50;
+ color: var(--color-text-accent);
}
h1 {
- color: #4caf50;
+ color: var(--color-text-accent);
font-weight: lighter;
font-size: 20px;
margin: 0 10px 0 0;
@@ -70,12 +72,13 @@ h1 {
}
.msg {
- color: #505050;
+ color: var(--color-text-primary);
padding-top: 5px;
}
.panel {
- background-color: white;
+ background-color: var(--color-background-panel);
+ color: var(--color-text-primary);
padding: 20px;
border-radius: 1px;
@@ -114,11 +117,11 @@ h1 {
display: inline-block;
min-width: 50px;
text-align: center;
- background-color: #eee;
+ background-color: var(--color-background-control-muted);
margin: 0;
cursor: pointer;
font-size: 18px;
- color: #666;
+ color: var(--color-text-quiet);
height: 24px;
vertical-align: middle;
padding-left: 10px;
@@ -150,7 +153,7 @@ h1 {
}
.small-button:hover {
- color: #4caf50;
+ color: var(--color-text-accent);
}
.small-button-for-hover {
@@ -174,25 +177,30 @@ h1 {
.search-error {
display: block;
- color: red;
+ color: var(--color-status-failure);
margin-bottom: 10px;
margin-left: 10px;
}
.menu-item {
display: inline-block;
+ color: var(--color-text-default);
font-size: 18px;
font-weight: lighter;
padding: 15px 30px;
}
+.menu-item-dimmed {
+ color: var(--color-menu-dimmed);
+}
+
.menu-item-active {
font-weight: bold;
}
.menu-item:hover {
- background: rgba(0, 0, 0, 15%);
- color: #4caf50;
+ background: var(--color-background-hover);
+ color: var(--color-text-accent);
cursor: pointer;
}
@@ -203,7 +211,7 @@ h1 {
}
.menu-item-first-letter:hover::first-letter {
- text-decoration: underline #4caf50 double;
+ text-decoration: underline var(--color-text-accent) double;
text-decoration-skip-ink: none;
}
@@ -218,7 +226,8 @@ h1 {
}
#warning {
- background: yellow;
+ background: var(--color-background-warning);
+ color: var(--color-text-warning);
margin-top: 5px;
padding: 5px 10px 5px 20px;
}
@@ -253,7 +262,7 @@ h1 {
width: 100%;
height: 100%;
z-index: 2;
- background-color: rgba(0, 0, 0, 86%);
+ background-color: var(--color-background-overlay);
}
.popup h1 {
@@ -265,7 +274,8 @@ h1 {
.run-command {
padding: 30px;
z-index: 3;
- background-color: white;
+ background-color: var(--color-background-popup);
+ color: var(--color-text-primary);
position: relative;
top: 5px;
margin-left: 15px;
@@ -278,8 +288,8 @@ h1 {
}
pre.output {
- background-color: #272727;
- color: white;
+ background-color: var(--color-background-code);
+ color: var(--color-text-inverse);
margin: 0;
padding: 10px;
border-radius: 2px;
@@ -304,10 +314,10 @@ pre.output {
}
.warning-button:hover {
- color: #4caf50;
+ color: var(--color-text-accent);
cursor: pointer;
}
.state-details-compressed {
- color: gray;
+ color: var(--color-text-muted);
}
diff --git a/saltgui/static/stylesheets/page.css b/saltgui/static/stylesheets/page.css
index 492a50ecb..c4fe4ab98 100644
--- a/saltgui/static/stylesheets/page.css
+++ b/saltgui/static/stylesheets/page.css
@@ -20,7 +20,7 @@ pre a.disabled:hover {
}
pre.output a {
- color: yellow;
+ color: var(--color-text-code-link);
cursor: pointer;
}
@@ -44,16 +44,16 @@ pre.output div:first-of-type {
pre .minion-id.host-success,
pre #summary-jobs-active .host-success {
- color: lime;
+ color: var(--color-status-success);
}
pre .minion-id.host-failure,
pre #summary-jobs-active .host-failure {
- color: red;
+ color: var(--color-status-failure);
}
td.address > span {
- color: #3f51b5;
+ color: var(--color-text-link);
cursor: copy;
position: relative;
}
@@ -63,6 +63,11 @@ td.tasks span {
padding-bottom: 3px;
}
+td.tasks span.task,
+td.tasks span.tasksummary {
+ background-color: var(--color-background-code);
+}
+
td.tasks span.task:first-child,
td.tasks span.tasksummary {
padding-left: 2px;
@@ -77,11 +82,11 @@ pre .minion-id.host-skips,
pre #summary-jobs-active .host-skips,
pre .minion-id.host-no-response,
pre #summary-jobs-active .host-no-response {
- color: yellow;
+ color: var(--color-status-warning);
}
pre .minion-id {
- color: #4caf50;
+ color: var(--color-text-accent);
}
.task-summary {
@@ -90,7 +95,7 @@ pre .minion-id {
}
pre span.active {
- color: greenyellow;
+ color: var(--color-status-success-soft);
font-weight: bold;
}
@@ -101,7 +106,7 @@ table {
}
table tr th {
- border-bottom: 3px double #4caf50;
+ border-bottom: 3px double var(--color-border-accent);
padding-right: 20px;
padding-top: 5px;
padding-bottom: 5px;
@@ -119,23 +124,23 @@ table tr td {
}
.no-job-status {
- color: gray;
+ color: var(--color-text-muted);
}
.no-job-details {
- color: gray;
+ color: var(--color-text-muted);
}
table thead th,
table tbody td {
padding: 8px;
text-align: left;
- border-bottom: 1px solid #ddd;
- color: #505050;
+ border-bottom: 1px solid var(--color-border-default);
+ color: var(--color-text-primary);
}
table thead th {
- border-bottom: 2px solid #ddd;
+ border-bottom: 2px solid var(--color-border-default);
}
table tr th:last-child {
@@ -158,17 +163,24 @@ table tr td:last-of-type {
opacity: 0.4;
}
+.button-in-text {
+ background-color: var(--color-background-control-muted);
+ border-radius: 2px;
+ color: var(--color-text-strong);
+ padding: 0 0.25em;
+}
+
.menu-item-hidden {
display: none;
}
.run-command-button {
- color: #263238;
+ color: var(--color-text-strong);
cursor: pointer;
}
.run-command-button:hover {
- color: #2e7d32;
+ color: var(--color-text-accent);
}
#template-catmenu-here,
@@ -181,25 +193,45 @@ table tr td:last-of-type {
position: relative;
}
+.job-details-success,
+.text-success {
+ color: var(--color-status-accepted);
+}
+
+.job-details-warning,
+.text-warning {
+ color: var(--color-status-caution);
+}
+
+.job-details-failure,
+.text-error {
+ color: var(--color-status-failure);
+}
+
+.job-details-info,
+.text-info {
+ color: var(--color-status-info);
+}
+
.accepted {
- color: #00a000;
+ color: var(--color-status-accepted);
}
.denied {
- color: #f0f;
+ color: var(--color-status-denied);
}
.unaccepted,
.keyunknown {
- color: #f00;
+ color: var(--color-status-unaccepted);
}
.rejected {
- color: #00f;
+ color: var(--color-status-rejected);
}
.offline {
- color: red;
+ color: var(--color-status-offline);
}
.prefiximage {
@@ -217,14 +249,14 @@ table tr td:last-of-type {
.jobs td .target {
font-weight: 500;
font-size: 18px;
- color: #505050;
+ color: var(--color-text-primary);
white-space: nowrap;
}
.jobs td .function {
font-weight: 500;
font-size: 14px;
- color: #3a3a3a;
+ color: var(--color-text-secondary);
}
.jobs td .time {
@@ -250,7 +282,7 @@ table tr td:last-of-type {
}
.highlight-rows tbody tr:hover {
- background-color: whitesmoke;
+ background-color: var(--color-background-row-hover);
cursor: pointer;
}
@@ -279,21 +311,21 @@ table tr td:last-of-type {
/* tasks */
.task-success {
- color: lime;
+ color: var(--color-status-success);
}
.task-success-changes {
- color: aqua;
+ color: var(--color-status-info);
}
.task-failure,
.task-failure-changes {
- color: red;
+ color: var(--color-status-failure);
}
.task-skipped,
.task-skipped-changes {
- color: yellow;
+ color: var(--color-status-warning);
}
pre .task-success,
@@ -305,6 +337,17 @@ pre .task-failure-changes {
cursor: pointer;
}
+.doc-inline-code {
+ background-color: var(--color-background-control-muted);
+ border-radius: 2px;
+ color: var(--color-text-primary);
+ padding: 0 0.25em;
+}
+
+.doc-inline-highlight {
+ color: var(--color-text-code-link);
+}
+
@media print {
.no-print,
.no-print * {
diff --git a/saltgui/static/stylesheets/schedules.css b/saltgui/static/stylesheets/schedules.css
index cfcbfdebb..56287d85e 100644
--- a/saltgui/static/stylesheets/schedules.css
+++ b/saltgui/static/stylesheets/schedules.css
@@ -7,5 +7,5 @@ td.schedule-value {
}
td.schedule-disabled {
- color: gray;
+ color: var(--color-text-muted);
}
diff --git a/saltgui/static/stylesheets/theme.css b/saltgui/static/stylesheets/theme.css
new file mode 100644
index 000000000..a9e4855e5
--- /dev/null
+++ b/saltgui/static/stylesheets/theme.css
@@ -0,0 +1,113 @@
+:root {
+ color-scheme: light;
+
+ --color-background-page: #263238;
+ --color-background-header: #fff;
+ --color-background-panel: #fff;
+ --color-background-popup: #fff;
+ --color-background-overlay: rgba(0, 0, 0, 86%);
+ --color-background-code: #272727;
+ --color-background-control: #fff;
+ --color-background-control-muted: #eee;
+ --color-background-control-soft: #f9f9f9;
+ --color-background-hover: rgba(0, 0, 0, 15%);
+ --color-background-row-hover: whitesmoke;
+ --color-background-tooltip: rgba(76, 175, 80, 80%);
+ --color-background-tooltip-error: rgba(244, 67, 54, 92%);
+ --color-background-tooltip-hover: #e0e0e0;
+ --color-background-tooltip-hover-code: #484848;
+ --color-background-warning: #ffeb3b;
+ --color-background-highlight: rgba(128, 128, 128, 60%);
+ --color-text-default: #000;
+ --color-text-strong: #263238;
+ --color-text-primary: #505050;
+ --color-text-secondary: #3a3a3a;
+ --color-text-muted: gray;
+ --color-text-quiet: #666;
+ --color-text-accent: #4caf50;
+ --color-text-link: #3f51b5;
+ --color-text-inverse: #fff;
+ --color-text-code-link: #ffeb3b;
+ --color-text-tooltip: #fff;
+ --color-text-tooltip-error: #fff;
+ --color-text-warning: #263238;
+ --color-border-accent: #4caf50;
+ --color-border-default: #ddd;
+ --color-border-control: #e2e2e2;
+ --color-shadow-panel: 0 0 24px rgba(0, 0, 0, 70%);
+ --color-shadow-dropdown: 0 3px 10px -2px rgba(0, 0, 0, 30%);
+ --color-shadow-dropdown-large: 0 8px 16px 0 rgba(0, 0, 0, 20%);
+ --color-shadow-button: 0 0 5px rgba(33, 33, 33, 50%);
+ --color-shadow-header: none;
+ --color-menu-dimmed: lightgray;
+ --color-status-accepted: #00a000;
+ --color-status-denied: #f0f;
+ --color-status-unaccepted: #f00;
+ --color-status-rejected: #00f;
+ --color-status-offline: #f00;
+ --color-status-success: lime;
+ --color-status-success-soft: greenyellow;
+ --color-status-warning: yellow;
+ --color-status-caution: orange;
+ --color-status-failure: red;
+ --color-status-info: aqua;
+ --color-notice-muted: gray;
+ --color-notice-danger: #f44336;
+}
+
+:root[data-theme="dark"] {
+ color-scheme: dark;
+
+ --color-background-page: #071318;
+ --color-background-header: #0d1f26;
+ --color-background-panel: #122a33;
+ --color-background-popup: #122a33;
+ --color-background-overlay: rgba(0, 0, 0, 86%);
+ --color-background-code: #0b171d;
+ --color-background-control: #173540;
+ --color-background-control-muted: #173540;
+ --color-background-control-soft: #173540;
+ --color-background-hover: rgba(34, 199, 189, 12%);
+ --color-background-row-hover: rgba(34, 199, 189, 8%);
+ --color-background-tooltip: rgba(34, 199, 189, 82%);
+ --color-background-tooltip-error: rgba(255, 127, 138, 90%);
+ --color-background-tooltip-hover: rgba(34, 199, 189, 12%);
+ --color-background-tooltip-hover-code: rgba(34, 199, 189, 12%);
+ --color-background-warning: rgba(244, 193, 79, 18%);
+ --color-background-highlight: rgba(11, 23, 29, 90%);
+ --color-text-default: #e6f7f5;
+ --color-text-strong: #e6f7f5;
+ --color-text-primary: #c4d7d5;
+ --color-text-secondary: #9ab8b5;
+ --color-text-muted: #9ab8b5;
+ --color-text-quiet: #9ab8b5;
+ --color-text-accent: #22c7bd;
+ --color-text-link: #8fc4ff;
+ --color-text-inverse: #e6f7f5;
+ --color-text-code-link: #ffe38f;
+ --color-text-tooltip: #062126;
+ --color-text-tooltip-error: #24050a;
+ --color-text-warning: #ffe3a2;
+ --color-border-accent: rgba(34, 199, 189, 12%);
+ --color-border-default: rgba(154, 184, 181, 18%);
+ --color-border-control: rgba(154, 184, 181, 18%);
+ --color-shadow-panel: 0 18px 40px rgba(0, 0, 0, 38%);
+ --color-shadow-dropdown: 0 18px 40px rgba(0, 0, 0, 38%);
+ --color-shadow-dropdown-large: 0 18px 40px rgba(0, 0, 0, 38%);
+ --color-shadow-button: 0 10px 30px rgba(34, 199, 189, 24%);
+ --color-shadow-header: 0 10px 30px rgba(0, 0, 0, 18%);
+ --color-menu-dimmed: #85a3a0;
+ --color-status-accepted: #60f29d;
+ --color-status-denied: #ff8df7;
+ --color-status-unaccepted: #ff8e8e;
+ --color-status-rejected: #7aa7ff;
+ --color-status-offline: #ff8e8e;
+ --color-status-success: #78ffaf;
+ --color-status-success-soft: #bfff72;
+ --color-status-warning: #ffe38f;
+ --color-status-caution: #ffb26b;
+ --color-status-failure: #ff8e8e;
+ --color-status-info: #66e7ff;
+ --color-notice-muted: #5a7980;
+ --color-notice-danger: #b93a48;
+}
diff --git a/saltgui/static/stylesheets/tooltip.css b/saltgui/static/stylesheets/tooltip.css
index faf2944b1..0da1b1ab0 100644
--- a/saltgui/static/stylesheets/tooltip.css
+++ b/saltgui/static/stylesheets/tooltip.css
@@ -5,8 +5,8 @@
.tooltip > .tooltip-text {
display: none;
font-size: 14px;
- background-color: rgba(76, 175, 80, 80%); /* #4caf50 */
- color: white;
+ background-color: var(--color-background-tooltip);
+ color: var(--color-text-tooltip);
padding: 7px;
border-radius: 3px;
position: absolute;
@@ -36,7 +36,8 @@
.tooltip > .tooltip-text-error-bottom-left {
text-align: left;
transform: translate(-5%, 0);
- background-color: red;
+ background-color: var(--color-background-tooltip-error);
+ color: var(--color-text-tooltip-error);
font-weight: bold;
}
@@ -56,8 +57,7 @@
}
.tooltip:hover {
- /* only slightly darker than 'whitesmoke(#f5f5f5)' */
- background-color: #e0e0e0;
+ background-color: var(--color-background-tooltip-hover);
}
.tooltip:hover > .tooltip-text {
@@ -65,12 +65,11 @@
}
pre.output .tooltip > .tooltip-text {
- background-color: rgba(76, 175, 80, 80%); /* #4caf50 */
+ background-color: var(--color-background-tooltip);
}
pre.output .tooltip:hover {
- /* only slightly lighter than '#272727' */
- background-color: #484848;
+ background-color: var(--color-background-tooltip-hover-code);
}
/* The arrow/triangle of the tooltip */
@@ -83,7 +82,7 @@ pre.output .tooltip:hover {
border-width: 5px;
border-style: solid;
top: 100%;
- border-color: rgba(76, 175, 80, 80%) transparent transparent transparent;
+ border-color: var(--color-background-tooltip) transparent transparent transparent;
}
.tooltip > .tooltip-text-logo {
@@ -108,7 +107,7 @@ pre.output .tooltip:hover {
.tooltip > .tooltip-text-error-bottom-left::after {
left: calc(5% - 2.5px);
- border-color: red transparent transparent;
+ border-color: var(--color-background-tooltip-error) transparent transparent;
}
.tooltip > .tooltip-text-bottom-center::after {
@@ -120,5 +119,5 @@ pre.output .tooltip:hover {
}
pre.output .tooltip > .tooltip-text::after {
- border-color: rgba(76, 175, 80, 80%) transparent transparent transparent; /* #4caf50 */
+ border-color: var(--color-background-tooltip) transparent transparent transparent;
}
diff --git a/tests/unit/Output.test.js b/tests/unit/Output.test.js
index 9112d5598..41bc23ad0 100644
--- a/tests/unit/Output.test.js
+++ b/tests/unit/Output.test.js
@@ -433,7 +433,7 @@ describe("Unittests for Output.js", () => {
OutputDocumentation.addDocumentationOutput(container, output);
assert.isTrue(
container.innerHTML.includes(
- "systemd-run(1) "));
+ "systemd-run(1) "));
done();
});