mirror of
https://github.com/webmin/webmin.git
synced 2026-10-29 22:50:58 +00:00
2823 lines
96 KiB
CSS
2823 lines
96 KiB
CSS
/*
|
|
* gray-theme.css
|
|
* Stylesheet of the framed theme. The frameset, the left menu, module
|
|
* pages and the system information page share this one file.
|
|
*
|
|
* Colors are custom properties. The light palette is set on :root and
|
|
* the dark one when a scheme is dark: forced by the theme settings on
|
|
* body[data-scheme="dark"], kept by the theme script on the html element,
|
|
* or preferred by the browser when neither marks the scheme light. The
|
|
* property names are the ones the ui-lib widgets read, so cards, badges
|
|
* and progress bars follow the same palette without a switch of their own.
|
|
*/
|
|
|
|
/* ---- Palette ---- */
|
|
|
|
:root {
|
|
color-scheme: light;
|
|
--font-family: system-ui, -apple-system, "Segoe UI", "Helvetica Neue",
|
|
Helvetica, Arial, sans-serif;
|
|
--font-family-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo,
|
|
Consolas, "Liberation Mono", monospace;
|
|
--font-size: 13px;
|
|
--radius: 8px;
|
|
--radius-sm: 6px;
|
|
--control-height: 32px;
|
|
/* Room between cards, buttons and blocks, and the same room around a
|
|
* page at the top and the sides; the bottom keeps scroll room under
|
|
* the last element */
|
|
--card-gap: 10px;
|
|
--page-pad-x: var(--card-gap);
|
|
--page-pad-top: var(--card-gap);
|
|
--page-pad-bottom: 28px;
|
|
/* Titles on the ground sit a little in from the card edge, so they
|
|
* neither touch the edge nor go as deep as the text inside the
|
|
* cards */
|
|
--title-inset: 4px;
|
|
--shadow: 0 1px 2px rgba(16, 24, 40, 0.04);
|
|
--shadow-color: rgba(16, 24, 40, 0.10);
|
|
/* The edge of the floating menu, a soft ring on the ground */
|
|
--border-color-panel: rgba(16, 24, 40, 0.17);
|
|
/* Which of two images, one per palette, is shown */
|
|
--show-light: block;
|
|
--show-dark: none;
|
|
|
|
/* Neutrals: a light gray ground, white surfaces and gray hairlines,
|
|
* with near-black text */
|
|
--bg-color: #dfdfdf;
|
|
--bg-color-frame: #ffffff;
|
|
--bg-color-hover: #f0f0f0;
|
|
--bg-color-switch: #e2e2e2;
|
|
--bg-color-switch-on: #ffffff;
|
|
--main-content: #ffffff;
|
|
--bg-table-header: #f5f5f5;
|
|
--bg-color-input: #ffffff;
|
|
--bg-color-disabled: #dfdfdf;
|
|
|
|
/* Card edges and lines inside cards; the darker one is for lines and
|
|
* controls drawn on the ground itself, which the gray ground would
|
|
* otherwise swallow */
|
|
--border-color: #cfcfcf;
|
|
--border-color-light: #e2e2e2;
|
|
--border-color-lighter: #e8e8e8;
|
|
--border-color-frame: #e2e2e2;
|
|
--border-color-darker: #b8b8b8;
|
|
--border-color-input: #bcbcbc;
|
|
--border-color-focused: #dbe5f5;
|
|
|
|
/* Text is black; secondary text keeps at least 12:1 contrast on the
|
|
* ground and the surfaces, and muted text at least 9:1 */
|
|
--text-color: #0b0c0e;
|
|
--text-color-soft: #1f2226;
|
|
--text-color-desc: #2b2f35;
|
|
--text-color-muted: #3d434b;
|
|
--text-color-disabled: #767d86;
|
|
--text-color-inverse: #ffffff;
|
|
|
|
/* Links are a deep blue, 9.5:1 on the ground, still apart from the
|
|
* black text */
|
|
--link-color: #163b82;
|
|
--link-color-hover: #0f2b63;
|
|
--accent-color: #2f66c4;
|
|
--accent-color-hover: #234d93;
|
|
--accent-color-soft: #a9c0e8;
|
|
--element-highlight-bg-color: #e3ebf8;
|
|
/* Text on a button filled with a notice color: white on the deep
|
|
* light-mode colors, the ground color on the bright dark-mode ones.
|
|
* The dark palette keeps its own amber for the warning button. */
|
|
--alert-button-text: var(--text-color-inverse);
|
|
/* The warning button is a warm orange with dark text: the deep amber
|
|
* of the warning text looked muddy on a button, and white text on
|
|
* orange would be too faint */
|
|
--alert-button-warning: #e8933c;
|
|
--alert-button-warning-text: var(--text-color);
|
|
|
|
/* Notice borders are darker than the ground, so a notice keeps a
|
|
* visible edge on it */
|
|
--text-color-success: #1f6f4a;
|
|
--text-color-success-stressed: #1f5c36;
|
|
--bg-color-success: #e8f5ec;
|
|
--border-color-success: #8fcaa3;
|
|
--text-color-info: #2f66c4;
|
|
--text-color-info-stressed: #1e3a5c;
|
|
--bg-color-info: #e9f1fa;
|
|
--border-color-info: #9dbde6;
|
|
--text-color-warning: #a86b12;
|
|
--text-color-warning-stressed: #4f3f0c;
|
|
--bg-color-warning: #fff6dd;
|
|
--border-color-warning: #e6c86f;
|
|
--text-color-danger: #b42323;
|
|
--text-color-danger-stressed: #7a1f1f;
|
|
--bg-color-danger: #fdecec;
|
|
--border-color-danger: #d98f8f;
|
|
--text-color-neutral-stressed: #4c535d;
|
|
--bg-color-neutral: #ececec;
|
|
--border-color-neutral: #d4d4d4;
|
|
|
|
--toggle-bg-color: #d0d5dc;
|
|
--toggle-thumb-color: #ffffff;
|
|
--select-arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'><path d='M2 3.5l3 3 3-3' fill='none' stroke='%236b7684' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
|
|
}
|
|
|
|
/* The dark palette. The two blocks below must be kept identical: the
|
|
* first applies when the browser prefers dark and nothing marks the
|
|
* scheme light, the second when the theme settings or the theme script
|
|
* mark it dark. */
|
|
@media (prefers-color-scheme: dark) {
|
|
html:not([data-scheme="light"]) body:not([data-scheme="light"]) {
|
|
color-scheme: dark;
|
|
--shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
--shadow-color: rgba(0, 0, 0, 0.4);
|
|
--border-color-panel: #30353d;
|
|
--show-light: none;
|
|
--show-dark: block;
|
|
--bg-color: #15171b;
|
|
--bg-color-frame: #1a1d22;
|
|
--bg-color-hover: #262a30;
|
|
--bg-color-switch: #262a30;
|
|
--bg-color-switch-on: #363b43;
|
|
--main-content: #1e2126;
|
|
--bg-table-header: #23262c;
|
|
--bg-color-input: #1b1e23;
|
|
--bg-color-disabled: #23262c;
|
|
--border-color: #2e333a;
|
|
--border-color-light: #2a2f36;
|
|
--border-color-lighter: #262a30;
|
|
--border-color-frame: #2a2f36;
|
|
--border-color-darker: #4d545e;
|
|
--border-color-input: #4d545e;
|
|
--border-color-focused: rgba(106, 168, 240, 0.3);
|
|
--text-color: #e6e8ea;
|
|
--text-color-soft: #c8ced5;
|
|
--text-color-desc: #a7b0ba;
|
|
--text-color-muted: #99a3ae;
|
|
--text-color-disabled: #6b7581;
|
|
--text-color-inverse: #ffffff;
|
|
--link-color: #6aa8f0;
|
|
--link-color-hover: #8fc0f3;
|
|
--accent-color: #3d6fc2;
|
|
--accent-color-hover: #5a8ad8;
|
|
--accent-color-soft: #3d5f8a;
|
|
--element-highlight-bg-color: #26395a;
|
|
--alert-button-text: var(--bg-color);
|
|
--alert-button-warning: #e0a63c;
|
|
--alert-button-warning-text: var(--bg-color);
|
|
--text-color-success: #7ec563;
|
|
--text-color-success-stressed: #8fd376;
|
|
--bg-color-success: rgba(126, 197, 99, 0.13);
|
|
--border-color-success: rgba(126, 197, 99, 0.5);
|
|
--text-color-info: #6aa8f0;
|
|
--text-color-info-stressed: #8fc0f3;
|
|
--bg-color-info: rgba(106, 168, 240, 0.13);
|
|
--border-color-info: rgba(106, 168, 240, 0.5);
|
|
--text-color-warning: #e0a63c;
|
|
--text-color-warning-stressed: #eab54f;
|
|
--bg-color-warning: rgba(224, 166, 60, 0.13);
|
|
--border-color-warning: rgba(224, 166, 60, 0.5);
|
|
--text-color-danger: #ef7b6f;
|
|
--text-color-danger-stressed: #f28c82;
|
|
--bg-color-danger: rgba(239, 123, 111, 0.13);
|
|
--border-color-danger: rgba(239, 123, 111, 0.5);
|
|
--text-color-neutral-stressed: #b6bec7;
|
|
--bg-color-neutral: #272b31;
|
|
--border-color-neutral: #3c424b;
|
|
--toggle-bg-color: #4d545e;
|
|
--toggle-thumb-color: #e6e8ea;
|
|
--select-arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'><path d='M2 3.5l3 3 3-3' fill='none' stroke='%2399a3ae' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
|
|
}
|
|
html:not([data-scheme="light"]) body:not([data-scheme="light"])
|
|
:is(.ui_page, .ui_multi, .ui_toggle, .ui_dropdown, .ui_dialog) {
|
|
color-scheme: dark;
|
|
}
|
|
/* The root follows its body, so the scrollbars and the canvas behind
|
|
* the page are dark as well */
|
|
html:not([data-scheme="light"]):has(> body:not([data-scheme="light"])) {
|
|
color-scheme: dark;
|
|
}
|
|
/* The frameset page, in the dark ground color */
|
|
html:not([data-scheme="light"]):has(> frameset) {
|
|
color-scheme: dark;
|
|
background: #15171b;
|
|
}
|
|
}
|
|
html[data-scheme="dark"] body, body[data-scheme="dark"] {
|
|
color-scheme: dark;
|
|
--shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
--shadow-color: rgba(0, 0, 0, 0.4);
|
|
--border-color-panel: #30353d;
|
|
--show-light: none;
|
|
--show-dark: block;
|
|
--bg-color: #15171b;
|
|
--bg-color-frame: #1a1d22;
|
|
--bg-color-hover: #262a30;
|
|
--bg-color-switch: #262a30;
|
|
--bg-color-switch-on: #363b43;
|
|
--main-content: #1e2126;
|
|
--bg-table-header: #23262c;
|
|
--bg-color-input: #1b1e23;
|
|
--bg-color-disabled: #23262c;
|
|
--border-color: #2e333a;
|
|
--border-color-light: #2a2f36;
|
|
--border-color-lighter: #262a30;
|
|
--border-color-frame: #2a2f36;
|
|
--border-color-darker: #4d545e;
|
|
--border-color-input: #4d545e;
|
|
--border-color-focused: rgba(106, 168, 240, 0.3);
|
|
--text-color: #e6e8ea;
|
|
--text-color-soft: #c8ced5;
|
|
--text-color-desc: #a7b0ba;
|
|
--text-color-muted: #99a3ae;
|
|
--text-color-disabled: #6b7581;
|
|
--text-color-inverse: #ffffff;
|
|
--link-color: #6aa8f0;
|
|
--link-color-hover: #8fc0f3;
|
|
--accent-color: #3d6fc2;
|
|
--accent-color-hover: #5a8ad8;
|
|
--accent-color-soft: #3d5f8a;
|
|
--element-highlight-bg-color: #26395a;
|
|
--alert-button-text: var(--bg-color);
|
|
--alert-button-warning: #e0a63c;
|
|
--alert-button-warning-text: var(--bg-color);
|
|
--text-color-success: #7ec563;
|
|
--text-color-success-stressed: #8fd376;
|
|
--bg-color-success: rgba(126, 197, 99, 0.13);
|
|
--border-color-success: rgba(126, 197, 99, 0.5);
|
|
--text-color-info: #6aa8f0;
|
|
--text-color-info-stressed: #8fc0f3;
|
|
--bg-color-info: rgba(106, 168, 240, 0.13);
|
|
--border-color-info: rgba(106, 168, 240, 0.5);
|
|
--text-color-warning: #e0a63c;
|
|
--text-color-warning-stressed: #eab54f;
|
|
--bg-color-warning: rgba(224, 166, 60, 0.13);
|
|
--border-color-warning: rgba(224, 166, 60, 0.5);
|
|
--text-color-danger: #ef7b6f;
|
|
--text-color-danger-stressed: #f28c82;
|
|
--bg-color-danger: rgba(239, 123, 111, 0.13);
|
|
--border-color-danger: rgba(239, 123, 111, 0.5);
|
|
--text-color-neutral-stressed: #b6bec7;
|
|
--bg-color-neutral: #272b31;
|
|
--border-color-neutral: #3c424b;
|
|
--toggle-bg-color: #4d545e;
|
|
--toggle-thumb-color: #e6e8ea;
|
|
--select-arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'><path d='M2 3.5l3 3 3-3' fill='none' stroke='%2399a3ae' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
|
|
}
|
|
html[data-scheme="dark"] body :is(.ui_page, .ui_multi, .ui_toggle, .ui_dropdown, .ui_dialog),
|
|
body[data-scheme="dark"] :is(.ui_page, .ui_multi, .ui_toggle, .ui_dropdown, .ui_dialog) {
|
|
color-scheme: dark;
|
|
}
|
|
html[data-scheme="dark"], html:has(> body[data-scheme="dark"]) {
|
|
color-scheme: dark;
|
|
}
|
|
html[data-scheme="dark"]:has(> frameset) {
|
|
color-scheme: dark;
|
|
background: #15171b;
|
|
}
|
|
/* A forced light scheme holds the frames to light as well, so a frame
|
|
* that is still loading never paints dark under a dark system setting */
|
|
html[data-scheme="light"]:has(> frameset) {
|
|
color-scheme: light;
|
|
}
|
|
/* The frameset page has no body to carry the palette, so it paints the
|
|
* ground itself, showing behind a frame that is still loading. It
|
|
* supports both schemes, so its frames are not held to light. */
|
|
:root:has(> frameset) {
|
|
color-scheme: light dark;
|
|
background: var(--bg-color);
|
|
}
|
|
|
|
/* ---- Base ---- */
|
|
|
|
/* The root stays unpainted: the body's ground reaches the canvas, and
|
|
* the dark tokens live on the body */
|
|
html {
|
|
background: transparent;
|
|
}
|
|
body {
|
|
margin: 0;
|
|
padding: var(--page-pad-top) var(--page-pad-x) var(--page-pad-bottom);
|
|
/* The core header sets height:100% inline on html and body, which
|
|
* would end the body at the viewport and put the bottom padding
|
|
* before the end of a long page */
|
|
height: auto !important;
|
|
font-family: var(--font-family);
|
|
font-size: var(--font-size);
|
|
line-height: 1.45;
|
|
color: var(--text-color);
|
|
background: var(--bg-color);
|
|
text-align: start;
|
|
}
|
|
/* Kept at type-selector specificity, so that a class on a link can
|
|
* recolor it and the body's link attributes are ignored */
|
|
a {
|
|
color: var(--link-color);
|
|
text-decoration: none;
|
|
}
|
|
a:hover {
|
|
color: var(--link-color-hover);
|
|
text-decoration: underline;
|
|
}
|
|
/* Element defaults */
|
|
img, a img { border: 0; }
|
|
p { margin: 6px 0; }
|
|
h1, h2, h3, h4, h5 {
|
|
margin: 10px 0 6px;
|
|
color: var(--text-color);
|
|
font-weight: 600;
|
|
line-height: 1.3;
|
|
}
|
|
h1 { font-size: 18px; }
|
|
h2 { font-size: 15px; }
|
|
h3, h4, h5 { font-size: 13px; }
|
|
b, strong { font-weight: 600; }
|
|
hr {
|
|
height: 0;
|
|
margin: 12px 0;
|
|
border: 0;
|
|
border-top: 1px solid var(--border-color-darker);
|
|
}
|
|
code, kbd, samp, tt, pre {
|
|
font-family: var(--font-family-mono);
|
|
font-size: 12px;
|
|
}
|
|
pre { margin: 6px 0; }
|
|
table {
|
|
border-collapse: separate;
|
|
border-spacing: 0;
|
|
font-size: inherit;
|
|
}
|
|
th, td {
|
|
border: 0;
|
|
text-align: start;
|
|
}
|
|
font { font-size: inherit; }
|
|
label { cursor: pointer; }
|
|
/* Utility classes printed by ui-lib and by modules */
|
|
.muted { color: var(--text-color-muted); }
|
|
.text-danger { color: var(--text-color-danger); }
|
|
.text-success { color: var(--text-color-success); }
|
|
.text-warning { color: var(--text-color-warning); }
|
|
.text-info { color: var(--text-color-info); }
|
|
.w-100 { width: 100% !important; }
|
|
.field-sizing-content {
|
|
field-sizing: content !important;
|
|
min-width: 15px !important;
|
|
}
|
|
.inherit-color,
|
|
.inherit-color:hover, .inherit-color:active, .inherit-color:focus,
|
|
.inherit-color::before, .inherit-color::after,
|
|
.inherit-color:hover::before, .inherit-color:hover::after,
|
|
.inherit-color:active::before, .inherit-color:active::after,
|
|
.inherit-color:focus::before, .inherit-color:focus::after {
|
|
color: inherit !important;
|
|
}
|
|
/* The warning in the login heading over plain HTTP */
|
|
.not-secure {
|
|
float: right;
|
|
color: var(--text-color-danger);
|
|
}
|
|
/* Sections and tab bodies that the theme scripts show and hide */
|
|
.itemhidden, .opener_hidden { display: none; }
|
|
.itemshown, .opener_shown { display: block; }
|
|
|
|
/* Small chevron drawn with two borders, used for collapsible headings */
|
|
details > summary {
|
|
cursor: pointer;
|
|
}
|
|
details > summary::-webkit-details-marker { display: none; }
|
|
details.ui_hidden_table_start > summary::before,
|
|
details.section > summary::after,
|
|
details.menu-cat > summary::after {
|
|
content: "";
|
|
flex: 0 0 auto;
|
|
width: 7px;
|
|
height: 7px;
|
|
border-right: 1.5px solid var(--text-color-muted);
|
|
border-bottom: 1.5px solid var(--text-color-muted);
|
|
transform: rotate(-45deg);
|
|
transition: transform 0.12s;
|
|
}
|
|
details.ui_hidden_table_start > summary::before {
|
|
margin-right: 4px;
|
|
}
|
|
details.section > summary::after {
|
|
margin: 0 4px 2px 0;
|
|
}
|
|
/* The menu chevron is smaller than the one on cards */
|
|
details.menu-cat > summary::after {
|
|
width: 5px;
|
|
height: 5px;
|
|
margin: 0 5px 1px 0;
|
|
}
|
|
details[open].ui_hidden_table_start > summary::before,
|
|
details[open].section > summary::after,
|
|
details[open].menu-cat > summary::after {
|
|
transform: rotate(45deg);
|
|
}
|
|
|
|
/* ---- Form controls ---- */
|
|
|
|
/* Forms and their controls shrink inside the content column. */
|
|
form.ui_form {
|
|
min-width: 0;
|
|
max-width: 100%;
|
|
}
|
|
|
|
/* Text fields, menus and text areas share one height, edge and radius */
|
|
input[type="text"], input[type="password"], input[type="email"],
|
|
input[type="number"], input[type="search"], input[type="url"],
|
|
input[type="tel"], input[type="date"], input[type="time"],
|
|
input[type="datetime-local"], input:not([type]), select, textarea {
|
|
box-sizing: border-box;
|
|
max-width: 100%;
|
|
min-height: var(--control-height);
|
|
padding: 4px 10px;
|
|
border: 1px solid var(--border-color-input);
|
|
border-radius: var(--radius-sm);
|
|
background: var(--bg-color-input);
|
|
color: var(--text-color);
|
|
font: inherit;
|
|
line-height: 1.45;
|
|
}
|
|
select { padding: 4px 8px; }
|
|
/* Drop-down selects draw their own arrow, inset from the edge */
|
|
select:not([multiple]):not([size]), select[size="1"]:not([multiple]) {
|
|
appearance: none;
|
|
-webkit-appearance: none;
|
|
padding-right: 28px;
|
|
background-image: var(--select-arrow);
|
|
background-repeat: no-repeat;
|
|
background-position: right 9px center;
|
|
background-size: 10px 10px;
|
|
}
|
|
/* Treat byte values and their unit menu as one compact control. */
|
|
.ui_bytesbox {
|
|
display: inline-flex;
|
|
align-items: stretch;
|
|
max-width: 100%;
|
|
vertical-align: middle;
|
|
}
|
|
.ui_bytesbox > input {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
border-start-end-radius: 0;
|
|
border-end-end-radius: 0;
|
|
}
|
|
.ui_bytesbox > select {
|
|
flex: 0 0 auto;
|
|
margin-inline-start: -1px;
|
|
border-start-start-radius: 0;
|
|
border-end-start-radius: 0;
|
|
}
|
|
.ui_bytesbox > :focus-visible {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
/* List boxes keep their own height */
|
|
select[multiple], select[size]:not([size="1"]) {
|
|
min-height: 0;
|
|
padding: 4px;
|
|
}
|
|
textarea {
|
|
min-height: 0;
|
|
padding: 8px 10px;
|
|
resize: vertical;
|
|
}
|
|
/* Text areas in forms are monospace, for config files and scripts */
|
|
table.ui_table td textarea {
|
|
line-height: normal;
|
|
font-family: var(--font-family-mono);
|
|
}
|
|
/* Focus ring in the accent color, and grayed disabled controls */
|
|
input:focus-visible, select:focus-visible, textarea:focus-visible {
|
|
outline: 0;
|
|
border-color: var(--accent-color);
|
|
box-shadow: 0 0 0 3px var(--border-color-focused);
|
|
}
|
|
input:disabled, select:disabled, textarea:disabled {
|
|
background: var(--bg-color-disabled);
|
|
color: var(--text-color-disabled);
|
|
}
|
|
/* Checkboxes and radios in the accent color, with room after a label */
|
|
input[type="checkbox"], input[type="radio"] {
|
|
width: 15px;
|
|
height: 15px;
|
|
margin: 0 5px 0 0;
|
|
vertical-align: -3px;
|
|
accent-color: var(--accent-color);
|
|
}
|
|
input[type="checkbox"] + label, input[type="radio"] + label {
|
|
margin-right: 14px;
|
|
}
|
|
/* A checkbox or radio right after a button, a field or a menu keeps its
|
|
* distance */
|
|
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]) + input[type="checkbox"],
|
|
select + input[type="checkbox"],
|
|
textarea + input[type="checkbox"],
|
|
button + input[type="checkbox"],
|
|
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]) + input[type="radio"],
|
|
select + input[type="radio"],
|
|
textarea + input[type="radio"],
|
|
button + input[type="radio"] {
|
|
margin-left: 10px;
|
|
}
|
|
/* Keep controls on the line below a text area from touching it. */
|
|
textarea + br + input[type="checkbox"],
|
|
textarea + br + input[type="radio"] {
|
|
margin-top: 8px;
|
|
}
|
|
input[type="file"] { font: inherit; }
|
|
/* Labels of checkboxes inside bold cells stay plain */
|
|
.ui_form_value td > b > input + label + tt,
|
|
.ui_form_value td > b > input + label {
|
|
font-weight: normal !important;
|
|
}
|
|
|
|
/* ---- Buttons ---- */
|
|
|
|
/* Form buttons use Gray Theme's compact raised surface and small radius. */
|
|
input[type="submit"], input[type="button"], input[type="reset"], button,
|
|
input.ui_submit, input.ui_button, input.ui_reset {
|
|
display: inline-block;
|
|
box-sizing: border-box;
|
|
min-height: var(--control-height);
|
|
margin: 1px 0;
|
|
padding: 5px 14px;
|
|
border: 1px solid var(--border-color-darker);
|
|
border-radius: var(--radius-sm);
|
|
background: var(--main-content);
|
|
color: var(--text-color);
|
|
font: inherit;
|
|
font-weight: 500;
|
|
line-height: 1.45;
|
|
cursor: pointer;
|
|
}
|
|
input[type="submit"]:hover, input[type="button"]:hover,
|
|
input[type="reset"]:hover, button:hover,
|
|
input.ui_submit:hover, input.ui_button:hover, input.ui_reset:hover {
|
|
background: var(--bg-color);
|
|
border-color: var(--text-color-disabled);
|
|
text-decoration: none;
|
|
}
|
|
input[type="submit"]:active, input[type="button"]:active,
|
|
input[type="reset"]:active, button:active {
|
|
background: var(--bg-color-switch);
|
|
}
|
|
input[type="submit"]:focus-visible, input[type="button"]:focus-visible,
|
|
input[type="reset"]:focus-visible, button:focus-visible {
|
|
outline: 0;
|
|
border-color: var(--accent-color);
|
|
box-shadow: 0 0 0 3px var(--border-color-focused);
|
|
}
|
|
input[type="submit"]:disabled, input[type="button"]:disabled,
|
|
input[type="reset"]:disabled, button:disabled {
|
|
background: var(--bg-color-disabled);
|
|
border-color: var(--border-color);
|
|
color: var(--text-color-disabled);
|
|
cursor: default;
|
|
}
|
|
|
|
/* UI-lib actions can carry classes used by other themes. Reset the outer
|
|
* control so they cannot change Gray Theme's sizing; the inner element draws
|
|
* the visible surface. */
|
|
:is(button, a, summary).ui_action {
|
|
--ui-action-radius: var(--radius-sm);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-sizing: border-box;
|
|
width: auto;
|
|
height: auto;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
margin: 0;
|
|
padding: 0;
|
|
border: 0;
|
|
border-radius: var(--ui-action-radius);
|
|
background: none;
|
|
box-shadow: none;
|
|
color: var(--ui-action-text);
|
|
font: inherit;
|
|
line-height: inherit;
|
|
text-decoration: none;
|
|
vertical-align: middle;
|
|
white-space: nowrap;
|
|
cursor: pointer;
|
|
}
|
|
/* Gray Theme softens UI-lib's square default to match its form controls,
|
|
* while keeping the stronger rounded and pill shape options distinct. */
|
|
:is(button, a, summary).ui_action.ui_action_rounded {
|
|
--ui-action-radius: var(--radius);
|
|
}
|
|
:is(button, a, summary).ui_action.ui_action_pill {
|
|
--ui-action-radius: 999px;
|
|
}
|
|
/* Neutral actions sit beside fields, so use the same surface and edge as
|
|
* inputs and selects in both palettes. */
|
|
:is(button, a, summary).ui_action.ui_action_neutral {
|
|
--ui-action-text: var(--text-color);
|
|
--ui-action-bg: var(--bg-color-input);
|
|
--ui-action-border: var(--border-color-input);
|
|
--ui-action-hover-text: var(--text-color);
|
|
--ui-action-hover-bg: var(--bg-color-hover);
|
|
--ui-action-hover-border: var(--text-color-disabled);
|
|
--ui-action-active-text: var(--text-color);
|
|
--ui-action-active-bg: var(--bg-color-switch);
|
|
--ui-action-active-border: var(--border-color-darker);
|
|
}
|
|
/* The inner element draws the action; the outer element handles semantics,
|
|
* keyboard focus and shape. */
|
|
:is(button, a, summary).ui_action > .ui_action_content {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-sizing: border-box;
|
|
gap: 5px;
|
|
padding: 2px 10px;
|
|
border: 1px solid var(--ui-action-border);
|
|
border-radius: inherit;
|
|
background: var(--ui-action-bg);
|
|
color: inherit;
|
|
font: inherit;
|
|
font-size: 0.9em;
|
|
font-weight: 600;
|
|
line-height: 1.5;
|
|
}
|
|
/* Compact and large actions retain the shared component's size variants. */
|
|
:is(button, a, summary).ui_action.ui_action_small > .ui_action_content {
|
|
padding: 0 8px 0 7px;
|
|
font-size: 0.8em;
|
|
line-height: 1.25;
|
|
/* Let the border round separately at fractional browser zoom levels. */
|
|
box-sizing: content-box;
|
|
min-height: 1lh;
|
|
}
|
|
/* Center compact labels by their cap height without shrinking the button. */
|
|
:is(button, a, summary).ui_action.ui_action_small > .ui_action_content > span {
|
|
text-box: trim-both cap alphabetic;
|
|
}
|
|
:is(button, a, summary).ui_action.ui_action_large > .ui_action_content {
|
|
min-height: var(--control-height);
|
|
padding: 4px 12px;
|
|
font-size: 1em;
|
|
}
|
|
/* Interaction colors belong to the visible inner surface. */
|
|
:is(button, a, summary).ui_action:not(:disabled):not([aria-disabled="true"]):hover {
|
|
border: 0;
|
|
background: none;
|
|
color: var(--ui-action-hover-text);
|
|
text-decoration: none;
|
|
}
|
|
:is(button, a, summary).ui_action:not(:disabled):not([aria-disabled="true"]):hover > .ui_action_content {
|
|
background: var(--ui-action-hover-bg);
|
|
border-color: var(--ui-action-hover-border);
|
|
}
|
|
:is(button, a, summary).ui_action:not(:disabled):not([aria-disabled="true"]):active {
|
|
border: 0;
|
|
background: none;
|
|
color: var(--ui-action-active-text);
|
|
}
|
|
:is(button, a, summary).ui_action:not(:disabled):not([aria-disabled="true"]):active > .ui_action_content {
|
|
background: var(--ui-action-active-bg);
|
|
border-color: var(--ui-action-active-border);
|
|
box-shadow: inset 0 0 0 1px var(--ui-action-active-border);
|
|
}
|
|
/* Put the keyboard outline around the painted surface, not its wrapper. */
|
|
:is(button, a, summary).ui_action:focus-visible {
|
|
outline: 0;
|
|
box-shadow: none;
|
|
}
|
|
:is(button, a, summary).ui_action:focus-visible > .ui_action_content {
|
|
outline: 2px solid var(--ui-action-focus-ring);
|
|
outline-offset: 2px;
|
|
}
|
|
/* Disabled actions keep their semantic state and use disabled palette
|
|
* variables on the visible surface. */
|
|
:is(button, a, summary).ui_action:is(:disabled, [aria-disabled="true"]) {
|
|
border: 0;
|
|
background: none;
|
|
color: var(--ui-action-disabled-text);
|
|
opacity: 0.55;
|
|
cursor: not-allowed;
|
|
}
|
|
:is(button, a, summary).ui_action:is(:disabled, [aria-disabled="true"]) > .ui_action_content {
|
|
background: var(--ui-action-disabled-bg);
|
|
border-color: var(--ui-action-disabled-border);
|
|
}
|
|
|
|
/* Legacy forms can print action buttons directly instead of using a
|
|
* button-row wrapper. Keep consecutive controls from touching. */
|
|
form.ui_form > :is(input[type="submit"], input[type="button"],
|
|
input[type="reset"], button) +
|
|
:is(input[type="submit"], input[type="button"], input[type="reset"], button) {
|
|
margin-inline-start: 6px;
|
|
}
|
|
|
|
/* The first button of a form is the primary action, unless it deletes */
|
|
.ui_form_end_buttons td:first-child > input.ui_submit:not([name*="delete"]):not([name*="remove"]),
|
|
.ui_form_end_side_left .ui_form_end_buttons td:first-child > input.ui_submit,
|
|
input.ui_submit.primary, button.primary {
|
|
background: var(--accent-color);
|
|
border-color: var(--accent-color-hover);
|
|
color: var(--text-color-inverse);
|
|
font-weight: 600;
|
|
}
|
|
.ui_form_end_buttons td:first-child > input.ui_submit:not([name*="delete"]):not([name*="remove"]):hover,
|
|
.ui_form_end_side_left .ui_form_end_buttons td:first-child > input.ui_submit:hover,
|
|
input.ui_submit.primary:hover, button.primary:hover {
|
|
background: var(--accent-color-hover);
|
|
border-color: var(--accent-color-hover);
|
|
}
|
|
|
|
/* Destructive buttons are outlined in red */
|
|
input.ui_submit[name="delete"], input.ui_submit[name^="delete"],
|
|
input.ui_submit[name$="delete"], input.ui_submit[name="remove"],
|
|
input.ui_submit.danger, button.danger {
|
|
background: var(--main-content);
|
|
border-color: var(--border-color-danger);
|
|
color: var(--text-color-danger);
|
|
font-weight: 500;
|
|
}
|
|
input.ui_submit[name="delete"]:hover, input.ui_submit[name^="delete"]:hover,
|
|
input.ui_submit[name$="delete"]:hover, input.ui_submit[name="remove"]:hover,
|
|
input.ui_submit.danger:hover, button.danger:hover {
|
|
background: var(--bg-color-danger);
|
|
border-color: var(--text-color-danger);
|
|
}
|
|
|
|
/* Buttons under a form sit at the left, with the same 11px gap between
|
|
* them as the footer bar keeps above and below them, whatever alignment
|
|
* the core gave their cells; the spacer cells between them are dropped */
|
|
table.ui_form_end_buttons { margin: 4px 0 var(--card-gap); }
|
|
table.ui_form_end_buttons > tbody > tr {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 11px;
|
|
}
|
|
table.ui_form_end_buttons td {
|
|
display: block;
|
|
padding: 0;
|
|
text-align: start;
|
|
}
|
|
table.ui_form_end_buttons td:not(:has(*)) { display: none; }
|
|
div.ui_form_end_buttons { padding: 6px 0; }
|
|
/* Buttons split between the left and the right of a form */
|
|
.ui_form_end_side_by_side {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
/* Rows of action buttons with a description (ui_buttons_start) */
|
|
table.ui_buttons_table {
|
|
width: 100%;
|
|
margin: 0 0 var(--card-gap);
|
|
background: var(--main-content);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--radius);
|
|
box-shadow: var(--shadow);
|
|
}
|
|
tr.ui_buttons_row > td {
|
|
padding: 10px 16px;
|
|
border-top: 1px solid var(--border-color-lighter);
|
|
vertical-align: middle;
|
|
}
|
|
/* Each row is a form with a table of its own, as a form cannot wrap table
|
|
* rows. Flattening the wrappers lets all rows share the outer columns */
|
|
tr.ui_buttons_form_row,
|
|
tr.ui_buttons_form_row > td,
|
|
form.ui_buttons_form,
|
|
form.ui_buttons_form > table,
|
|
form.ui_buttons_form > table > tbody { display: contents; }
|
|
tr.ui_buttons_form_row:first-child tr.ui_buttons_row > td { border-top: 0; }
|
|
td.ui_buttons_label {
|
|
width: 1%;
|
|
padding-right: 16px;
|
|
white-space: nowrap;
|
|
}
|
|
td.ui_buttons_label > input.ui_submit { margin-right: 10px; }
|
|
td.ui_buttons_label input.ui_button { margin-left: 6px; }
|
|
td.ui_buttons_label label { margin-right: 6px; }
|
|
td.ui_buttons_value {
|
|
padding-left: 16px;
|
|
color: var(--text-color-desc);
|
|
}
|
|
tr.ui_buttons_row.disabled > td { opacity: 0.6; }
|
|
|
|
/* Rows of links above or below tables (ui_links_row): each link is a
|
|
* small button, styled with the header buttons below, and the bar
|
|
* separators between them are dropped */
|
|
.ui_links_row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 6px 8px;
|
|
margin: 0 0 8px;
|
|
}
|
|
.ui_links_sep { display: none; }
|
|
td[align="right"] > .ui_links_row { justify-content: flex-end; }
|
|
/* Inside a table cell, such as the buttons of a login in the dashboard
|
|
* list, the row has no margin, so it centers with the cell's text */
|
|
table.ui_columns td > .ui_links_row { margin: 0; }
|
|
.ui_link_pagination.disabled {
|
|
filter: grayscale(1) opacity(0.6);
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* ---- Module page header ---- */
|
|
/* The header table printed by header() becomes the page title with the
|
|
* module links beside it, on the page ground. It bleeds over the page
|
|
* padding and stays at the top while the page scrolls. */
|
|
|
|
table.header {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 5;
|
|
display: block;
|
|
width: auto;
|
|
margin: calc(-1 * var(--page-pad-top)) calc(-1 * var(--page-pad-x)) 14px;
|
|
padding: 14px var(--page-pad-x) 12px;
|
|
background: var(--bg-color);
|
|
}
|
|
table.header > tbody { display: block; }
|
|
/* The cells sit 8px apart, like the buttons inside them; the title cell
|
|
* keeps a little more distance from the first button */
|
|
table.header > tbody > tr {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
table.header > tbody > tr > td {
|
|
width: auto;
|
|
padding: 0;
|
|
vertical-align: middle;
|
|
}
|
|
/* The status line above the title, when Webmin is set to show it */
|
|
#headln1 {
|
|
flex: 1 1 100%;
|
|
text-align: center;
|
|
font-size: 12px;
|
|
color: var(--text-color-muted);
|
|
}
|
|
#headln2c {
|
|
order: 1;
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
padding-left: var(--title-inset);
|
|
padding-right: 10px;
|
|
text-align: start;
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
letter-spacing: -0.3px;
|
|
line-height: 1.3;
|
|
}
|
|
#headln2c font { font-size: inherit; }
|
|
/* The lighter line under a bold title starts a couple of pixels further
|
|
* in, so it reads as even with the title's first letter */
|
|
.ui_header_below {
|
|
display: block;
|
|
margin-top: 2px;
|
|
padding-left: 2px;
|
|
font-size: 13px;
|
|
font-weight: 400;
|
|
letter-spacing: 0;
|
|
color: var(--text-color-muted);
|
|
}
|
|
#headln2c br:has(+ .ui_header_below) { display: none; }
|
|
#headln2l { order: 2; }
|
|
#headln2r { order: 3; }
|
|
#headln2l, #headln2r {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 6px 8px;
|
|
font-size: 12.5px;
|
|
font-weight: 400;
|
|
white-space: nowrap;
|
|
}
|
|
#headln2l br, #headln2r br { display: none; }
|
|
#headln2l:empty, #headln2r:empty { display: none; }
|
|
/* The module links beside the title are small buttons; the flex box
|
|
* centers the icon and the text in the 26px height */
|
|
#headln2l a, #headln2r a, .page-action, .ui_links_row > a {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
box-sizing: border-box;
|
|
min-height: 26px;
|
|
padding: 0 10px;
|
|
border: 1px solid var(--border-color-darker);
|
|
border-radius: var(--radius-sm);
|
|
background: var(--main-content);
|
|
color: var(--text-color);
|
|
font-size: 12.5px;
|
|
font-weight: 500;
|
|
line-height: 1.333;
|
|
white-space: nowrap;
|
|
text-decoration: none;
|
|
}
|
|
#headln2l a:hover, #headln2r a:hover, .page-action:hover,
|
|
.ui_links_row > a:hover {
|
|
background: var(--bg-color);
|
|
border-color: var(--text-color-disabled);
|
|
color: var(--text-color);
|
|
text-decoration: none;
|
|
}
|
|
#headln2r b { font-weight: 600; }
|
|
/* Text under the title, such as the domain a page is about, sits right
|
|
* below it */
|
|
table.header:has(+ .ui_post_header) {
|
|
margin-bottom: 0;
|
|
padding-bottom: 2px;
|
|
}
|
|
.ui_post_header {
|
|
margin: 0 0 12px;
|
|
padding-left: calc(var(--title-inset) + 2px);
|
|
color: var(--text-color-desc);
|
|
}
|
|
/* Subheadings between groups on a page are small capital labels, like
|
|
* the labels in the menu and on the tiles, inset like the page title */
|
|
.ui_subheading {
|
|
margin: 20px 0 8px;
|
|
padding-left: var(--title-inset);
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.06em;
|
|
text-transform: uppercase;
|
|
color: var(--text-color-muted);
|
|
}
|
|
.ui_subheading:first-child { margin-top: 0; }
|
|
|
|
/* Return links printed under a page by theme_footer */
|
|
.ui_footer_links {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 4px 8px;
|
|
margin: 12px 0 0;
|
|
color: var(--text-color-muted);
|
|
}
|
|
.ui_footer_links svg { vertical-align: middle; }
|
|
/* Paging arrows */
|
|
.ui_nav_link svg { vertical-align: middle; }
|
|
.ui_nav_link_disabled { color: var(--text-color-disabled); }
|
|
|
|
/* ---- Notices ---- */
|
|
|
|
.ui_alert_box, .alert {
|
|
box-sizing: border-box;
|
|
margin: 0 0 10px;
|
|
padding: 10px 14px;
|
|
line-height: 1.45;
|
|
border: 1px solid var(--border-color-info);
|
|
border-radius: var(--radius);
|
|
background: var(--bg-color-info);
|
|
color: var(--text-color-info-stressed);
|
|
}
|
|
.alert-success {
|
|
background: var(--bg-color-success);
|
|
border-color: var(--border-color-success);
|
|
color: var(--text-color-success-stressed);
|
|
}
|
|
.alert-warning {
|
|
background: var(--bg-color-warning);
|
|
border-color: var(--border-color-warning);
|
|
color: var(--text-color-warning-stressed);
|
|
}
|
|
.alert-danger, .alert-danger-fatal {
|
|
background: var(--bg-color-danger);
|
|
border-color: var(--border-color-danger);
|
|
color: var(--text-color-danger-stressed);
|
|
}
|
|
.ui_alert_box center, .alert center { text-align: inherit; }
|
|
.ui_alert_box a, .alert a { color: inherit; text-decoration: underline; }
|
|
|
|
/* Notices drawn by the theme: an icon, the text, and any trailing
|
|
* buttons on the same line as the text. The icon is as tall as a line
|
|
* of text, so it sits centered on the first line when there are more */
|
|
.ui_alert_box, .alert:has(> .ui_alert_icon) {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 12px;
|
|
}
|
|
.ui_alert_icon {
|
|
display: inline-flex;
|
|
flex: 0 0 auto;
|
|
align-items: center;
|
|
height: 1.45em;
|
|
color: var(--text-color-info);
|
|
}
|
|
.alert-success .ui_alert_icon { color: var(--text-color-success); }
|
|
.alert-warning .ui_alert_icon { color: var(--text-color-warning); }
|
|
.alert-danger .ui_alert_icon,
|
|
.alert-danger-fatal .ui_alert_icon { color: var(--text-color-danger); }
|
|
.ui_alert_body {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
}
|
|
.ui_alert_body p { margin: 4px 0; }
|
|
.ui_alert_body p:first-child { margin-top: 0; }
|
|
.ui_alert_body p:last-child { margin-bottom: 0; }
|
|
.ui_alert_body form { margin: 0; }
|
|
.ui_alert_body:has(> .ui_alert_actions),
|
|
.ui_alert_body form:has(> .ui_alert_actions) {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 2px 16px;
|
|
}
|
|
/* Buttons in a notice are slim, like the header buttons. With buttons,
|
|
* the icon and the text line are both as tall as one, so the three
|
|
* center on one another whether the buttons share the text line or sit
|
|
* under it */
|
|
.ui_alert_box { --alert-button-height: 26px; }
|
|
.ui_alert_box:has(.ui_alert_actions) > .ui_alert_icon {
|
|
height: var(--alert-button-height);
|
|
}
|
|
.ui_alert_box:has(.ui_alert_actions) .ui_alert_text {
|
|
padding: calc((var(--alert-button-height) - 1.45em) / 2) 0;
|
|
}
|
|
/* The text keeps its own width and grows into the rest of the line, so
|
|
* buttons that fit end at the right; when they do not fit they drop
|
|
* under the text, at its left edge */
|
|
.ui_alert_text {
|
|
flex: 1 0 auto;
|
|
max-width: 100%;
|
|
min-width: 0;
|
|
}
|
|
.ui_alert_actions {
|
|
display: flex;
|
|
flex: 0 0 auto;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
.ui_alert_actions form { display: contents; }
|
|
/* Buttons printed as a form end table become items of the actions row,
|
|
* like bare buttons */
|
|
.ui_alert_actions table.ui_form_end_buttons,
|
|
.ui_alert_actions table.ui_form_end_buttons > tbody,
|
|
.ui_alert_actions table.ui_form_end_buttons > tbody > tr,
|
|
.ui_alert_actions table.ui_form_end_buttons td { display: contents; }
|
|
.ui_alert_box table.ui_form_end_buttons { margin: 6px 0 0; }
|
|
.ui_alert_actions input.ui_submit, .ui_alert_actions input.ui_button,
|
|
.ui_alert_actions button,
|
|
.ui_alert_box table.ui_form_end_buttons input.ui_submit {
|
|
min-height: var(--alert-button-height);
|
|
margin: 0;
|
|
padding: 0 10px;
|
|
font-size: 12.5px;
|
|
line-height: 1.2;
|
|
}
|
|
/* The first of several buttons in a notice is its main action, filled
|
|
* in the notice's own color and a shade darker under the mouse. The
|
|
* buttons are either bare inputs or the cells of a form end table; in
|
|
* the table the first cell's button is the main one, unless it deletes,
|
|
* and the selectors outrank the primary button rule of ordinary forms. */
|
|
.ui_alert_box input.ui_submit:first-of-type:not(:last-of-type),
|
|
.ui_alert_box table.ui_form_end_buttons td:first-child > input.ui_submit:not([name*="delete"]):not([name*="remove"]) {
|
|
background: var(--text-color-info);
|
|
border-color: transparent;
|
|
color: var(--alert-button-text);
|
|
font-weight: 600;
|
|
}
|
|
.ui_alert_box.alert-success input.ui_submit:first-of-type:not(:last-of-type),
|
|
.ui_alert_box.alert-success table.ui_form_end_buttons td:first-child > input.ui_submit:not([name*="delete"]):not([name*="remove"]) {
|
|
background: var(--text-color-success);
|
|
}
|
|
.ui_alert_box.alert-warning input.ui_submit:first-of-type:not(:last-of-type),
|
|
.ui_alert_box.alert-warning table.ui_form_end_buttons td:first-child > input.ui_submit:not([name*="delete"]):not([name*="remove"]) {
|
|
background: var(--alert-button-warning);
|
|
color: var(--alert-button-warning-text);
|
|
}
|
|
.ui_alert_box.alert-danger input.ui_submit:first-of-type:not(:last-of-type),
|
|
.ui_alert_box.alert-danger-fatal input.ui_submit:first-of-type:not(:last-of-type),
|
|
.ui_alert_box.alert-danger table.ui_form_end_buttons td:first-child > input.ui_submit:not([name*="delete"]):not([name*="remove"]),
|
|
.ui_alert_box.alert-danger-fatal table.ui_form_end_buttons td:first-child > input.ui_submit:not([name*="delete"]):not([name*="remove"]) {
|
|
background: var(--text-color-danger);
|
|
}
|
|
.ui_alert_box input.ui_submit:first-of-type:not(:last-of-type):hover,
|
|
.ui_alert_box table.ui_form_end_buttons td:first-child > input.ui_submit:not([name*="delete"]):not([name*="remove"]):hover {
|
|
filter: brightness(0.92);
|
|
}
|
|
|
|
/* ---- Cards ---- */
|
|
/* Forms with a heading, tables of rows, collapsible sections, tab boxes
|
|
* and icon grids all sit in the same card */
|
|
|
|
/* Give modern widgets the theme's card spacing. Add the same gap after a
|
|
* widget group followed by legacy page content. */
|
|
body .ui_page { --ui-gap: var(--card-gap); }
|
|
.ui_page:has(~ :not(script, style, link, dialog, [hidden], .ui_hidden,
|
|
input[type="hidden"])) {
|
|
margin-bottom: var(--card-gap);
|
|
}
|
|
|
|
table.shrinkwrapper, table.wrapper, div.wrapper,
|
|
details.ui_hidden_table_start, details.section, div.ui_tabs_box {
|
|
width: 100%;
|
|
margin: 0 0 var(--card-gap);
|
|
background: var(--main-content);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--radius);
|
|
box-shadow: var(--shadow);
|
|
}
|
|
div.wrapper, details.ui_hidden_table_start, details.section,
|
|
div.ui_tabs_box { box-sizing: border-box; }
|
|
table.shrinkwrapper > tbody > tr > td,
|
|
table.wrapper > tbody > tr > td { padding: 0; }
|
|
/* Data-table cards stay within the page. Only their columns scroll when
|
|
* their natural width is wider than the available content area. */
|
|
table.wrapper:has(> tbody > tr > td > .ui_columns_scroll) {
|
|
table-layout: fixed;
|
|
}
|
|
.ui_columns_scroll {
|
|
width: 100%;
|
|
max-width: 100%;
|
|
overflow-x: auto;
|
|
}
|
|
/* Keep form tables inside their cards regardless of control width. Column
|
|
* count classes give labels useful proportions in multi-field rows. */
|
|
table.shrinkwrapper > tbody > tr > td > .ui_table,
|
|
table.ui_form_rows { table-layout: fixed; }
|
|
table.ui_form_rows_2 > tbody > tr.ui_form_pair > td.ui_form_label:not([width]) {
|
|
width: 20%;
|
|
}
|
|
:is(table.ui_form_rows_3, table.ui_form_rows_4) > tbody > tr.ui_form_pair > td.ui_form_label:not([width]) {
|
|
width: 15%;
|
|
}
|
|
:is(table.ui_form_rows_5, table.ui_form_rows_6) > tbody > tr.ui_form_pair > td.ui_form_label:not([width]) {
|
|
width: 10%;
|
|
}
|
|
|
|
/* ---- Forms with a heading (ui_table_start) ---- */
|
|
/* Markup: table.shrinkwrapper > td > table.ui_table with the heading in
|
|
* thead and the label/value rows in a nested table. The heading is plain
|
|
* text over a hairline. */
|
|
|
|
table.ui_table {
|
|
width: 100%;
|
|
border-collapse: separate;
|
|
border-spacing: 0;
|
|
}
|
|
table.ui_table td { vertical-align: top; }
|
|
table.ui_table > thead > tr > td,
|
|
table.ui_table > tbody > tr.maintitle > td {
|
|
padding: 9px 16px;
|
|
border-bottom: 1px solid var(--border-color-light);
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
vertical-align: middle;
|
|
}
|
|
table.ui_table > thead > tr > td * { vertical-align: middle; }
|
|
table.ui_table > thead > tr > td b { font-weight: 600; }
|
|
/* The right-hand part of a heading is plain text */
|
|
table.ui_table > thead > tr > td[align="right"] {
|
|
font-size: 13px;
|
|
font-weight: 400;
|
|
}
|
|
tr.ui_table_body > td { padding: 8px 16px 12px; }
|
|
/* Label and value cells align at the top in every form table, also in
|
|
* collapsible sections, whose table has no ui_table class */
|
|
tr.ui_form_pair > td {
|
|
padding: 4px 3px;
|
|
vertical-align: top;
|
|
}
|
|
td.ui_form_label {
|
|
text-align: end;
|
|
color: var(--text-color-desc);
|
|
}
|
|
td.ui_form_label b, td.ui_form_label label { font-weight: 400; }
|
|
/* Room between a label and its value */
|
|
tr.ui_form_pair .ui_form_value { padding-left: 10px; }
|
|
/* A row without a label, such as a grid of features, starts where a
|
|
* label would */
|
|
tr.ui_form_pair > .ui_form_value:first-child { padding-left: 3px; }
|
|
/* A row whose value holds a field, menu or button, on its own or in an
|
|
* inline wrapper, gets an invisible strut of the control height on the
|
|
* first line of both cells, so the label centers on the control and a
|
|
* value of several lines keeps its label level with the first one.
|
|
* Rows of radios or checkboxes, text areas and nested tables keep the
|
|
* plain top alignment. A select switch widget starts with its menu at
|
|
* full control height, so only its label gets the strut: one in the
|
|
* cell would push the whole widget down. */
|
|
tr.ui_form_pair:has(
|
|
> td.ui_form_value > input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="image"]),
|
|
> td.ui_form_value > select:not([multiple]):not([size]:not([size="1"])),
|
|
> td.ui_form_value > button,
|
|
> td.ui_form_value > :is(span, b, label, nobr, font) > input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="image"]),
|
|
> td.ui_form_value > :is(span, b, label, nobr, font) > select:not([multiple]):not([size]:not([size="1"]))
|
|
) > :is(td.ui_form_label, td.ui_form_value)::before,
|
|
tr.ui_form_pair:has(> td.ui_form_value > .ui_select_switch:first-child > select:first-child) > td.ui_form_label::before {
|
|
content: "";
|
|
display: inline-block;
|
|
width: 0;
|
|
height: var(--control-height);
|
|
vertical-align: middle;
|
|
}
|
|
/* Tables nested in a value cell are flush, radio tables apart */
|
|
.ui_form_pair td table tr td { padding: 0; }
|
|
.ui_form_pair td table.ui_radio_table tr td,
|
|
.ui_radio_table tr td { padding: 2px; }
|
|
/* Grids of checkboxes inside a form row, such as a server's features */
|
|
.ui_form_pair td table.ui_grid_table tr td { padding: 3px 4px; }
|
|
.ui_form_pair td table.ui_grid_table tr td:first-child { padding-left: 0; }
|
|
.ui_form_pair td > table.wrapper,
|
|
.ui_form_pair td > div > table.wrapper { margin: 0; }
|
|
/* Lists and the old bar charts some modules print inside forms */
|
|
table.ui_table dt, table.ui_table dd { padding: 0 2px; }
|
|
table.ui_table td div.barchart * { margin: 0; }
|
|
.barchart {
|
|
padding: 1px;
|
|
border: 1px solid var(--border-color);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* A form inside another form's row is an inner box */
|
|
table.shrinkwrapper table.shrinkwrapper,
|
|
table.shrinkwrapper table.wrapper,
|
|
table.wrapper table.wrapper {
|
|
margin: 4px 0;
|
|
border-color: var(--border-color-light);
|
|
box-shadow: none;
|
|
}
|
|
/* A grid in a form row, such as the feature checkboxes, is plain: the
|
|
* card edge and shadow the theme gives every grid would show as a line
|
|
* under its last row */
|
|
.ui_form_pair td table.wrapper:has(> tbody > tr > td > table.ui_grid_table) {
|
|
border: 0;
|
|
border-radius: 0;
|
|
background: transparent;
|
|
box-shadow: none;
|
|
}
|
|
/* Nested form tables have no edge, unless marked with the border class */
|
|
table.ui_table .ui_table { border: 0; }
|
|
table.ui_table.border {
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--radius);
|
|
background: var(--main-content);
|
|
}
|
|
/* Cells of a grid of elements (ui_grid_table) */
|
|
table.ui_grid_table td { padding: 2px 4px; }
|
|
/* A grid of plain items in a card of its own, such as the list of Webmin
|
|
* users, keeps the card's 16px side padding in its first and last
|
|
* columns, with some air above its first row and under its last. Grids
|
|
* inside a form row, grids of tables and the two-column link bars keep
|
|
* their tight cells. */
|
|
table.wrapper:not(.ui_form_pair *) > tbody > tr > td > table.ui_grid_table:not(:has(.ui_links_row, table)) > tbody > tr.ui_grid_row > td.ui_grid_cell {
|
|
padding: 4px 8px;
|
|
}
|
|
table.wrapper:not(.ui_form_pair *) > tbody > tr > td > table.ui_grid_table:not(:has(.ui_links_row, table)) > tbody > tr.ui_grid_row > td.ui_grid_cell:first-child {
|
|
padding-left: 16px;
|
|
}
|
|
table.wrapper:not(.ui_form_pair *) > tbody > tr > td > table.ui_grid_table:not(:has(.ui_links_row, table)) > tbody > tr.ui_grid_row > td.ui_grid_cell:last-child {
|
|
padding-right: 16px;
|
|
}
|
|
table.wrapper:not(.ui_form_pair *) > tbody > tr > td > table.ui_grid_table:not(:has(.ui_links_row, table)) > tbody > tr.ui_grid_row:first-child > td.ui_grid_cell {
|
|
padding-top: 8px;
|
|
}
|
|
table.wrapper:not(.ui_form_pair *) > tbody > tr > td > table.ui_grid_table:not(:has(.ui_links_row, table)) > tbody > tr.ui_grid_row:last-child > td.ui_grid_cell {
|
|
padding-bottom: 8px;
|
|
}
|
|
|
|
/* Collapsible sections (ui_hidden_table_start) and the system
|
|
* information cards share one look */
|
|
details.ui_hidden_table_start > summary, details.section > summary {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 9px 16px;
|
|
border-radius: var(--radius);
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
list-style: none;
|
|
}
|
|
details.section > summary > span { flex: 1 1 auto; }
|
|
details[open].ui_hidden_table_start > summary,
|
|
details[open].section > summary {
|
|
border-bottom: 1px solid var(--border-color-light);
|
|
border-radius: var(--radius) var(--radius) 0 0;
|
|
}
|
|
details.ui_hidden_table_start > summary span { white-space: nowrap; }
|
|
details.ui_hidden_table_start > summary span.rightheading {
|
|
margin-left: auto;
|
|
font-size: 13px;
|
|
font-weight: 400;
|
|
}
|
|
details.ui_hidden_table_start > table {
|
|
display: block;
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
max-width: 100%;
|
|
padding: 8px 14px 10px;
|
|
overflow-x: auto;
|
|
}
|
|
details.ui_hidden_table_start > table > tbody {
|
|
display: table;
|
|
width: 100%;
|
|
}
|
|
/* Make a collapsible form the only horizontal scroll area. Nested column
|
|
* tables keep their natural width without adding another scrollbar. */
|
|
details.ui_hidden_table_start table.wrapper:has(.ui_columns_scroll) {
|
|
width: max-content;
|
|
min-width: 100%;
|
|
table-layout: auto;
|
|
}
|
|
details.ui_hidden_table_start .ui_columns_scroll {
|
|
width: auto;
|
|
max-width: none;
|
|
overflow-x: visible;
|
|
}
|
|
details.ui_hidden_table_start table.ui_columns {
|
|
width: max-content;
|
|
min-width: 100%;
|
|
}
|
|
.section-body { padding: 10px 16px 14px; }
|
|
/* Text above a dashboard block's content */
|
|
.section-note {
|
|
margin: 0 0 8px;
|
|
color: var(--text-color-muted);
|
|
}
|
|
|
|
/* ---- Tables of rows (ui_columns_start) ---- */
|
|
/* The heading rows carry the maintitle class from the theme config as
|
|
* well as the ui_columns classes; body rows use mainbody, and the
|
|
* row0/row1, mainsel and mainhigh names are kept for modules that print
|
|
* them directly. */
|
|
|
|
table.ui_columns {
|
|
width: 100%;
|
|
border-collapse: separate;
|
|
border-spacing: 0;
|
|
}
|
|
table.ui_columns > thead > tr > td,
|
|
table.ui_columns > tbody > tr.maintitle > td,
|
|
tr.ui_columns_heads > td, tr.ui_columns_heading > td, tr.ui_columns_header > td {
|
|
padding: 8px 16px;
|
|
border-bottom: 1px solid var(--border-color);
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: var(--text-color-muted);
|
|
vertical-align: middle;
|
|
}
|
|
/* The title row above the column headings is larger */
|
|
table.ui_columns > thead > tr.ui_columns_heading > td {
|
|
font-size: 14px;
|
|
color: var(--text-color);
|
|
}
|
|
table.ui_columns > thead > tr > td b { font-weight: inherit; }
|
|
/* Rows are as tall as their content plus 4px above and below, in
|
|
* every table alike */
|
|
table.ui_columns > tbody > tr > td,
|
|
table.sortable > tbody > tr > td {
|
|
padding: 4px 16px;
|
|
border-bottom: 1px solid var(--border-color-lighter);
|
|
color: var(--text-color);
|
|
vertical-align: middle;
|
|
}
|
|
table.ui_columns > tbody > tr:last-child > td { border-bottom: 0; }
|
|
/* The first column, or the one after a checkbox, names the row and is
|
|
* a little bolder; the checkbox column is as narrow as it can be */
|
|
table.ui_columns > tbody > tr > td:first-child,
|
|
table.ui_columns > tbody > tr > td.ui_checked_checkbox + td,
|
|
table.ui_columns > tbody > tr > td.ui_radio_radio + td {
|
|
color: var(--text-color);
|
|
font-weight: 500;
|
|
}
|
|
table.ui_columns > tbody > tr > td.ui_checked_checkbox,
|
|
table.ui_columns > tbody > tr > td.ui_radio_radio {
|
|
width: 1%;
|
|
padding-right: 4px;
|
|
}
|
|
table.ui_columns > tbody > tr.ui_columns_header > td,
|
|
tr.ui_columns_header > td {
|
|
font-size: inherit;
|
|
color: var(--text-color);
|
|
background: var(--bg-table-header);
|
|
}
|
|
/* Rows carry bgcolor attributes from the theme config; the palette wins */
|
|
tr.row0, tr.row1, tr.mainbody { background: transparent; }
|
|
tr.maintitle { background: transparent; }
|
|
/* Rows highlight on hover, except in tables of input fields */
|
|
table.ui_columns > tbody > tr:not(:has(input:not([type="checkbox"]):not([type="radio"]), select, textarea)):hover > td,
|
|
tr.mainhigh > td {
|
|
background: var(--bg-table-header);
|
|
}
|
|
tr.mainsel > td, tr.mainhighsel > td,
|
|
tr.ui_checked_columns:has(> td > input:checked) > td,
|
|
tr.ui_radio_columns:has(> td > input:checked) > td {
|
|
background: var(--element-highlight-bg-color);
|
|
}
|
|
table.ui_columns.no-hover > tbody > tr:hover > td { background: transparent; }
|
|
.ui_checked_columns .ui_checked_checkbox { min-width: 20px; }
|
|
.ui_emptymsg { color: var(--text-color-muted); }
|
|
/* The icon grid table, which also carries the main id, and the compact
|
|
* tables some modules mark as simple */
|
|
table#main {
|
|
margin: 0;
|
|
padding: 0;
|
|
border: 0;
|
|
}
|
|
table#main td { padding: 4px; }
|
|
.table-simple {
|
|
width: auto !important;
|
|
max-width: 100%;
|
|
}
|
|
.table-simple tr td { padding: 0 4px !important; }
|
|
|
|
/* A colored word alone in a cell, such as Yes or No, becomes a pill */
|
|
table.ui_columns > tbody > tr > td > .ui_text_color:only-child {
|
|
display: inline-block;
|
|
padding: 2px 9px;
|
|
border-radius: 999px;
|
|
font-size: 11.5px;
|
|
font-weight: 600;
|
|
line-height: 1.4;
|
|
background: var(--bg-color-neutral);
|
|
}
|
|
table.ui_columns > tbody > tr > td > .text_type_success:only-child {
|
|
background: var(--bg-color-success);
|
|
}
|
|
table.ui_columns > tbody > tr > td > .text_type_info:only-child {
|
|
background: var(--bg-color-info);
|
|
}
|
|
table.ui_columns > tbody > tr > td > .text_type_danger:only-child {
|
|
background: var(--bg-color-danger);
|
|
}
|
|
table.ui_columns > tbody > tr > td > .text_type_warn:only-child {
|
|
background: var(--bg-color-neutral);
|
|
color: var(--text-color-neutral-stressed) !important;
|
|
}
|
|
|
|
/* A links row right above a table is drawn as the card's toolbar, and
|
|
* a button row right after it as the card's footer. Both bars are
|
|
* tinted, so they stand apart from the white rows of the table. */
|
|
form.ui_form > .ui_links_row:has(+ table.wrapper),
|
|
form.ui_form > table.wrapper:has(> tbody > tr > td > table.ui_grid_table .ui_links_row):has(+ table.wrapper) {
|
|
margin: 0;
|
|
padding: 11px 16px;
|
|
background: var(--bg-table-header);
|
|
border: 1px solid var(--border-color);
|
|
border-bottom: 0;
|
|
border-radius: var(--radius) var(--radius) 0 0;
|
|
box-shadow: var(--shadow);
|
|
}
|
|
/* The two-column variant, with links at the left and the right, is a
|
|
* table: its cell carries the bar padding, not the table */
|
|
form.ui_form > table.wrapper:has(> tbody > tr > td > table.ui_grid_table .ui_links_row):has(+ table.wrapper) {
|
|
padding: 0;
|
|
}
|
|
form.ui_form > table.wrapper:has(> tbody > tr > td > table.ui_grid_table .ui_links_row):has(+ table.wrapper) > tbody > tr > td {
|
|
padding: 11px 16px;
|
|
}
|
|
form.ui_form > table.wrapper:has(> tbody > tr > td > table.ui_grid_table .ui_links_row) table.ui_grid_table td {
|
|
padding: 0;
|
|
}
|
|
form.ui_form > table.wrapper:has(> tbody > tr > td > table.ui_grid_table .ui_links_row) .ui_links_row {
|
|
margin: 0;
|
|
}
|
|
form.ui_form > .ui_links_row:has(+ table.wrapper) + table.wrapper,
|
|
form.ui_form > table.wrapper:has(.ui_links_row) + table.wrapper {
|
|
border-top-left-radius: 0;
|
|
border-top-right-radius: 0;
|
|
}
|
|
form.ui_form > table.wrapper:has(+ table.ui_form_end_buttons) {
|
|
border-bottom-left-radius: 0;
|
|
border-bottom-right-radius: 0;
|
|
}
|
|
form.ui_form > table.wrapper + table.ui_form_end_buttons {
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
margin: calc(-1px - var(--card-gap)) 0 var(--card-gap);
|
|
padding: 11px 16px;
|
|
background: var(--bg-table-header);
|
|
border: 1px solid var(--border-color);
|
|
border-top: 1px solid var(--border-color-light);
|
|
border-radius: 0 0 var(--radius) var(--radius);
|
|
}
|
|
form.ui_form > table.wrapper + table.ui_form_end_buttons + .ui_links_row {
|
|
margin-top: -4px;
|
|
}
|
|
/* A links row right after a table is the card's footer, matching the
|
|
* toolbar above it; any button row after that footer joins it */
|
|
form.ui_form > table.wrapper:has(+ .ui_links_row),
|
|
form.ui_form > table.wrapper:has(+ table.wrapper > tbody > tr > td > table.ui_grid_table .ui_links_row) {
|
|
border-bottom-left-radius: 0;
|
|
border-bottom-right-radius: 0;
|
|
}
|
|
form.ui_form > table.wrapper + .ui_links_row,
|
|
form.ui_form > table.wrapper + table.wrapper:has(> tbody > tr > td > table.ui_grid_table .ui_links_row) {
|
|
margin: calc(-1px - var(--card-gap)) 0 var(--card-gap);
|
|
padding: 11px 16px;
|
|
background: var(--bg-table-header);
|
|
border: 1px solid var(--border-color);
|
|
border-top: 1px solid var(--border-color-light);
|
|
border-radius: 0 0 var(--radius) var(--radius);
|
|
box-shadow: var(--shadow);
|
|
}
|
|
form.ui_form > table.wrapper + table.wrapper:has(> tbody > tr > td > table.ui_grid_table .ui_links_row) {
|
|
padding: 0;
|
|
}
|
|
form.ui_form > table.wrapper + table.wrapper:has(> tbody > tr > td > table.ui_grid_table .ui_links_row) > tbody > tr > td {
|
|
padding: 11px 16px;
|
|
}
|
|
/* A links row and a button row under one table form a single footer
|
|
* bar: 11px above the links, 11px between the rows and 11px under the
|
|
* buttons, as in the toolbar above the table */
|
|
form.ui_form > table.wrapper + .ui_links_row:has(+ table.ui_form_end_buttons),
|
|
form.ui_form > table.wrapper + table.wrapper:has(> tbody > tr > td > table.ui_grid_table .ui_links_row):has(+ table.ui_form_end_buttons) {
|
|
margin-bottom: 0;
|
|
padding-bottom: 0;
|
|
border-bottom: 0;
|
|
border-radius: 0;
|
|
box-shadow: none;
|
|
}
|
|
form.ui_form > table.wrapper + table.wrapper:has(> tbody > tr > td > table.ui_grid_table .ui_links_row):has(+ table.ui_form_end_buttons) > tbody > tr > td {
|
|
padding-bottom: 0;
|
|
}
|
|
form.ui_form > table.wrapper + .ui_links_row + table.ui_form_end_buttons,
|
|
form.ui_form > table.wrapper + table.wrapper:has(> tbody > tr > td > table.ui_grid_table .ui_links_row) + table.ui_form_end_buttons {
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
margin: 0 0 var(--card-gap);
|
|
padding: 11px 16px;
|
|
background: var(--bg-table-header);
|
|
border: 1px solid var(--border-color);
|
|
border-top: 0;
|
|
border-radius: 0 0 var(--radius) var(--radius);
|
|
box-shadow: var(--shadow);
|
|
}
|
|
|
|
/* A legacy card form may print its action buttons directly after the
|
|
* table. Leave the normal card gap before another form begins; the core
|
|
* may put its form-end script between the two forms. */
|
|
form.ui_form:has(> :is(table.wrapper, table.shrinkwrapper)):has(> .ui_submit):has(
|
|
+ form.ui_form, + script + form.ui_form) {
|
|
margin-bottom: var(--card-gap);
|
|
}
|
|
|
|
/* A small form of controls before another form or a table, such as the
|
|
* search box over a list of web apps or the file menu over a config
|
|
* editor, is one row of controls with the card gap under it, so it
|
|
* never touches what follows. The core prints a script right after
|
|
* every form end, so the next block may come after that script. */
|
|
form:not(.menu-form):not(:has(table)):has(+ form, + table.wrapper, + table.shrinkwrapper, + script + form, + script + table.wrapper, + script + table.shrinkwrapper) {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 6px 8px;
|
|
margin: 0 0 var(--card-gap);
|
|
}
|
|
|
|
/* Icon grids (icons_table): the table is a grid of equal columns and
|
|
* its cells the items, so the icons reflow as the page narrows instead
|
|
* of squeezing their columns, and a partly filled last row keeps the
|
|
* columns of the rows above; the empty cells that fill the last row are
|
|
* dropped */
|
|
div.wrapper:has(> table.icons_table) { padding: 8px; }
|
|
table.icons_table {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
}
|
|
table.icons_table > tbody, table.icons_table > tbody > tr {
|
|
display: contents;
|
|
}
|
|
/* Each cell stacks the icon over its name, centered. The name is small
|
|
* text in the text color, balanced over two lines when it wraps, and
|
|
* takes the link color only on hover */
|
|
table.icons_table > tbody > tr > td {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 6px;
|
|
box-sizing: border-box;
|
|
/* The cell's own width attribute is a share of the old table, and
|
|
* would shrink the cell to a sliver of its grid column */
|
|
width: auto;
|
|
min-width: 0;
|
|
padding: 12px 8px 14px;
|
|
text-align: center;
|
|
}
|
|
table.icons_table > tbody > tr > td:empty { display: none; }
|
|
table.icons_table table {
|
|
display: table;
|
|
margin: 0;
|
|
}
|
|
table.icons_table a {
|
|
max-width: 100%;
|
|
color: var(--text-color);
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
line-height: 1.35;
|
|
text-decoration: none;
|
|
text-wrap: balance;
|
|
overflow-wrap: break-word;
|
|
}
|
|
table.icons_table a:hover {
|
|
color: var(--link-color);
|
|
text-decoration: none;
|
|
}
|
|
table.icons_table img { display: block; }
|
|
|
|
/* ---- Tabs (ui_tabs_start) ---- */
|
|
|
|
/* The tab row sits on the ground, so its line is the darker one */
|
|
div.ui_tabs {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 4px;
|
|
margin: 0 0 12px;
|
|
border-bottom: 1px solid var(--border-color-darker);
|
|
}
|
|
div.ui_tabs a.ui_tab {
|
|
display: block;
|
|
margin-bottom: -1px;
|
|
padding: 8px 12px 10px;
|
|
border-bottom: 2px solid transparent;
|
|
color: var(--text-color-muted);
|
|
font-weight: 500;
|
|
text-decoration: none;
|
|
}
|
|
div.ui_tabs a.ui_tab:hover { color: var(--text-color); }
|
|
div.ui_tabs a.ui_tab.ui_tab_selected {
|
|
border-bottom-color: var(--accent-color);
|
|
color: var(--text-color);
|
|
font-weight: 600;
|
|
}
|
|
div.ui_tabs a.ui_tab sup {
|
|
vertical-align: text-top;
|
|
font-size: 70%;
|
|
}
|
|
div.ui_tabs_box { padding: 12px 14px 14px; }
|
|
/* The last block in a tab body ends at the box padding */
|
|
div.ui_tabs_box .ui_tabs_start > form:last-of-type > table.ui_form_end_buttons:last-child,
|
|
div.ui_tabs_box .ui_tabs_start > table.ui_form_end_buttons:last-of-type,
|
|
div.ui_tabs_box .ui_tabs_start > form:last-of-type > table.shrinkwrapper:last-child,
|
|
div.ui_tabs_box .ui_tabs_start > table.shrinkwrapper:last-of-type,
|
|
div.ui_tabs_box .ui_tabs_start > table.wrapper:last-of-type {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
/* ---- Help bubbles and tooltips ---- */
|
|
|
|
.ui_help {
|
|
display: inline-block;
|
|
margin: 0 0 0 5px;
|
|
font-size: 10px;
|
|
line-height: 14px;
|
|
min-width: 14px;
|
|
text-align: center;
|
|
color: var(--text-color-inverse);
|
|
background: var(--text-color-muted);
|
|
border-radius: 50%;
|
|
cursor: help;
|
|
vertical-align: super;
|
|
opacity: 0.8;
|
|
}
|
|
.ui_help samp { font: inherit; }
|
|
/* Help bubbles show while the mouse is over their cell; the one next to
|
|
* a form label always shows */
|
|
tr:has(.ui_help) td:not(:hover) .ui_help { visibility: hidden; }
|
|
tr:has(.ui_help) td.ui_form_label .ui_help { visibility: visible; }
|
|
td:not(:hover) .ui_link_help { visibility: hidden; }
|
|
.ui_link_help {
|
|
display: initial;
|
|
float: right;
|
|
margin-right: 4px;
|
|
}
|
|
.ui_text_wrap { float: right; }
|
|
/* Tooltips from the aria-label of elements marked data-tooltip: a dark
|
|
* box with an arrow above the element, shown on hover */
|
|
[data-tooltip][aria-label] {
|
|
position: relative;
|
|
vertical-align: top;
|
|
}
|
|
/* Card actions align help bubbles through ui-lib, but this theme still
|
|
* needs the bubble to anchor its generated tooltip. */
|
|
.ui_card_actions .ui_help[data-tooltip][aria-label] {
|
|
position: relative;
|
|
}
|
|
[data-tooltip][aria-label]::after {
|
|
content: "";
|
|
position: absolute;
|
|
bottom: 100%;
|
|
right: 50%;
|
|
width: 0;
|
|
height: 0;
|
|
margin-bottom: 5px;
|
|
border: 10px solid transparent;
|
|
border-top-color: var(--text-color);
|
|
border-bottom-width: 0;
|
|
transform: translateX(50%);
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
/* Keep a tooltip near the top above the sticky page header. */
|
|
z-index: 10;
|
|
}
|
|
[data-tooltip][aria-label]::before {
|
|
content: attr(aria-label);
|
|
position: absolute;
|
|
bottom: calc(15px + 100%);
|
|
right: 50%;
|
|
width: max-content;
|
|
max-width: 50vw;
|
|
padding: 4px 8px;
|
|
border-radius: var(--radius-sm);
|
|
background: var(--text-color);
|
|
color: var(--bg-color);
|
|
font-size: 11px;
|
|
font-weight: normal;
|
|
text-align: center;
|
|
text-shadow: none;
|
|
/* Wrap between words, breaking one only when it is too long. */
|
|
overflow-wrap: anywhere;
|
|
transform: translateX(50%);
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
z-index: 10;
|
|
}
|
|
[data-tooltip][aria-label]:hover::before,
|
|
[data-tooltip][aria-label]:hover::after {
|
|
visibility: visible;
|
|
opacity: 1;
|
|
transition-duration: 0.5s;
|
|
}
|
|
/* Opacity gives the tooltip trigger its own stacking context. Raise that
|
|
* context while the tooltip is visible, because raising its children alone
|
|
* cannot place them above the sticky header. */
|
|
[data-tooltip][aria-label]:hover {
|
|
z-index: 10;
|
|
}
|
|
|
|
/* ---- Inline disclosures (ui_details) ---- */
|
|
|
|
/* The arrow of a closed disclosure marked on-hover shows only under the
|
|
* mouse */
|
|
details:not([open]).on-hover:not(:hover) summary::after {
|
|
visibility: hidden !important;
|
|
}
|
|
/* An inline disclosure is plain text with an arrow after its summary
|
|
* and its content indented under it */
|
|
details.inline {
|
|
margin: 0;
|
|
padding: 0;
|
|
background: transparent;
|
|
border: 0;
|
|
cursor: pointer;
|
|
}
|
|
details.inline > summary { list-style: none; }
|
|
details.inline > summary::after {
|
|
content: "\23F4";
|
|
position: relative;
|
|
margin: 0 7px;
|
|
color: var(--text-color-muted);
|
|
}
|
|
details[open].inline > summary::after { content: "\23F7"; }
|
|
details.inline > summary + span {
|
|
display: inline-block;
|
|
margin: 1px 0 0 18px;
|
|
font-size: 98%;
|
|
line-height: 1.33;
|
|
cursor: default;
|
|
}
|
|
details.inline.inlined > summary,
|
|
details.inline.inlined { display: inline !important; }
|
|
details.inline.inlined > span { margin-top: 10px; }
|
|
.ui_table.ui_columns td > details.inline.inlined > span { margin-top: 0; }
|
|
details.inline > summary + span > tt { font-size: 96%; }
|
|
details.inline.fit > summary + span span,
|
|
details.inline.fit > summary + span {
|
|
display: inline-block;
|
|
width: fit-content;
|
|
}
|
|
details > span .ui_alert_box { margin-top: 5px; }
|
|
details > span .ui_alert_box:not(:has(+ p)) { margin-bottom: 5px; }
|
|
details.error, details.inline.error > summary + span {
|
|
color: var(--text-color-danger-stressed);
|
|
}
|
|
/* Line breaks the core marks with data-x-br become small block gaps;
|
|
* one right after a disclosure is dropped */
|
|
[data-second-print]:has(details) + br:has(+ [data-x-br]) { display: none; }
|
|
br[data-x-br] {
|
|
content: "";
|
|
display: block;
|
|
margin: 2em;
|
|
font-size: 42%;
|
|
}
|
|
|
|
/* ---- Left frame (left.cgi) ---- */
|
|
|
|
/* The frame shows the page ground, and the menu floats on it as a card
|
|
* with a 7px gap at the top, the left and the bottom, and rounder
|
|
* corners than the other cards. At the right the card meets the frame
|
|
* edge, so the page padding of the right frame is the whole gap between
|
|
* the card and the content, and a page opened in a window of its own
|
|
* keeps that same padding on every side. The card is as tall as the
|
|
* frame, so its bottom edge stays in view, and the menu scrolls inside
|
|
* it. The body height has to win over the theme's own height reset. */
|
|
body.left-frame {
|
|
box-sizing: border-box;
|
|
height: 100vh !important;
|
|
padding: 7px 0 7px 7px;
|
|
overflow: hidden;
|
|
background: var(--bg-color);
|
|
}
|
|
.menu-panel {
|
|
box-sizing: border-box;
|
|
height: 100%;
|
|
overflow-y: auto;
|
|
background: var(--bg-color-frame);
|
|
border: 1px solid var(--border-color-panel);
|
|
border-radius: 14px;
|
|
box-shadow: 0 2px 6px var(--shadow-color);
|
|
}
|
|
/* The brand block and the product switch stick to the top of the panel
|
|
* while the menu scrolls under them */
|
|
.menu-top {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 2;
|
|
background: var(--bg-color-frame);
|
|
border-bottom: 1px solid var(--border-color-frame);
|
|
}
|
|
/* Leave room below the product switch only when it is present. Without
|
|
* one, the top block ends at its single divider. */
|
|
.menu-top:has(> .mode) { padding-bottom: 12px; }
|
|
/* A bar bouncing along the top edge of the panel while the right frame
|
|
* loads a page; the menu script sets the class on the root */
|
|
.menu-loader {
|
|
display: none;
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
left: 0;
|
|
height: 3px;
|
|
overflow: hidden;
|
|
}
|
|
html.loading-right .menu-loader { display: block; }
|
|
.menu-loader::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 35%;
|
|
height: 100%;
|
|
border-radius: 2px;
|
|
background: var(--accent-color);
|
|
animation: menu-bounce 0.9s ease-in-out infinite alternate;
|
|
}
|
|
/* Match Authentic's crimson progress bar for Cloudmin navigation. */
|
|
.menu-panel-cloudmin .menu-loader::before { background: #dc143c; }
|
|
@keyframes menu-bounce { to { left: 65%; } }
|
|
/* The brand block: the logo has as much room above it as below it, and
|
|
* the logo files are trimmed to their drawing, so every product sits the
|
|
* same way. The logo links to the system information page. */
|
|
.menu-brand {
|
|
padding: 14px 16px 14px;
|
|
}
|
|
/* Separate the brand details from the product switch when it exists. */
|
|
.menu-top:has(> .mode) .menu-brand {
|
|
border-bottom: 1px solid var(--border-color-frame);
|
|
}
|
|
.menu-brand-name {
|
|
display: block;
|
|
margin: 0 0 12px;
|
|
line-height: 0;
|
|
}
|
|
/* The product logo, in the version matching the palette */
|
|
.menu-brand-logo {
|
|
display: var(--show-light);
|
|
height: 36px;
|
|
width: auto;
|
|
max-width: 100%;
|
|
}
|
|
.menu-brand-logo-dark { display: var(--show-dark); }
|
|
/* Host and login under the logo: small lines with a leading icon; a
|
|
* long hostname wraps rather than being cut */
|
|
.menu-meta {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
.menu-host, .menu-user {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 6px;
|
|
min-width: 0;
|
|
font-size: 12px;
|
|
line-height: 1.4;
|
|
color: var(--text-color-desc);
|
|
}
|
|
.menu-host svg, .menu-user svg {
|
|
flex: 0 0 auto;
|
|
margin-top: 2px;
|
|
color: var(--text-color-muted);
|
|
}
|
|
.menu-host span, .menu-user span {
|
|
min-width: 0;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
.menu-user b {
|
|
color: var(--text-color);
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* Product switch between Virtualmin, Cloudmin, Webmin and Usermin */
|
|
.mode {
|
|
display: flex;
|
|
margin: 14px 16px 0;
|
|
padding: 3px;
|
|
background: var(--bg-color-switch);
|
|
border-radius: var(--radius);
|
|
}
|
|
.mode > b {
|
|
flex: 1 1 0;
|
|
padding: 3px 0;
|
|
border-radius: var(--radius-sm);
|
|
background: var(--bg-color-switch-on);
|
|
box-shadow: 0 1px 2px var(--shadow-color);
|
|
color: var(--text-color);
|
|
font-size: 12.5px;
|
|
font-weight: 600;
|
|
text-align: center;
|
|
white-space: nowrap;
|
|
cursor: default;
|
|
}
|
|
.mode > b:has(> a) {
|
|
background: transparent;
|
|
box-shadow: none;
|
|
color: var(--text-color-muted);
|
|
font-weight: 400;
|
|
cursor: pointer;
|
|
}
|
|
.mode > b > a {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
color: inherit;
|
|
text-decoration: none;
|
|
}
|
|
.mode > b > a:hover { color: var(--text-color); }
|
|
/* While the other product's menu loads, the switch ignores further
|
|
* clicks, the pill that was clicked reads as chosen with a small
|
|
* spinning ring before its name, and the old one fades */
|
|
.mode.loading { pointer-events: none; }
|
|
.mode.loading > b:not(.active) { opacity: 0.5; }
|
|
.mode.loading > b.active { color: var(--text-color); }
|
|
/* The ring sits 6px left of the name. Its right margin and its negative
|
|
* left margin cancel its own width, so it takes no room in the centered
|
|
* row and the name does not move when it appears */
|
|
.mode.loading > b.active > a::before {
|
|
content: "";
|
|
flex: 0 0 auto;
|
|
box-sizing: border-box;
|
|
width: 10px;
|
|
height: 10px;
|
|
margin: 0 6px 0 -16px;
|
|
border: 2px solid var(--border-color-darker);
|
|
border-top-color: var(--accent-color);
|
|
border-radius: 50%;
|
|
animation: menu-spin 0.7s linear infinite;
|
|
}
|
|
@keyframes menu-spin { to { transform: rotate(360deg); } }
|
|
|
|
/* Room under the last item, so the menu scrolls past it */
|
|
.leftmenu { padding: 14px 8px 56px; }
|
|
/* The creation link of the menu is its one filled button */
|
|
.menu-create {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
margin: 0 8px 12px;
|
|
padding: 6px 12px;
|
|
border-radius: var(--radius-sm);
|
|
background: var(--accent-color);
|
|
color: var(--text-color-inverse);
|
|
font-weight: 600;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
}
|
|
.menu-create:hover {
|
|
background: var(--accent-color-hover);
|
|
color: var(--text-color-inverse);
|
|
text-decoration: none;
|
|
}
|
|
/* Cloudmin's creation action uses a quiet orange tint and outline. Normal
|
|
* theme text stays readable without making the button dominate the menu. */
|
|
.menu-panel-cloudmin .menu-create {
|
|
background: rgba(223, 121, 53, 0.14);
|
|
box-shadow: inset 0 0 0 1px rgba(223, 121, 53, 0.42);
|
|
color: var(--text-color);
|
|
}
|
|
.menu-panel-cloudmin .menu-create:hover {
|
|
background: rgba(223, 121, 53, 0.22);
|
|
color: var(--text-color);
|
|
}
|
|
.menu-panel-cloudmin .menu-create svg { color: #df7935; }
|
|
/* Virtualmin uses the same quiet treatment with the theme's blue palette. */
|
|
.menu-panel-virtualmin .menu-create {
|
|
background: var(--element-highlight-bg-color);
|
|
box-shadow: inset 0 0 0 1px var(--accent-color-soft);
|
|
color: var(--text-color);
|
|
}
|
|
.menu-panel-virtualmin .menu-create:hover {
|
|
box-shadow: inset 0 0 0 2px var(--border-color-info);
|
|
color: var(--text-color);
|
|
}
|
|
.menu-panel-virtualmin .menu-create svg { color: var(--accent-color-hover); }
|
|
/* Forms in the menu: a caps label over a field that fills the panel */
|
|
.menu-form { margin: 0 8px 12px; }
|
|
.menu-label {
|
|
display: block;
|
|
margin-bottom: 4px;
|
|
font-size: 11.5px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.5px;
|
|
text-transform: uppercase;
|
|
color: var(--text-color-muted);
|
|
}
|
|
.menu-field {
|
|
display: flex;
|
|
gap: 6px;
|
|
}
|
|
.menu-field select, .menu-field input {
|
|
flex: 1 1 auto;
|
|
width: 100%;
|
|
min-width: 0;
|
|
font-size: 12.5px;
|
|
}
|
|
/* Menu rows: 26px links that highlight under the mouse */
|
|
.menu-link {
|
|
display: block;
|
|
padding: 4px 8px 4px 12px;
|
|
border-radius: var(--radius-sm);
|
|
line-height: 18px;
|
|
color: var(--text-color);
|
|
text-decoration: none;
|
|
}
|
|
.menu-link:hover {
|
|
background: var(--bg-color-hover);
|
|
color: var(--text-color);
|
|
text-decoration: none;
|
|
}
|
|
/* Links inside a category are indented under its name */
|
|
.menu-link.menu-sub { padding-left: 24px; }
|
|
/* The logout link stands a little apart, muted, with its icon before
|
|
* the text like any other icon in a link */
|
|
.menu-link.menu-logout {
|
|
margin-top: 8px;
|
|
color: var(--text-color-muted);
|
|
}
|
|
.menu-link svg {
|
|
margin-right: 6px;
|
|
vertical-align: -3px;
|
|
opacity: 0.85;
|
|
}
|
|
/* Items the user cannot use are grayed; small marks after a name sit at
|
|
* the top of the line */
|
|
.menu-link.inactive, .menu-text.inactive {
|
|
opacity: 0.6;
|
|
filter: grayscale(1);
|
|
}
|
|
.menu-link sub {
|
|
vertical-align: top;
|
|
font-size: 80%;
|
|
}
|
|
/* Categories are rows with a chevron at the right, a little taller than
|
|
* the 26px link rows, and their names are cut with an ellipsis */
|
|
details.menu-cat > summary {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
height: 28px;
|
|
padding: 0 10px 0 12px;
|
|
border-radius: var(--radius-sm);
|
|
color: var(--text-color);
|
|
list-style: none;
|
|
}
|
|
details.menu-cat > summary:hover {
|
|
background: var(--bg-color-hover);
|
|
color: var(--text-color);
|
|
}
|
|
details.menu-cat > summary span {
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
details.menu-cat[open] { margin-bottom: 4px; }
|
|
/* Separator lines, and plain text or HTML blocks between items */
|
|
.menu-divider {
|
|
margin: 8px 8px;
|
|
border: 0;
|
|
border-top: 1px solid var(--border-color-frame);
|
|
}
|
|
/* A leading separator follows hidden format metadata in modern Cloudmin,
|
|
* so it has no visible section above it to divide. */
|
|
.leftmenu > .menu-divider:first-child { display: none; }
|
|
.menu-text, .menu-html {
|
|
padding: 4px 12px;
|
|
font-size: 12px;
|
|
color: var(--text-color-muted);
|
|
}
|
|
.menu-html img { max-width: 100%; }
|
|
|
|
/* ---- System information page (right.cgi) ---- */
|
|
/* The page is a widget page: its header holds the title, the operating
|
|
* system and time, and the module links as buttons; then tiles, then
|
|
* the information blocks as collapsible cards. */
|
|
|
|
body.right-frame { padding: 0; }
|
|
/* Blocks are spaced by the widget page gap alone. The margins are set
|
|
* here again because the theme's own card and notice rules outrank the
|
|
* widget stylesheet's sibling gap. */
|
|
.right-frame .ui_page.page {
|
|
--ui-gap: var(--card-gap);
|
|
padding: 0 var(--page-pad-x) var(--page-pad-bottom);
|
|
}
|
|
.right-frame .ui_page.page > * { margin: 0; }
|
|
.right-frame .ui_page.page > * + * { margin-top: var(--ui-gap); }
|
|
/* The header bleeds over the page padding; its selector outranks the
|
|
* margin reset above */
|
|
.right-frame .ui_page.page > .ui_page_head {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 5;
|
|
align-items: center;
|
|
margin: 0 calc(-1 * var(--page-pad-x)) 12px;
|
|
padding: 14px var(--page-pad-x) 12px;
|
|
background: var(--bg-color);
|
|
}
|
|
.right-frame .ui_page_titles { padding-left: var(--title-inset); }
|
|
.right-frame .ui_page_title {
|
|
font-size: 22px;
|
|
font-weight: 700;
|
|
letter-spacing: -0.3px;
|
|
}
|
|
.right-frame .ui_page_desc {
|
|
margin-top: 4px;
|
|
padding-left: 2px;
|
|
color: var(--text-color-desc);
|
|
}
|
|
.right-frame .ui_page_actions {
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
padding-top: 0;
|
|
}
|
|
.page-action svg { color: var(--text-color-muted); }
|
|
|
|
/* Tiles: one figure per card, with its label above and a bar under it.
|
|
* Four across, two on a narrow frame and one on a very narrow one. */
|
|
.tiles { --ui-gap: var(--card-gap); }
|
|
.tiles.ui_grid_fixed { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
|
@media (max-width: 1100px) {
|
|
.tiles.ui_grid_fixed { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
}
|
|
@media (max-width: 520px) {
|
|
.tiles.ui_grid_fixed { grid-template-columns: minmax(0, 1fr); }
|
|
}
|
|
.tile .ui_card_body { padding: 12px 16px 14px; }
|
|
.tile .ui_stat { gap: 4px; }
|
|
.tile .ui_stat_label {
|
|
order: -1;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.5px;
|
|
text-transform: uppercase;
|
|
color: var(--text-color-muted);
|
|
}
|
|
.tile .ui_stat_value {
|
|
font-size: 26px;
|
|
font-weight: 700;
|
|
letter-spacing: -0.3px;
|
|
}
|
|
.tile .ui_stat_desc {
|
|
font-size: 12.5px;
|
|
color: var(--text-color-desc);
|
|
}
|
|
.tile .ui_progress { margin-top: 10px; }
|
|
|
|
/* Information cards */
|
|
.section .ui_dl {
|
|
--ui-dl-label: 150px;
|
|
gap: 8px 40px;
|
|
}
|
|
.section .ui_dl_cols > .ui_dl_row { min-width: calc((100% - 40px) / 2); }
|
|
.section .ui_dl_row { gap: 3px 14px; }
|
|
.section .ui_dl_row dt {
|
|
font-weight: 400;
|
|
color: var(--text-color-desc);
|
|
text-align: end;
|
|
}
|
|
.section .ui_dl_row dd { display: block; }
|
|
/* Values in the system status card are monospace */
|
|
.section[data-name="system-statussysinfo"] .ui_dl_row dd {
|
|
font-family: var(--font-family-mono);
|
|
font-size: 12.5px;
|
|
}
|
|
.section-body > .ui_dl + .ui_dl { margin-top: 8px; }
|
|
/* Bars in cards and tiles are thin and round, and their second segment
|
|
* is a soft accent instead of gray */
|
|
.section .ui_progress { margin-top: 6px; }
|
|
.section .ui_progress_track, .tile .ui_progress_track {
|
|
height: 6px;
|
|
border-radius: 999px;
|
|
}
|
|
.section .ui_progress_bar.ui_bg_neutral,
|
|
.tile .ui_progress_bar.ui_bg_neutral { background: var(--accent-color-soft); }
|
|
.section-body > table.wrapper:last-child { margin-bottom: 0; }
|
|
/* A paragraph break between a note and the table under it, as in the
|
|
* package updates block, leaves a clear gap */
|
|
.section-body p:has(+ table) { margin-bottom: 10px; }
|
|
/* Rows of counts: every count carries the separator at its right, so a
|
|
* second row of counts lines up with the first */
|
|
.section .ui_stats { gap: 8px 16px; }
|
|
.section .ui_stats .ui_stat {
|
|
padding: 2px 16px 2px 0;
|
|
border-right: 1px solid var(--border-color-light);
|
|
}
|
|
.section .ui_stats .ui_stat:last-child { border-right: 0; }
|
|
.section .ui_stats .ui_stat_value {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
}
|
|
.section .ui_stats .ui_stat_label {
|
|
font-weight: 400;
|
|
color: var(--text-color-desc);
|
|
}
|
|
|
|
/* ---- Widgets ---- */
|
|
/* The ui-lib widgets keep square corners by default; this theme rounds
|
|
* them like its own cards. */
|
|
|
|
.ui_card, .ui_stat, .ui_empty, .ui_choice, .ui_radio_list,
|
|
.ui_select_switch_panel, .ui_code_block, .ui_multi_items {
|
|
border-radius: var(--radius);
|
|
}
|
|
.ui_card { box-shadow: var(--shadow); }
|
|
.ui_card_title { font-size: 15px; }
|
|
.ui_badge, .ui_chip, .ui_code, .ui_search_input {
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
.ui_progress_track, .ui_progress_bar { border-radius: 999px; }
|
|
|
|
/* ---- Popup windows ---- */
|
|
/* Choosers and help open in small windows split into frames, some only
|
|
* 50px tall, so their pages use tighter spacing and controls */
|
|
|
|
body#popup:not(.left-frame):not(.right-frame) {
|
|
--control-height: 28px;
|
|
padding: 8px;
|
|
font-size: 12.5px;
|
|
}
|
|
body#popup:not(.left-frame):not(.right-frame) input:not([type="checkbox"]):not([type="radio"]),
|
|
body#popup:not(.left-frame):not(.right-frame) select,
|
|
body#popup:not(.left-frame):not(.right-frame) button {
|
|
min-height: 28px;
|
|
padding: 2px 8px;
|
|
}
|
|
body#popup:not(.left-frame):not(.right-frame) table.wrapper {
|
|
margin: 0 0 6px;
|
|
}
|
|
/* Forms in popups have no box of their own: the chooser's bottom frame
|
|
* is 50px tall and holds only a button and a field */
|
|
body#popup:not(.left-frame):not(.right-frame) table.shrinkwrapper {
|
|
margin: 0;
|
|
border: 0;
|
|
background: transparent;
|
|
box-shadow: none;
|
|
}
|
|
body#popup:not(.left-frame):not(.right-frame) table.ui_columns > thead > tr > td {
|
|
padding: 4px 8px;
|
|
}
|
|
body#popup:not(.left-frame):not(.right-frame) table.ui_columns > tbody > tr > td {
|
|
height: auto;
|
|
padding: 3px 8px;
|
|
white-space: nowrap;
|
|
}
|
|
body#popup:not(.left-frame):not(.right-frame) tr.ui_table_body > td {
|
|
padding: 0;
|
|
}
|
|
body#popup:not(.left-frame):not(.right-frame) tr.ui_form_pair > td {
|
|
padding: 2px 3px;
|
|
}
|
|
body#popup:not(.left-frame):not(.right-frame) table.ui_form_end_buttons {
|
|
margin: 4px 0;
|
|
}
|
|
body#popup:not(.left-frame):not(.right-frame) hr { margin: 6px 0; }
|
|
body#popup:not(.left-frame):not(.right-frame) p { margin: 4px 0; }
|
|
|
|
/* ---- Login page ---- */
|
|
/* The login form is a narrow card in the middle of the page. The theme
|
|
* merges its class into the card table, as the core prints it as a
|
|
* second class attribute. A header without a title or links, as on this
|
|
* page, takes no room. */
|
|
|
|
table.shrinkwrapper.loginform {
|
|
max-width: 440px;
|
|
margin: 24px auto var(--card-gap);
|
|
}
|
|
/* Notices above the login form, the certificate warning and the login
|
|
* messages the script tags as failed, logout or timed out, take the
|
|
* card's width and sit a card gap above it. The messages are drawn by
|
|
* the error page rules below. The empty paragraph after a message is
|
|
* dropped. */
|
|
body:has(> center > form.ui_form[action*="_login.cgi"]) > center > :is(.ui_alert_box, h3.ui_login_message) {
|
|
max-width: 640px;
|
|
margin: var(--card-gap) auto calc(var(--card-gap) * 2) auto;
|
|
text-align: start;
|
|
}
|
|
body:has(> center > form.ui_form[action*="_login.cgi"]) > center > h3.ui_login_message + p:empty {
|
|
display: none;
|
|
}
|
|
body:has(> center > form.ui_form[action*="_login.cgi"]) > center > .ui_alert_box + h3.ui_login_message {
|
|
margin-top: var(--card-gap);
|
|
}
|
|
body:has(> center > form.ui_form[action*="_login.cgi"]) > center > :is(.ui_alert_box + form.ui_form, h3.ui_login_message + p + form.ui_form) > table.shrinkwrapper.loginform {
|
|
margin-top: var(--card-gap);
|
|
}
|
|
table.header:has(#headln2c > font:empty):not(:has(a, img, #headln1)) {
|
|
display: none;
|
|
}
|
|
/* The login button is the main action of the page, and the forgotten
|
|
* password form under the card is a plain link with room above it,
|
|
* not a filled button; its selectors outrank the primary button rule */
|
|
form.ui_form[action*="session_login.cgi"] > input.ui_submit {
|
|
background: var(--accent-color);
|
|
border-color: var(--accent-color-hover);
|
|
color: var(--text-color-inverse);
|
|
font-weight: 600;
|
|
}
|
|
form.ui_form[action*="session_login.cgi"] > input.ui_submit:hover {
|
|
background: var(--accent-color-hover);
|
|
}
|
|
form.ui_form[action*="forgot_form.cgi"] { margin-top: 14px; }
|
|
form.ui_form[action*="forgot_form.cgi"] table.ui_form_end_buttons td:first-child > input.ui_submit {
|
|
min-height: 0;
|
|
padding: 4px 8px;
|
|
border-color: transparent;
|
|
background: transparent;
|
|
color: var(--link-color);
|
|
font-weight: 500;
|
|
}
|
|
form.ui_form[action*="forgot_form.cgi"] table.ui_form_end_buttons td:first-child > input.ui_submit:hover {
|
|
border-color: transparent;
|
|
background: transparent;
|
|
color: var(--link-color-hover);
|
|
text-decoration: underline;
|
|
}
|
|
/* The password reset flow: the form asking for the username, the page
|
|
* confirming the email, and the new password form from the emailed
|
|
* link. Each prints its title in the page header and its content inside
|
|
* a center block. The header becomes the head row of a card like the
|
|
* login form's, with the card's edge and top corners, and the center
|
|
* block continues the card under it. The confirmation page is told
|
|
* apart by its content, text with two tt elements and no form. The
|
|
* empty paragraph the core prints after the header is dropped, and a
|
|
* warning above the form takes the card's width. */
|
|
:is(body:has(> center > form.ui_form[action*="forgot_send.cgi"]),
|
|
body:has(> center > form.ui_form[action*="forgot.cgi"]),
|
|
body:has(> table.header + p + center > tt:first-child)) > table.header {
|
|
position: static;
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
max-width: 440px;
|
|
margin: 24px auto 0;
|
|
padding: 9px 16px;
|
|
background: var(--main-content);
|
|
border: 1px solid var(--border-color);
|
|
border-bottom-color: var(--border-color-light);
|
|
border-radius: var(--radius) var(--radius) 0 0;
|
|
}
|
|
:is(body:has(> center > form.ui_form[action*="forgot_send.cgi"]),
|
|
body:has(> center > form.ui_form[action*="forgot.cgi"]),
|
|
body:has(> table.header + p + center > tt:first-child)) > table.header #headln2c {
|
|
padding: 0;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
letter-spacing: 0;
|
|
}
|
|
:is(body:has(> center > form.ui_form[action*="forgot_send.cgi"]),
|
|
body:has(> center > form.ui_form[action*="forgot.cgi"]),
|
|
body:has(> table.header + p + center > tt:first-child)) > table.header + p:empty {
|
|
display: none;
|
|
}
|
|
:is(body:has(> center > form.ui_form[action*="forgot_send.cgi"]),
|
|
body:has(> center > form.ui_form[action*="forgot.cgi"]),
|
|
body:has(> table.header + p + center > tt:first-child)) > .ui_alert_box {
|
|
max-width: 440px;
|
|
margin: 0 auto;
|
|
border-radius: 0;
|
|
}
|
|
:is(body:has(> center > form.ui_form[action*="forgot_send.cgi"]),
|
|
body:has(> center > form.ui_form[action*="forgot.cgi"]),
|
|
body:has(> table.header + p + center > tt:first-child)) > center {
|
|
box-sizing: border-box;
|
|
max-width: 440px;
|
|
margin: 0 auto var(--card-gap);
|
|
padding: 12px 16px 6px;
|
|
background: var(--main-content);
|
|
border: 1px solid var(--border-color);
|
|
border-top: 0;
|
|
border-radius: 0 0 var(--radius) var(--radius);
|
|
box-shadow: var(--shadow);
|
|
text-align: start;
|
|
}
|
|
/* The username form: the field on a line of its own under its label.
|
|
* The new password form: its own card table melts into the card. Both:
|
|
* the button centered under the fields with room above it, like the
|
|
* buttons under the login card. The confirmation text keeps the card's
|
|
* bottom padding. */
|
|
:is(body:has(> center > form.ui_form[action*="forgot_send.cgi"]),
|
|
body:has(> center > form.ui_form[action*="forgot.cgi"]),
|
|
body:has(> table.header + p + center > tt:first-child)) > center > form.ui_form > b {
|
|
display: block;
|
|
margin: 12px 0 4px;
|
|
font-weight: 400;
|
|
color: var(--text-color-desc);
|
|
}
|
|
:is(body:has(> center > form.ui_form[action*="forgot_send.cgi"]),
|
|
body:has(> center > form.ui_form[action*="forgot.cgi"]),
|
|
body:has(> table.header + p + center > tt:first-child)) > center > form.ui_form > input.ui_textbox {
|
|
width: 100%;
|
|
}
|
|
:is(body:has(> center > form.ui_form[action*="forgot_send.cgi"]),
|
|
body:has(> center > form.ui_form[action*="forgot.cgi"]),
|
|
body:has(> table.header + p + center > tt:first-child)) > center > form.ui_form > table.shrinkwrapper {
|
|
margin: 0;
|
|
border: 0;
|
|
background: transparent;
|
|
box-shadow: none;
|
|
}
|
|
:is(body:has(> center > form.ui_form[action*="forgot_send.cgi"]),
|
|
body:has(> center > form.ui_form[action*="forgot.cgi"]),
|
|
body:has(> table.header + p + center > tt:first-child)) > center > form.ui_form > table.shrinkwrapper tr.ui_table_body > td {
|
|
padding: 0;
|
|
}
|
|
:is(body:has(> center > form.ui_form[action*="forgot_send.cgi"]),
|
|
body:has(> center > form.ui_form[action*="forgot.cgi"]),
|
|
body:has(> table.header + p + center > tt:first-child)) > center > form.ui_form > table.ui_form_end_buttons {
|
|
margin: 10px 0 8px;
|
|
}
|
|
:is(body:has(> center > form.ui_form[action*="forgot_send.cgi"]),
|
|
body:has(> center > form.ui_form[action*="forgot.cgi"]),
|
|
body:has(> table.header + p + center > tt:first-child)) > center > form.ui_form > table.ui_form_end_buttons > tbody > tr {
|
|
justify-content: center;
|
|
}
|
|
body:has(> table.header + p + center > tt:first-child) > center {
|
|
padding-bottom: 14px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* ---- Error page ---- */
|
|
/* The core prints an error as a heading between rules, with an optional
|
|
* call stack table, and its own inline style after the theme's. The
|
|
* heading becomes a danger notice with the notice icon, the rules are
|
|
* dropped, and the stack becomes a card table under a small label. The
|
|
* selectors repeat the classes of the inline style, so they outrank it. */
|
|
|
|
hr:has(+ h3[data-fatal-error-text]), h3[data-fatal-error-text] + hr,
|
|
hr:has(+ table.err-stack), table.err-stack + hr { display: none; }
|
|
/* The notice sits close under the title; the inline style block the
|
|
* core prints right after the header marks the error page */
|
|
table.header:has(+ style[data-err]) { margin-bottom: 2px; }
|
|
h3[data-fatal-error-text], h3.err-head[data-fatal-error-text],
|
|
h3.ui_login_message {
|
|
display: flex;
|
|
box-sizing: border-box;
|
|
align-items: flex-start;
|
|
gap: 12px;
|
|
margin: 0 0 var(--card-gap);
|
|
padding: 10px 14px;
|
|
border: 1px solid var(--border-color-danger);
|
|
border-radius: var(--radius);
|
|
background: var(--bg-color-danger);
|
|
color: var(--text-color-danger-stressed);
|
|
font: 500 13px/1.45 var(--font-family);
|
|
text-transform: none;
|
|
transform: none;
|
|
white-space: normal;
|
|
overflow-wrap: break-word;
|
|
}
|
|
/* The login messages: a logout is a success notice and a timed out
|
|
* session a warning; a failed login keeps the danger look */
|
|
h3.ui_login_logout {
|
|
background: var(--bg-color-success);
|
|
border-color: var(--border-color-success);
|
|
color: var(--text-color-success-stressed);
|
|
}
|
|
h3.ui_login_timed_out {
|
|
background: var(--bg-color-warning);
|
|
border-color: var(--border-color-warning);
|
|
color: var(--text-color-warning-stressed);
|
|
}
|
|
/* The icon is one of the theme's notice icons, drawn as a mask in the
|
|
* notice color: x-circle, check-circle or the warning triangle */
|
|
h3[data-fatal-error-text]::before, h3.ui_login_message::before {
|
|
content: "";
|
|
flex: 0 0 auto;
|
|
width: 18px;
|
|
height: 18px;
|
|
margin-top: 1px;
|
|
background: var(--text-color-danger);
|
|
-webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M8 14.25A6.25 6.25 0 1 0 8 1.75a6.25 6.25 0 0 0 0 12.5z'/><path d='M6 6l4 4'/><path d='M10 6l-4 4'/></svg>") center / contain no-repeat;
|
|
mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M8 14.25A6.25 6.25 0 1 0 8 1.75a6.25 6.25 0 0 0 0 12.5z'/><path d='M6 6l4 4'/><path d='M10 6l-4 4'/></svg>") center / contain no-repeat;
|
|
}
|
|
h3.ui_login_logout::before {
|
|
background: var(--text-color-success);
|
|
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M8 14.25A6.25 6.25 0 1 0 8 1.75a6.25 6.25 0 0 0 0 12.5z'/><path d='M5.4 8.3l1.8 1.9 3.4-4'/></svg>");
|
|
mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M8 14.25A6.25 6.25 0 1 0 8 1.75a6.25 6.25 0 0 0 0 12.5z'/><path d='M5.4 8.3l1.8 1.9 3.4-4'/></svg>");
|
|
}
|
|
h3.ui_login_timed_out::before {
|
|
background: var(--text-color-warning);
|
|
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M8 2.6l6.4 10.4H1.6L8 2.6z'/><path d='M8 6.8v2.7'/><path d='M8 11.3h.01'/></svg>");
|
|
mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M8 2.6l6.4 10.4H1.6L8 2.6z'/><path d='M8 6.8v2.7'/><path d='M8 11.3h.01'/></svg>");
|
|
}
|
|
h3[data-fatal-error-text] pre {
|
|
margin: 0;
|
|
font-size: 12px;
|
|
white-space: pre-wrap;
|
|
}
|
|
table.err-stack, table.err-stack.captured {
|
|
width: 100%;
|
|
margin: 0 0 var(--card-gap);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--radius);
|
|
border-collapse: separate;
|
|
border-spacing: 0;
|
|
background: var(--main-content);
|
|
box-shadow: var(--shadow);
|
|
}
|
|
table.err-stack > caption.err-head, table.err-stack > caption.err-head.captured {
|
|
caption-side: top;
|
|
padding: 0 0 8px var(--title-inset);
|
|
color: var(--text-color-muted);
|
|
font: 600 12px/1.45 var(--font-family);
|
|
letter-spacing: 0.06em;
|
|
text-align: start;
|
|
text-transform: uppercase;
|
|
transform: none;
|
|
}
|
|
table.err-stack > tbody > tr > td, table.err-stack.captured > tbody > tr > td.captured {
|
|
padding: 4px 16px;
|
|
border-bottom: 1px solid var(--border-color-lighter);
|
|
color: var(--text-color);
|
|
font: 12.5px/1.45 var(--font-family-mono);
|
|
transform: none;
|
|
}
|
|
table.err-stack > tbody > tr:first-child > td,
|
|
table.err-stack.captured > tbody > tr:first-child > td.captured {
|
|
height: auto;
|
|
padding: 8px 16px;
|
|
border-bottom: 1px solid var(--border-color);
|
|
color: var(--text-color-muted);
|
|
font: 600 12px/1.45 var(--font-family);
|
|
text-transform: none;
|
|
vertical-align: middle;
|
|
}
|
|
table.err-stack > tbody > tr:first-child > td > b,
|
|
table.err-stack.captured > tbody > tr:first-child > td.captured > b {
|
|
border: 0;
|
|
color: inherit;
|
|
font-weight: inherit;
|
|
}
|
|
table.err-stack > tbody > tr:last-child > td { border-bottom: 0; }
|
|
|
|
/* ---- Module specific rules ---- */
|
|
|
|
/* The category selector above a module's configuration form */
|
|
form.ui_form[action="config.cgi"],
|
|
form.ui_form[action="uconfig.cgi"] {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 6px 8px;
|
|
margin: 0 0 8px;
|
|
}
|
|
/* The terminal fills the frame under the header, and the page does not
|
|
* scroll. The module sizes the terminal from the whole viewport with no
|
|
* room for the header, so the page scrolled, and the scrollbar widened
|
|
* the terminal past the frame. The 66px are the sticky header and its
|
|
* margin; the rules outrank the module's own by specificity. */
|
|
body.xterm { overflow: hidden; }
|
|
body.xterm #terminal {
|
|
box-sizing: border-box;
|
|
width: auto;
|
|
min-width: 0;
|
|
max-width: none;
|
|
height: calc(100vh - 66px - var(--card-gap));
|
|
min-height: 0;
|
|
max-height: none;
|
|
margin: 0;
|
|
padding: 6px;
|
|
border: 0;
|
|
border-radius: var(--radius);
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* Label and value rows some modules print as divs */
|
|
.ui_div_row { margin: 4px 0; }
|
|
.ui_div_row > span:first-child {
|
|
display: inline-block;
|
|
width: 100px;
|
|
min-width: 10%;
|
|
max-width: 15%;
|
|
padding: 4px 8px 0 0;
|
|
vertical-align: top;
|
|
text-align: right;
|
|
font-weight: 600;
|
|
}
|
|
.ui_div_row > span:last-child {
|
|
display: inline-block;
|
|
width: 80%;
|
|
max-width: 80%;
|
|
padding-top: 4px;
|
|
}
|
|
/* The module configuration page centers its table headings */
|
|
form[action="config_save.cgi"] #sortableTable2 > thead > tr > td {
|
|
text-align: center;
|
|
}
|
|
/* Search box and page selector at the right above paged lists, with the
|
|
* clear button inside the search field */
|
|
.ui_form_elements_wrapper_search {
|
|
float: right;
|
|
margin-bottom: 3px;
|
|
}
|
|
.ui_form_elements_wrapper_search * { line-height: initial !important; }
|
|
.ui_form_elements_wrapper_search [data-class="ui_textbox_pagination_search"] {
|
|
padding-right: 17px;
|
|
}
|
|
.ui_form_elements_wrapper_search [data-class="ui_textbox_pagination_search"] + .ui_reset,
|
|
.ui_form_elements_wrapper_search [data-class="ui_textbox_pagination_search"] + .ui_reset:active,
|
|
.ui_form_elements_wrapper_search [data-class="ui_textbox_pagination_search"] + .ui_reset:focus {
|
|
position: absolute;
|
|
right: 17px;
|
|
min-height: 17px;
|
|
background: transparent;
|
|
border-color: transparent;
|
|
box-shadow: none;
|
|
cursor: pointer;
|
|
outline: none;
|
|
}
|
|
.ui_form_elements_wrapper_search [data-class="ui_textbox_pagination_search"]:not([value=""]) + .ui_reset {
|
|
color: var(--text-color-danger);
|
|
}
|
|
.ui_form_elements_wrapper_paginator {
|
|
float: right;
|
|
margin-top: 3px;
|
|
}
|
|
.ui_form_elements_wrapper_paginator .ui_textbox {
|
|
padding-right: 2px;
|
|
border: 1px dashed var(--link-color);
|
|
text-align: center;
|
|
}
|
|
|
|
/* Mail composer (Quill) */
|
|
.ql-compose-container {
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--radius);
|
|
}
|
|
.ql-compose-container .ql-container.ql-snow { border: 0; }
|
|
.ql-compose-container .ql-toolbar.ql-snow {
|
|
border-top: 0;
|
|
border-right: 0;
|
|
border-left: 0;
|
|
border-color: var(--border-color-light);
|
|
}
|
|
.ql-compose-container .ql-editor {
|
|
min-height: 180px;
|
|
padding: 4px 6px 12px 6px;
|
|
}
|
|
.ql-snow .ql-editor pre { font-size: 92%; }
|
|
.ui_form_pair[data-contents="email"] > .ui_form_value,
|
|
.ql-compose-container .iframe_quote_details,
|
|
.ql-compose-container .ql-container.ql-snow .ql-editor {
|
|
background: var(--main-content);
|
|
color: var(--text-color);
|
|
}
|
|
.ui_form_pair > .ui_form_value > textarea[name='body'],
|
|
.ql-compose-container .ql-container.ql-snow .ql-editor {
|
|
font-size: 14px;
|
|
line-height: 1.231;
|
|
}
|
|
.ui_form_pair > .ui_form_value > textarea[name='body'] {
|
|
width: -webkit-fill-available !important;
|
|
min-height: 180px;
|
|
}
|
|
.ql-compose-container .ql-container.ql-snow .ql-editor * { line-height: 1.231; }
|
|
.ui_form_pair[data-contents="email"] > .ui_form_value { padding: 0; }
|
|
.ql-compose-container .ql-container.ql-snow .ql-editor blockquote {
|
|
margin: 0 0 0 6px;
|
|
padding-left: 12px;
|
|
border-left-width: 1px;
|
|
}
|
|
.ql-compose-container .ql-container.ql-snow .ql-editor pre.ql-syntax {
|
|
background: var(--bg-table-header);
|
|
color: initial;
|
|
}
|
|
.ql-compose-container .ql-snow .ql-picker.ql-expanded .ql-picker-label {
|
|
color: var(--text-color-muted);
|
|
}
|
|
.ql-compose-container .ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-fill {
|
|
fill: var(--text-color-muted);
|
|
}
|
|
.ql-compose-container .ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke {
|
|
stroke: var(--text-color-muted);
|
|
}
|
|
.ql-compose-container .ql-snow .ql-tooltip { z-index: 99; }
|
|
|
|
/* Command shell */
|
|
.shell pre {
|
|
overflow-x: auto;
|
|
overflow-y: hidden;
|
|
}
|
|
.shell input[name="cmd"] { max-width: 64.5vw; }
|
|
.shell select[name="pcmd"] { max-width: 65vw; }
|
|
|
|
/* Features not available in this edition */
|
|
[data-pro-disabled$="-link"] {
|
|
filter: grayscale(1) contrast(0.1);
|
|
opacity: 1;
|
|
}
|
|
.ui_form_pair:has([data-pro-disabled="cell"]) ~ .ui_form_pair:has(hr) ~ .ui_form_pair > .ui_form_label:not([data-pro-disabled="cell"]) > b > label > a,
|
|
.ui_form_pair:has([data-pro-disabled="cell"]) ~ .ui_form_pair:has(hr) ~ .ui_form_pair > .ui_form_value > .ui_multi_select .ui_multi_select_heads b,
|
|
[data-pro-disabled="cell"],
|
|
[data-pro-disabled$="-elem"] {
|
|
filter: grayscale(1);
|
|
opacity: 0.73;
|
|
}
|
|
.ui_form_pair:has([data-pro-disabled="cell"]) ~ .ui_form_pair:has(hr) ~ .ui_form_pair > .ui_form_value:not([data-pro-disabled="cell"]) > .ui_multi_select .ui_multi_select_heads b {
|
|
opacity: 0.61;
|
|
}
|
|
.ui_form_pair:has([data-pro-disabled="cell"]) ~ .ui_form_pair:has(hr) ~ .ui_form_pair > .ui_form_value > .ui_multi_select .ui_multi_select_row input[type="button"],
|
|
.ui_form_pair:has([data-pro-disabled="cell"]) ~ .ui_form_pair:has(hr) ~ .ui_form_pair > .ui_form_value > .ui_multi_select .ui_multi_select_row select {
|
|
color: rgba(118, 118, 118, 0.4);
|
|
pointer-events: none;
|
|
opacity: 0.8;
|
|
}
|
|
.ui_form_pair:has([data-pro-disabled="cell"]) ~ .ui_form_pair:has(hr) ~ .ui_form_pair > .ui_form_value > .ui_multi_select .ui_multi_select_row input[type="button"] {
|
|
opacity: 0.3;
|
|
}
|
|
.ui_form_pair:has([data-pro-disabled="cell"]) ~ .ui_form_pair:has(hr) ~ .ui_form_pair > .ui_form_value:only-child {
|
|
color: var(--text-color-muted);
|
|
filter: grayscale(1) contrast(0.9);
|
|
pointer-events: none;
|
|
}
|
|
.ui_form_pair:has([data-pro-disabled="cell"]) ~ .ui_form_pair:has(hr) ~ .ui_form_pair > .ui_form_value > .ui_multi_select .ui_multi_select_row select {
|
|
background: rgba(239, 239, 239, 0.3);
|
|
border-color: rgba(118, 118, 118, 0.3);
|
|
box-shadow: none;
|
|
}
|
|
[data-pro-disabled="cell"] + .ui_form_value,
|
|
.ui_form_pair:has([data-pro-disabled="cell"]) ~ .ui_form_pair:has(hr) ~ .ui_form_pair > .ui_form_value > .ui_multi_select .ui_multi_select_row > td,
|
|
.shrinkwrapper:has([data-pro-disabled="cell"]) ~ .ui_form_end_buttons {
|
|
cursor: not-allowed;
|
|
}
|
|
.shrinkwrapper:has([data-pro-disabled="cell"]) ~ .ui_form_end_buttons [name="create"] {
|
|
pointer-events: none;
|
|
opacity: 0.4;
|
|
}
|
|
[data-pro-disabled$="-elem"] input:not([type="radio"]):not([type="checkbox"]) {
|
|
filter: grayscale(1) contrast(0.9);
|
|
}
|
|
[data-pro-disabled$="-elem"] input { cursor: not-allowed !important; }
|
|
.menu-link.inactive, [data-pro-disabled$="-link"] a { cursor: alias; }
|
|
[data-pro-disabled$="-elem"] a { filter: grayscale(1) contrast(0.5); }
|
|
[data-pro-disabled$="-elem"] a:hover { filter: grayscale(1) contrast(1); }
|
|
|
|
/* ---- Print ---- */
|
|
|
|
@media print {
|
|
body { background: #fff; color: #000; }
|
|
body > .header,
|
|
body > .header ~ center,
|
|
.ui_form ~ a,
|
|
.ui_table a[href*=".cgi"],
|
|
.ui_form .ui_link,
|
|
.ui_form > center,
|
|
.ui_form > [name],
|
|
.ui_form > .wrapper,
|
|
.ui_form > .wrapper ~ .shrinkwrapper,
|
|
.ui_table > thead > tr > td:not(:first-child):last-child,
|
|
.ui_footer_links,
|
|
.page-links {
|
|
display: none;
|
|
}
|
|
.ui_table > thead > tr > td > b { font-size: 1.33em; }
|
|
.ui_table > thead > tr > td {
|
|
position: relative;
|
|
padding: 10px;
|
|
border-bottom: 2px solid #d9d9d9;
|
|
text-align: center;
|
|
}
|
|
body > .header ~ .ui_form[action*=".cgi"]:first-of-type { margin-top: -50px; }
|
|
pre { white-space: pre-wrap; }
|
|
a { word-break: break-word; }
|
|
a[href]:after { content: none; }
|
|
}
|