Files
unraid-podman/webui/plugins/podman/styles/podman.css
T
maggesandClaude Sonnet 5 46a8503498
Build Packages / Build .txz packages (push) Failing after 8m53s
Lint / ShellCheck (push) Successful in 43s
Lint / Validate .plg XML (push) Successful in 11s
Lint / EditorConfig (push) Failing after 6s
Rework Terminal into a real live console; polish danger buttons and Settings
Terminal panel now opens a genuinely interactive shell (ttyd bound to a
unix socket, proxied through Unraid's own /logterminal/ nginx location —
the same mechanism Unraid's own Docker "Console" button uses) instead of
one-shot exec calls, shown inline with a Disconnect action; bash is the
default shell. Container/shell selectors and action buttons are now
correctly bottom-aligned (root cause: Unraid's theme puts a 10px margin
on every <button>, never reset before).

Destructive actions (Disconnect, Compose/Template Delete, Volumes/Images/
Networks Remove) get a consistent, solid red treatment at rest instead of
only tinting on hover, via new --bad-strong/--bad-contrast tokens.

Settings panel restructured: a real save toolbar instead of a button
buried in an empty-label row, card subtitles, a toggle switch instead of
a bare checkbox, and installed-package versions shown as chips.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 21:19:47 +00:00

575 lines
34 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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; --bad-strong: #9c1f2c; --bad-contrast: #fff5f6; --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; --bad-strong: #f6838c; --bad-contrast: #2a0a0d; --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; --bad-strong: #f6838c; --bad-contrast: #2a0a0d; --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; --bad-strong: #9c1f2c; --bad-contrast: #fff5f6; --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); }
/*
* margin: 0 — Unraid's own webGui theme applies a 10px top/bottom margin
* to plain <button> elements site-wide. Without resetting it, every
* .podman-btn carries an invisible 10px gap above and below its own box,
* which silently breaks flex cross-axis alignment anywhere a button sits
* next to a non-button sibling (e.g. align-items: flex-end next to a
* <select> — verified live: the button's margin, not its content, was
* what left it floating 10px above the dropdown it should line up with).
*/
.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); margin: 0;
}
.podman-btn:hover { border-color: var(--text-faint); }
/*
* !important here for the same reason as .podman-btn-ghost below: Unraid's
* own webGUI theme applies a default border/hover treatment to every
* <button> that otherwise silently wins over this rule at rest — verified
* live via a screen recording: without !important, "Create" only ever
* looked filled while under the mouse (Unraid's generic hover glow, applied
* to literally any button), never at rest, making it indistinguishable
* from Cancel except by coincidence of cursor position.
*/
.podman-btn-primary {
background: var(--accent) !important; border-color: var(--accent) !important; color: var(--accent-contrast) !important;
}
.podman-btn-primary:hover {
background: var(--accent-strong) !important; border-color: var(--accent-strong) !important;
}
.podman-btn-danger { color: var(--bad); }
.podman-btn-danger:hover { border-color: var(--bad); }
.podman-btn-icon { padding: 6px 8px; min-width: 32px; min-height: 32px; justify-content: center; font-size: 15px; line-height: 1; }
.podman-btn[disabled] { opacity: .4; cursor: not-allowed; }
/*
* Secondary action (Cancel, "+ Add row") — every button previously shared
* the same bordered/accent-colored treatment as Create, so nothing in a
* form stood out as THE primary action (found from a live screenshot AND
* a screen recording: Cancel/Create/+Add/× all read as equally weighted,
* with the exact same hover glow even). Unraid's own webGUI applies a
* site-wide default border+hover-gradient to every <button>, at higher
* effective priority than a plain single-class selector here — verified
* live: a bare `.podman-btn-ghost { border-color: transparent }` was
* silently losing to it, on both the rest AND hover state. !important is
* the only reliable way to guarantee this specific, deliberate style
* wins regardless of what Unraid's base theme does elsewhere.
*/
.podman-btn-ghost {
background: transparent !important; border-color: transparent !important;
color: var(--text-dim) !important; box-shadow: none !important;
}
.podman-btn-ghost:hover {
background: var(--surface-2) !important; border-color: transparent !important;
color: var(--text) !important; box-shadow: none !important;
}
/*
* A ghost button can still carry danger intent (Disconnect, Delete,
* template "Delete") — needs its own !important since .podman-btn-ghost's
* color/background/border would otherwise win by rule order. Solid fill
* at rest (not just a tint, and not just on hover) so it reads with the
* same weight as .podman-btn-primary, just in red instead of accent —
* a merely tinted/outlined button still read as "just another secondary
* action" per live feedback.
*/
.podman-btn-ghost.podman-btn-danger {
color: var(--bad-contrast) !important; background: var(--bad) !important; border-color: var(--bad) !important;
}
.podman-btn-ghost.podman-btn-danger:hover {
background: var(--bad-strong) !important; border-color: var(--bad-strong) !important; color: var(--bad-contrast) !important;
}
.podman-btn-ghost.podman-btn-danger[disabled] {
color: var(--text-faint) !important; background: transparent !important; border-color: var(--border) !important;
}
/* Icon-only danger buttons (row "remove" trash icons) carry an emoji
glyph, not text — .podman-btn-danger's `color` alone doesn't recolor an
emoji, so these get the same solid red fill instead, at rest not just
on hover, so "destructive" reads at a glance across a whole table. */
.podman-btn-icon.podman-btn-danger { border-color: var(--bad) !important; background: var(--bad) !important; }
.podman-btn-icon.podman-btn-danger:hover { background: var(--bad-strong) !important; border-color: var(--bad-strong) !important; }
.podman-btn-icon.podman-btn-danger[disabled] { border-color: var(--border) !important; background: transparent !important; }
.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-btn {
/* !important for the same reason as .podman-btn-ghost/-primary — Unraid's
own site-wide button theme otherwise still shows its default border
at rest (only losing to plain rules on hover), so a name link one
click away from every table row still looked like a bordered button
forever, not a plain label. */
appearance: none; border: none !important; background: none !important; padding: 0; cursor: pointer;
color: var(--text); font-family: var(--font-ui); font-size: 13px; text-align: left;
}
.podman-row-name-btn:hover { color: var(--accent-strong); }
.podman-row-name-btn:hover .ico { border-color: var(--accent); }
.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; }
/*
* The actions <td> itself stays a plain table-cell (default display) so
* every row's column width is computed the same way by the table's layout
* algorithm — putting "display: flex" directly on the <td> used to take it
* out of that algorithm, so browsers could size/position it slightly
* differently row to row (found live: the trash-can button in Images drifted
* a few pixels between rows instead of lining up in one column). The actual
* flex/gap/alignment lives on this inner wrapper instead.
*/
.podman-actions { text-align: right; white-space: nowrap; }
.podman-actions-row { display: inline-flex; gap: 4px; justify-content: flex-end; }
/*
* Segmented toggle (Containers' All/Running/Stopped filter, Logs' Follow/
* Paused) — previously just an inline-styled wrapper <div> around plain
* <button>s with no CSS of their own at all, so every option (not just the
* active one) showed Unraid's own default button border permanently,
* all three chips looking identically "selected". !important for the same
* site-wide-theme-override reason as .podman-btn-ghost/-primary.
*/
.podman-segmented { display: flex; gap: 2px; background: var(--surface-3); border: 1px solid var(--text-faint); padding: 3px; border-radius: 8px; }
.podman-segmented button {
appearance: none; border: none !important; background: transparent !important; color: var(--text-dim) !important;
padding: 6px 12px; border-radius: 6px; font-size: 12px; font-weight: 700; cursor: pointer;
font-family: var(--font-ui); transition: background .12s, color .12s;
}
.podman-segmented button:hover { color: var(--text) !important; }
/* Filled with the accent color (not just a slightly different neutral
shade) — the previous var(--surface) vs. var(--surface-2) contrast
between active/inactive was too close in the dark theme to notice at a
glance (found live). */
.podman-segmented button.active { background: var(--accent) !important; color: var(--accent-contrast) !important; box-shadow: var(--shadow); }
.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; }
/*
* !important throughout: Unraid's own webGui/styles/default-base.css
* targets input[type="text"] with an attribute selector (higher
* specificity than our single .podman-search class, :where() around it
* notwithstanding) forcing border-width:0 / border-bottom-width:1px /
* background:transparent — an underline-only text field, not a boxed one.
* Found live: our border/background were being silently dropped even
* though this rule appears later in the stylesheet.
*/
.podman-search {
flex: 1; min-width: 180px; max-width: 320px; font-size: 13px; color: var(--text); font-family: var(--font-ui);
background: var(--surface-3) !important; border: 1px solid var(--text-faint) !important;
border-radius: 7px !important; padding: 7px 11px !important;
}
.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); margin-right: auto; }
.podman-badge { display: inline-block; font-size: 10.5px; font-weight: 700; color: var(--text-dim); background: var(--surface-3); padding: 2px 8px; border-radius: 100px; margin-top: 6px; }
.podman-template-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 14px; padding: 18px; }
.podman-template-grid .podman-empty-note { grid-column: 1 / -1; }
.podman-template-card {
border: 1px solid var(--border); border-radius: 10px; padding: 14px; background: var(--surface);
display: flex; flex-direction: column; gap: 10px;
}
.podman-template-icon { width: 40px; height: 40px; border-radius: 8px; object-fit: cover; background: var(--surface-2); }
.podman-template-icon-fallback {
display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 16px;
color: var(--accent); border: 1px solid var(--border);
}
.podman-template-name { font-weight: 700; font-size: 13.5px; }
.podman-template-overview { font-size: 12px; color: var(--text-dim); line-height: 1.4; }
.podman-template-actions { display: flex; gap: 6px; margin-top: auto; padding-top: 4px; }
/*
* min-width: 0 overrides the flex-item default of min-width: auto, which
* otherwise refuses to shrink a button below its own label's intrinsic
* width — without it, "Delete" (the widest label, and uppercased by
* Unraid's own site-wide button theme) pushed past the card's right edge
* instead of actually sharing the row evenly with Use/Export (found live).
*/
.podman-template-actions .podman-btn {
flex: 1; min-width: 0; justify-content: center; padding: 6px 8px; font-size: 11.5px;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.podman-local-template-list { max-height: 220px; overflow-y: auto; border: 1px solid var(--border); border-radius: 7px; margin-top: 8px; }
.podman-local-template-item {
display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-bottom: 1px solid var(--border);
}
.podman-local-template-item:last-child { border-bottom: none; }
.podman-local-template-name { font-weight: 600; font-size: 12.5px; white-space: nowrap; }
.podman-local-template-item .podman-row-sub { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.podman-local-template-item .podman-btn { flex: none; padding: 5px 10px; font-size: 11.5px; }
.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-launcher {
display: flex; align-items: flex-end; gap: 16px; flex-wrap: wrap; margin-bottom: 14px;
padding: 12px 14px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
}
.podman-term-launcher label { display: flex; flex-direction: column; gap: 4px; font-size: 12.5px; font-weight: 600; color: var(--text-dim); }
/*
* !important here for the same reason as .podman-search's: Unraid's own
* webGui/styles/default-base.css has `select:where(:not(.unapi *))` rules
* for background/border/padding that would otherwise still show through
* around this class's box-model properties.
*/
.podman-term-select {
min-width: 180px !important; padding: 7px 12px !important; font-size: 13px !important;
font-family: var(--font-mono) !important; color: var(--text) !important;
background: var(--surface-3) !important; border: 1px solid var(--accent) !important; border-radius: 6px !important;
}
.podman-term-frame { display: block; width: 100%; height: 480px; border: 1px solid var(--border); border-radius: 8px; background: #0f1114; }
.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; }
/*
* !important: this is now a real <textarea>, not a read-only <pre> —
* Unraid's own webGui/styles/default-base.css targets textarea the same
* way it targets input[type="text"] (see .podman-search's comment for
* the exact rule), forcing border-width:0/border-bottom-width:1px/
* background:transparent/border-radius:0, which would otherwise make the
* whole editor look like a barely-visible underline instead of an actual
* text area.
*/
.podman-yaml-editor {
display: block; width: 100%; box-sizing: border-box; resize: vertical;
border: none !important; border-radius: 0 !important; outline: none;
}
.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)); }
/* Settings panel: a shared save action above all cards (Storage's and
Autostart & Lifecycle's fields save together in one call — see
settings.js's save() — so one button belongs above both, not buried in
either card, and definitely not in its own row with an empty label).
Framed as its own small bar (background/border), not bare text+button
floating at the top of the page. */
.podman-settings-actions {
display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 14px;
padding: 12px 16px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px;
}
.podman-settings-actions .hint { margin: 0; max-width: 52ch; color: var(--text-dim); }
.podman-card-head .sub { margin-top: 3px; }
/* Number field + unit label (GB, seconds) — the input itself stays compact
instead of stretching to .podman-field-row's normal 340px text-field width. */
.podman-input-suffix { display: flex; align-items: center; gap: 8px; }
.podman-input-suffix input[type="number"] { max-width: 100px; width: auto; }
.podman-input-suffix span { font-size: 12px; color: var(--text-dim); }
/*
* Toggle switch — a plain checkbox reads as a leftover form control next
* to everything else in this panel getting a designed treatment; this
* hides the native checkbox (still the real, accessible input driving
* state) and draws a track+thumb off its :checked state instead. Sized in
* em off the track's own font-size so it scales if that ever changes.
*/
.podman-switch { position: relative; display: inline-flex; align-items: center; cursor: pointer; font-size: 22px; }
.podman-switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.podman-switch-track {
display: inline-block; width: 1.9em; height: 1.05em; border-radius: 999px;
background: var(--surface-3); border: 1px solid var(--border); transition: background .15s, border-color .15s;
}
.podman-switch-thumb {
display: block; width: 0.75em; height: 0.75em; margin: 0.13em; border-radius: 50%;
background: var(--text-faint); transition: transform .15s, background .15s;
}
.podman-switch input:checked + .podman-switch-track { background: var(--accent); border-color: var(--accent); }
.podman-switch input:checked + .podman-switch-track .podman-switch-thumb { background: var(--accent-contrast); transform: translateX(0.85em); }
.podman-switch input:focus-visible + .podman-switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }
.podman-version-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.podman-version-chip {
font-size: 11.5px; font-family: var(--font-mono); background: var(--surface-3); color: var(--text-dim);
border: 1px solid var(--border); padding: 5px 11px; border-radius: 100px;
}
.podman-version-chip b { color: var(--text); font-weight: 600; margin-left: 5px; }
.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 alone wasn't enough — its auto flex-basis still let the
select stretch to fill the row (verified live: "TCP"/"Volume"
dropdowns spanned almost the entire row width). An explicit width
pins it to content-appropriate size regardless. */
flex: none; width: 110px;
}
.podman-row-group-item span { color: var(--text-faint); font-size: 12px; flex: none; }
/*
* Port number fields share the row with a select + remove button, unlike
* the wide source/path/key/value fields elsewhere in these row groups —
* left on flex:1 like everything else, both port inputs fought the fixed-
* width select/button for space and got squeezed down to a few pixels
* (found live: they rendered as near-invisible slivers). Fixed width,
* not flex-grown.
*/
.podman-row-group-item input.podman-input-narrow { flex: none; width: 90px; }
/* Row-remove (x) reads as a normal button like everything else at full
.podman-btn weight — muted/borderless by default, only turning
"danger" red on hover, so it registers as a quiet per-row affordance
rather than competing with the form's actual actions. */
.podman-row-group-item .podman-row-remove-btn {
background: transparent; border-color: transparent; color: var(--text-faint); flex: none;
}
.podman-row-group-item .podman-row-remove-btn:hover { background: var(--bad-bg); border-color: transparent; color: var(--bad); }
/* Anchored dropdown context menu — see app.js openContextMenu(). */
.podman-context-menu {
/*
* "fixed", not "absolute": this menu is appended to .podman-plugin, not
* document.body, and Unraid's own page wrapper around .podman-plugin
* turned out to have its own positioned ancestor — with "absolute" the
* menu was positioning itself relative to THAT ancestor's box while the
* JS math (getBoundingClientRect + scrollY/X) assumed the viewport,
* so it rendered far from the button that opened it (found live: it
* appeared well below and to the side of the anchor). "fixed" is always
* viewport-relative regardless of any ancestor, which is what the JS
* math actually assumes.
*/
position: fixed; 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); background: var(--bad-bg); }
.podman-context-menu button.danger:hover { background: var(--bad); color: var(--bad-contrast); }
.podman-context-menu button[disabled] { opacity: .4; cursor: not-allowed; }
.podman-context-menu-sep { height: 1px; background: var(--border); margin: 4px 2px; }
/* Container detail modal — see containers.js openDetailModal(). */
.podman-modal-xwide { max-width: 760px; }
.podman-detail-tabs {
display: flex; gap: 2px; padding: 0 20px; border-bottom: 1px solid var(--border); overflow-x: auto;
}
.podman-detail-tabs button {
appearance: none; background: none; border: none; border-bottom: 2px solid transparent;
color: var(--text-dim); padding: 10px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;
white-space: nowrap; font-family: var(--font-ui);
}
.podman-detail-tabs button:hover { color: var(--text); }
.podman-detail-tabs button.active { color: var(--accent-strong); border-bottom-color: var(--accent); }
.podman-detail-body { padding: 16px 20px; max-height: 50vh; overflow-y: auto; }
.podman-detail-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.podman-detail-table td { padding: 6px 0; border-bottom: 1px solid var(--border); vertical-align: top; }
.podman-detail-table td:first-child { color: var(--text-faint); font-weight: 600; width: 160px; padding-right: 12px; }
.podman-detail-table td.mono { font-family: var(--font-mono); word-break: break-all; }
.podman-detail-json {
background: #0f1114; color: #c7ccd4; font-family: var(--font-mono); font-size: 11.8px;
padding: 14px 16px; border-radius: 8px; white-space: pre-wrap; word-break: break-all; line-height: 1.6;
}
.podman-detail-empty { color: var(--text-faint); font-size: 12.5px; padding: 8px 0; }