First increment of the big WebUI feature-parity spec (see task list) — row-level actions were about to run out of icon-button space, so this adds a small anchored dropdown menu (app.js openContextMenu) for secondary per-container actions instead of cramming more buttons into every row. All four new actions verified live against the real podman API before wiring up the UI (same discipline as the CSRF/pull/compose bugs found earlier — field names and response shapes checked against the running socket, not assumed from docs). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
289 lines
18 KiB
CSS
289 lines
18 KiB
CSS
/**
|
|
* styles/podman.css
|
|
*
|
|
* Design tokens and component styles for the Podman plugin page. Ported
|
|
* directly from the approved mockup (webui/mockups/prototype.html) so the
|
|
* real implementation is visually identical to what was reviewed — the
|
|
* masthead/main-nav chrome from that mockup is NOT included here, since
|
|
* Podman.page renders inside Unraid's own real chrome, not a recreation
|
|
* of it.
|
|
*
|
|
* Token strategy: custom properties on :root, redefined under
|
|
* prefers-color-scheme (OS default) and under [data-theme] (explicit
|
|
* override, e.g. from Unraid's own theme setting) — component rules below
|
|
* only ever reference the tokens, never hardcode a color, so both themes
|
|
* stay in sync automatically. See docs/ARCHITECTURE.md section 18.
|
|
*/
|
|
|
|
.podman-plugin {
|
|
--bg: #eef0f2; --surface: #ffffff; --surface-2: #f4f5f7; --surface-3: #e9ebee;
|
|
--border: #dde1e6; --text: #1c2024; --text-dim: #5b6572; --text-faint: #8a94a1;
|
|
--accent: #d8541a; --accent-strong: #b8420f; --accent-contrast: #fff8f3;
|
|
--good: #1a8f4c; --good-bg: #e4f6ea; --warn: #9a6b00; --warn-bg: #fdf1d6;
|
|
--bad: #c22b3a; --bad-bg: #fbe6e8; --neutral: #5b6572; --neutral-bg: #e9ebee;
|
|
--shadow: 0 1px 2px rgba(20, 22, 26, .06), 0 4px 12px rgba(20, 22, 26, .05);
|
|
--font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
--font-mono: ui-monospace, "SF Mono", "Cascadia Code", "Roboto Mono", Consolas, "Liberation Mono", monospace;
|
|
font-family: var(--font-ui);
|
|
color: var(--text);
|
|
font-size: 14px;
|
|
line-height: 1.5;
|
|
}
|
|
@media (prefers-color-scheme: dark) {
|
|
.podman-plugin {
|
|
--bg: #15171b; --surface: #1c1f24; --surface-2: #23262c; --surface-3: #2b2f36;
|
|
--border: #34383f; --text: #e7e9ec; --text-dim: #9aa1ab; --text-faint: #6b7280;
|
|
--accent: #ef7f3f; --accent-strong: #f6975f; --accent-contrast: #1a1002;
|
|
--good: #4cc785; --good-bg: #123322; --warn: #e0b23d; --warn-bg: #3a2e0d;
|
|
--bad: #ef6470; --bad-bg: #3a1519; --neutral: #9aa1ab; --neutral-bg: #2b2f36;
|
|
--shadow: 0 1px 2px rgba(0,0,0,.3), 0 8px 24px rgba(0,0,0,.35);
|
|
}
|
|
}
|
|
:root[data-theme="dark"] .podman-plugin {
|
|
--bg: #15171b; --surface: #1c1f24; --surface-2: #23262c; --surface-3: #2b2f36;
|
|
--border: #34383f; --text: #e7e9ec; --text-dim: #9aa1ab; --text-faint: #6b7280;
|
|
--accent: #ef7f3f; --accent-strong: #f6975f; --accent-contrast: #1a1002;
|
|
--good: #4cc785; --good-bg: #123322; --warn: #e0b23d; --warn-bg: #3a2e0d;
|
|
--bad: #ef6470; --bad-bg: #3a1519; --neutral: #9aa1ab; --neutral-bg: #2b2f36;
|
|
--shadow: 0 1px 2px rgba(0,0,0,.3), 0 8px 24px rgba(0,0,0,.35);
|
|
}
|
|
:root[data-theme="light"] .podman-plugin {
|
|
--bg: #eef0f2; --surface: #ffffff; --surface-2: #f4f5f7; --surface-3: #e9ebee;
|
|
--border: #dde1e6; --text: #1c2024; --text-dim: #5b6572; --text-faint: #8a94a1;
|
|
--accent: #d8541a; --accent-strong: #b8420f; --accent-contrast: #fff8f3;
|
|
--good: #1a8f4c; --good-bg: #e4f6ea; --warn: #9a6b00; --warn-bg: #fdf1d6;
|
|
--bad: #c22b3a; --bad-bg: #fbe6e8; --neutral: #5b6572; --neutral-bg: #e9ebee;
|
|
--shadow: 0 1px 2px rgba(20,22,26,.06), 0 4px 12px rgba(20,22,26,.05);
|
|
}
|
|
|
|
.podman-plugin * { box-sizing: border-box; }
|
|
.podman-plugin h1, .podman-plugin h2, .podman-plugin h3 { text-wrap: balance; margin: 0; font-weight: 600; }
|
|
.podman-plugin .tnum { font-variant-numeric: tabular-nums; }
|
|
.podman-plugin .mono { font-family: var(--font-mono); }
|
|
.podman-plugin :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
|
|
|
|
.podman-pagehead {
|
|
display: flex; align-items: flex-end; justify-content: space-between;
|
|
gap: 16px; flex-wrap: wrap; padding: 4px 0 0;
|
|
}
|
|
.podman-pagehead .titlewrap { display: flex; align-items: center; gap: 12px; }
|
|
.podman-pagehead .icon {
|
|
width: 38px; height: 38px; border-radius: 9px; background: var(--surface-3);
|
|
display: grid; place-items: center; color: var(--accent); border: 1px solid var(--border);
|
|
}
|
|
.podman-pagehead h1 { font-size: 21px; }
|
|
.podman-pagehead .meta { font-size: 12.5px; color: var(--text-dim); margin-top: 2px; }
|
|
.podman-pagehead .meta .dot-good { color: var(--good); }
|
|
.podman-pagehead .meta .dot-bad { color: var(--bad); }
|
|
|
|
.podman-btn {
|
|
appearance: none; border: 1px solid var(--border); background: var(--surface); color: var(--text);
|
|
padding: 8px 14px; border-radius: 7px; font-size: 13px; font-weight: 600; cursor: pointer;
|
|
display: inline-flex; align-items: center; gap: 6px; transition: border-color .12s, background .12s;
|
|
font-family: var(--font-ui);
|
|
}
|
|
.podman-btn:hover { border-color: var(--text-faint); }
|
|
.podman-btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-contrast); }
|
|
.podman-btn-primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
|
|
.podman-btn-danger { color: var(--bad); }
|
|
.podman-btn-danger:hover { border-color: var(--bad); }
|
|
.podman-btn-icon { padding: 6px 8px; }
|
|
.podman-btn[disabled] { opacity: .4; cursor: not-allowed; }
|
|
|
|
.podman-subnav {
|
|
margin: 14px 0 0; padding: 0; display: flex; gap: 4px; border-bottom: 1px solid var(--border);
|
|
overflow-x: auto; list-style: none;
|
|
}
|
|
.podman-subnav button {
|
|
appearance: none; background: none; border: none; border-bottom: 2px solid transparent;
|
|
color: var(--text-dim); padding: 10px 12px; font-size: 13px; font-weight: 600; cursor: pointer;
|
|
white-space: nowrap; display: flex; align-items: center; gap: 6px; font-family: var(--font-ui);
|
|
}
|
|
.podman-subnav button:hover { color: var(--text); }
|
|
.podman-subnav button.active { color: var(--accent-strong); border-bottom-color: var(--accent); }
|
|
.podman-subnav button .n { font-size: 10.5px; padding: 1px 5px; border-radius: 8px; background: var(--surface-3); color: var(--text-dim); }
|
|
|
|
.podman-main { padding: 20px 0 48px; }
|
|
.podman-panel { display: none; }
|
|
.podman-panel.active { display: block; }
|
|
|
|
.podman-card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow); }
|
|
.podman-card-pad { padding: 16px 18px; }
|
|
.podman-card-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid var(--border); }
|
|
.podman-card-head h2 { font-size: 14.5px; }
|
|
.podman-card-head .sub { font-size: 12px; color: var(--text-faint); font-weight: 500; }
|
|
|
|
.podman-grid { display: grid; gap: 14px; }
|
|
.podman-stat-grid { grid-template-columns: repeat(6, 1fr); }
|
|
@media (max-width: 1080px) { .podman-stat-grid { grid-template-columns: repeat(3, 1fr); } }
|
|
@media (max-width: 620px) { .podman-stat-grid { grid-template-columns: repeat(2, 1fr); } }
|
|
|
|
.podman-stat { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 14px 16px; box-shadow: var(--shadow); }
|
|
.podman-stat .label { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); font-weight: 700; }
|
|
.podman-stat .value { font-size: 24px; font-weight: 700; margin-top: 6px; }
|
|
.podman-stat .value small { font-size: 13px; color: var(--text-dim); font-weight: 600; }
|
|
.podman-stat .bar { height: 5px; border-radius: 3px; background: var(--surface-3); margin-top: 10px; overflow: hidden; }
|
|
.podman-stat .bar > span { display: block; height: 100%; background: var(--accent); border-radius: 3px; }
|
|
|
|
.podman-chip {
|
|
display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 100px;
|
|
font-size: 11.5px; font-weight: 700; letter-spacing: .01em; white-space: nowrap;
|
|
}
|
|
.podman-chip .d { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
|
|
.podman-chip-good { background: var(--good-bg); color: var(--good); }
|
|
.podman-chip-warn { background: var(--warn-bg); color: var(--warn); }
|
|
.podman-chip-bad { background: var(--bad-bg); color: var(--bad); }
|
|
.podman-chip-neutral { background: var(--neutral-bg); color: var(--neutral); }
|
|
|
|
.podman-plugin table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
.podman-plugin thead th {
|
|
text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
|
|
color: var(--text-faint); font-weight: 700; padding: 10px 18px; border-bottom: 1px solid var(--border);
|
|
}
|
|
.podman-plugin tbody td { padding: 11px 18px; border-bottom: 1px solid var(--border); vertical-align: middle; }
|
|
.podman-plugin tbody tr:last-child td { border-bottom: none; }
|
|
.podman-plugin tbody tr:hover { background: var(--surface-2); }
|
|
.podman-table-wrap { overflow-x: auto; }
|
|
.podman-row-name { display: flex; align-items: center; gap: 10px; font-weight: 600; }
|
|
.podman-row-name .ico {
|
|
width: 26px; height: 26px; border-radius: 6px; flex: none; background: var(--surface-3);
|
|
display: grid; place-items: center; font-size: 12px; border: 1px solid var(--border); color: var(--text-dim);
|
|
}
|
|
.podman-row-sub { font-size: 11.5px; color: var(--text-faint); font-weight: 500; margin-top: 1px; }
|
|
.podman-actions { display: flex; gap: 4px; justify-content: flex-end; }
|
|
|
|
.podman-usage-mini { display: flex; align-items: center; gap: 8px; min-width: 110px; }
|
|
.podman-usage-mini .track { flex: 1; height: 5px; border-radius: 3px; background: var(--surface-3); overflow: hidden; }
|
|
.podman-usage-mini .track > span { display: block; height: 100%; background: var(--accent); }
|
|
.podman-usage-mini .num { font-size: 11.5px; color: var(--text-dim); width: 34px; text-align: right; }
|
|
|
|
.podman-toolbar { display: flex; align-items: center; gap: 10px; padding: 14px 18px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
|
|
.podman-search { flex: 1; min-width: 180px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 7px 11px; font-size: 13px; color: var(--text); font-family: var(--font-ui); }
|
|
.podman-search::placeholder { color: var(--text-faint); }
|
|
|
|
.podman-two-col { display: grid; grid-template-columns: 1.3fr 1fr; gap: 14px; align-items: start; }
|
|
@media (max-width: 920px) { .podman-two-col { grid-template-columns: 1fr; } }
|
|
|
|
.podman-activity-list { list-style: none; margin: 0; padding: 6px 0; }
|
|
.podman-activity-list li { display: flex; gap: 10px; padding: 9px 18px; border-bottom: 1px solid var(--border); font-size: 12.5px; }
|
|
.podman-activity-list li:last-child { border-bottom: none; }
|
|
.podman-activity-list .dot { width: 7px; height: 7px; border-radius: 50%; margin-top: 5px; flex: none; }
|
|
.podman-activity-list .when { color: var(--text-faint); font-size: 11px; margin-top: 1px; }
|
|
|
|
.podman-empty-note { padding: 28px 18px; text-align: center; color: var(--text-faint); font-size: 13px; }
|
|
|
|
.podman-pod-card { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; margin-bottom: 14px; background: var(--surface); box-shadow: var(--shadow); }
|
|
.podman-pod-head { display: flex; align-items: center; gap: 10px; padding: 13px 16px; background: var(--surface-2); border-bottom: 1px solid var(--border); }
|
|
.podman-pod-head .name { font-weight: 700; font-size: 13.5px; }
|
|
.podman-pod-head .infra { font-size: 11.5px; color: var(--text-faint); }
|
|
|
|
.podman-logs-layout { display: grid; grid-template-columns: 200px 1fr; min-height: 460px; }
|
|
@media (max-width: 760px) { .podman-logs-layout { grid-template-columns: 1fr; } }
|
|
.podman-logs-side { border-right: 1px solid var(--border); }
|
|
.podman-logs-side .item { padding: 10px 14px; font-size: 12.5px; border-bottom: 1px solid var(--border); cursor: pointer; display: flex; align-items: center; gap: 8px; }
|
|
.podman-logs-side .item.active { background: var(--surface-2); font-weight: 700; box-shadow: inset 2px 0 0 var(--accent); }
|
|
.podman-log-pane {
|
|
background: #0f1114; color: #c7ccd4; font-family: var(--font-mono); font-size: 12.3px;
|
|
padding: 14px 16px; height: 400px; overflow-y: auto; line-height: 1.65;
|
|
}
|
|
.podman-log-pane .l { white-space: pre-wrap; word-break: break-word; }
|
|
.podman-log-pane .ts { color: #6b7280; }
|
|
.podman-log-pane .lvl-warn { color: #e0b23d; }
|
|
.podman-log-pane .lvl-error { color: #ef6470; }
|
|
|
|
.podman-term { background: #0f1114; color: #d7dbe0; font-family: var(--font-mono); font-size: 12.6px; border-radius: 8px; padding: 14px 16px; height: 380px; overflow-y: auto; line-height: 1.7; }
|
|
.podman-term .prompt { color: #4cc785; }
|
|
.podman-term .path { color: #6fb2f5; }
|
|
.podman-term-input {
|
|
width: 100%; margin-top: 10px; background: #0f1114; color: #d7dbe0; border: 1px solid var(--border);
|
|
border-radius: 6px; padding: 8px 10px; font-family: var(--font-mono); font-size: 12.6px;
|
|
}
|
|
|
|
.podman-compose-layout { display: grid; grid-template-columns: 230px 1fr; min-height: 480px; }
|
|
@media (max-width: 800px) { .podman-compose-layout { grid-template-columns: 1fr; } }
|
|
.podman-compose-side { border-right: 1px solid var(--border); }
|
|
.podman-compose-proj { padding: 12px 14px; border-bottom: 1px solid var(--border); cursor: pointer; }
|
|
.podman-compose-proj.active { background: var(--surface-2); box-shadow: inset 2px 0 0 var(--accent); }
|
|
.podman-compose-proj .path { font-size: 11px; color: var(--text-faint); margin-top: 2px; font-family: var(--font-mono); }
|
|
.podman-yaml { background: #0f1114; color: #c7ccd4; font-family: var(--font-mono); font-size: 12.4px; padding: 16px 18px; height: 420px; overflow: auto; line-height: 1.7; white-space: pre-wrap; }
|
|
|
|
.podman-field-row { display: grid; grid-template-columns: 220px 1fr; gap: 16px; padding: 14px 18px; border-bottom: 1px solid var(--border); align-items: start; }
|
|
.podman-field-row:last-child { border-bottom: none; }
|
|
.podman-field-row label { font-weight: 600; font-size: 13px; }
|
|
.podman-field-row .hint { font-size: 11.5px; color: var(--text-faint); margin-top: 4px; max-width: 46ch; }
|
|
.podman-field-row input[type="text"], .podman-field-row input[type="number"], .podman-field-row select {
|
|
background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 8px 10px;
|
|
font-size: 13px; color: var(--text); width: 100%; max-width: 340px; font-family: var(--font-ui);
|
|
}
|
|
.podman-danger-card { border-color: color-mix(in srgb, var(--bad) 40%, var(--border)); }
|
|
.podman-danger-card .podman-card-head { border-bottom-color: color-mix(in srgb, var(--bad) 30%, var(--border)); }
|
|
.podman-danger-card .podman-card-head h2 { color: var(--bad); }
|
|
|
|
.podman-badge-update { font-size: 10px; font-weight: 700; color: var(--accent-strong); background: color-mix(in srgb, var(--accent) 16%, transparent); padding: 2px 7px; border-radius: 100px; margin-left: 8px; }
|
|
|
|
.podman-loading, .podman-error { padding: 32px 18px; text-align: center; color: var(--text-faint); font-size: 13px; }
|
|
|
|
/**
|
|
* Modal form dialog — replaces browser-native prompt()/confirm() for any
|
|
* action that needs more than a single yes/no (e.g. "New Volume" needs a
|
|
* name AND an optional host path together, which prompt() can't express
|
|
* as one coherent form). See app.js's openFormModal().
|
|
*/
|
|
.podman-modal-backdrop {
|
|
position: fixed; inset: 0; background: rgba(15, 17, 20, .55); z-index: 1000;
|
|
display: flex; align-items: center; justify-content: center; padding: 20px;
|
|
}
|
|
.podman-modal {
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
|
|
box-shadow: var(--shadow); width: 100%; max-width: 420px; max-height: calc(100vh - 40px);
|
|
overflow-y: auto; color: var(--text); font-family: var(--font-ui);
|
|
}
|
|
.podman-modal-head { padding: 16px 20px; border-bottom: 1px solid var(--border); }
|
|
.podman-modal-head h3 { font-size: 15px; }
|
|
.podman-modal-body { padding: 16px 20px; display: grid; gap: 14px; }
|
|
.podman-modal-field label { display: block; font-weight: 600; font-size: 12.5px; margin-bottom: 6px; }
|
|
.podman-modal-field input[type="text"] {
|
|
background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 8px 10px;
|
|
font-size: 13px; color: var(--text); width: 100%; font-family: var(--font-ui);
|
|
}
|
|
.podman-modal-field .hint { font-size: 11.5px; color: var(--text-faint); margin-top: 4px; }
|
|
.podman-modal-field select {
|
|
background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 8px 10px;
|
|
font-size: 13px; color: var(--text); font-family: var(--font-ui);
|
|
}
|
|
.podman-modal-checkbox label { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 12.5px; margin-bottom: 0; }
|
|
.podman-modal-error { font-size: 12.5px; color: var(--bad); background: var(--bad-bg); border-radius: 7px; padding: 8px 10px; }
|
|
.podman-modal-actions { padding: 14px 20px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 8px; }
|
|
.podman-error { color: var(--bad); }
|
|
|
|
/* Wider variant + repeatable row groups, for forms with more than 1-2 fields (e.g. Create Container). */
|
|
.podman-modal-wide { max-width: 640px; }
|
|
.podman-row-group { display: grid; gap: 8px; margin-bottom: 8px; }
|
|
.podman-row-group-item {
|
|
display: flex; align-items: center; gap: 8px;
|
|
}
|
|
.podman-row-group-item input[type="text"] {
|
|
background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 7px 9px;
|
|
font-size: 12.5px; color: var(--text); font-family: var(--font-mono); flex: 1; min-width: 0;
|
|
}
|
|
.podman-row-group-item select {
|
|
background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 7px 9px;
|
|
font-size: 12.5px; color: var(--text); font-family: var(--font-ui); flex: none;
|
|
}
|
|
.podman-row-group-item span { color: var(--text-faint); font-size: 12px; flex: none; }
|
|
|
|
/* Anchored dropdown context menu — see app.js openContextMenu(). */
|
|
.podman-context-menu {
|
|
position: absolute; width: 180px; background: var(--surface); border: 1px solid var(--border);
|
|
border-radius: 9px; box-shadow: var(--shadow); z-index: 1001; padding: 4px; display: grid; gap: 1px;
|
|
}
|
|
.podman-context-menu button {
|
|
appearance: none; border: none; background: none; text-align: left; padding: 8px 10px;
|
|
font-size: 12.5px; font-weight: 600; color: var(--text); border-radius: 6px; cursor: pointer;
|
|
font-family: var(--font-ui); width: 100%;
|
|
}
|
|
.podman-context-menu button:hover { background: var(--surface-2); }
|
|
.podman-context-menu button.danger { color: var(--bad); }
|
|
.podman-context-menu button[disabled] { opacity: .4; cursor: not-allowed; }
|
|
.podman-context-menu-sep { height: 1px; background: var(--border); margin: 4px 2px; }
|