Files
webmin/gray-theme/unauthenticated/gray-theme.css
Ilia Ross 16148ed27e
Some checks failed
Tests / prove (push) Has been cancelled
Package and upload artifacts / build (push) Has been cancelled
Fix button height and box-sizing for dropdowns and update display property
2026-10-23 05:20:31 +02:00

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; }
}