The button drew its text from --status-danger, which is tuned as a FILL colour — the confirm button in the remove modal sits white text on top of it, and there it is correct. Reused as text on the app's own background it measures 3.79:1 on nebula, 3.84 on dark-blue and 3.95 on the example theme, all under the 4.5:1 AA floor, which is why the control read as barely there. Light is the only theme where the same value passes, at 4.53:1, so brightening it globally would have fixed three themes by breaking the fourth. Two jobs, two values. --status-danger-text is now defined per theme: #f87171 on the dark ones (6.29-6.47:1) and #c92a37 on light (5.43:1, up from 4.53). #f87171 is not a new invention — it is the red the CVE severity chips already use for exactly this job, so danger-as-text now looks the same wherever it appears. The border follows the text so the two stay coherent, and both fall back to --status-danger, leaving any theme without the token no worse off than before. The filled modal button is deliberately untouched: white on #dc3545 was never the problem. Other danger-as-text call sites exist (loading screen, network notifier) and would likely benefit, but they render on their own backgrounds rather than the app surface, so they are not assumed to share the measurement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
347 lines
15 KiB
CSS
347 lines
15 KiB
CSS
/* Nebula — default theme. Matches the loading/login aurora gradient. */
|
|
[data-theme="nebula"] {
|
|
/* Gradient stops (shared by surface-bg and install/login screens).
|
|
Three deep cosmic stops: indigo -> violet-blue -> ocean. */
|
|
--gradient-from: #1a1442;
|
|
--gradient-mid: #1b2a5e;
|
|
--gradient-to: #0f3b6e;
|
|
|
|
/* Surfaces */
|
|
--surface-bg: linear-gradient(135deg, var(--gradient-from) 0%, var(--gradient-mid) 50%, var(--gradient-to) 100%);
|
|
--surface-bg-solid: #0f1729;
|
|
--surface-elevated: rgba(255, 255, 255, 0.07);
|
|
--surface-sunken: rgba(0, 0, 0, 0.22);
|
|
--surface-hover: rgba(255, 255, 255, 0.10);
|
|
--surface-selected: rgba(0, 212, 255, 0.18);
|
|
|
|
/* Borders — bright enough to read as glass edges */
|
|
--border: rgba(255, 255, 255, 0.16);
|
|
--border-strong: rgba(255, 255, 255, 0.26);
|
|
--border-subtle: rgba(255, 255, 255, 0.08);
|
|
|
|
/* Text */
|
|
--text-primary: #ffffff;
|
|
--text-secondary: rgba(255, 255, 255, 0.82);
|
|
--text-muted: rgba(255, 255, 255, 0.65);
|
|
--text-on-accent: #0a1426;
|
|
|
|
/* Accent (cyan from the loading screen) */
|
|
--accent: #00d4ff;
|
|
--accent-hover: #0099cc;
|
|
--accent-soft: rgba(0, 212, 255, 0.15);
|
|
--accent-rgb: 0, 212, 255;
|
|
|
|
/* Status colors — stable across themes */
|
|
--status-success: #28a745;
|
|
--status-success-hover: #218838;
|
|
--status-success-rgb: 40, 167, 69;
|
|
--status-danger: #dc3545;
|
|
--status-danger-hover: #c82333;
|
|
--status-danger-rgb: 220, 53, 69;
|
|
/* Danger as TEXT on this theme's own background. --status-danger is tuned as a
|
|
FILL (white sits on top of it); reused as text it drops to 3.8:1 here, under
|
|
the 4.5:1 AA floor. Two jobs, two values. */
|
|
--status-danger-text: #f87171;
|
|
--status-danger-text-rgb: 248, 113, 113;
|
|
--status-warning: #ffc107;
|
|
--status-warning-rgb: 255, 193, 7;
|
|
--status-info: #17a2b8;
|
|
--status-info-rgb: 23, 162, 184;
|
|
|
|
/* Theme-aware foreground RGB triplets for overlays */
|
|
--text-rgb: 255, 255, 255;
|
|
--bg-rgb: 0, 0, 0;
|
|
|
|
/* Components — cards LIFT off the gradient with a light glassy tint
|
|
instead of darkening it. The cyan undertone keeps them on-theme. */
|
|
--card-bg: linear-gradient(155deg, rgba(255, 255, 255, 0.09) 0%, rgba(0, 212, 255, 0.05) 100%);
|
|
--card-border: rgba(255, 255, 255, 0.16);
|
|
--card-shadow: 0 4px 18px rgba(0, 0, 0, 0.30), inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
|
--card-shadow-hover: 0 10px 32px rgba(0, 212, 255, 0.18), 0 4px 18px rgba(0, 0, 0, 0.40), inset 0 1px 0 rgba(255, 255, 255, 0.10);
|
|
--input-bg: rgba(255, 255, 255, 0.06);
|
|
--input-border: rgba(255, 255, 255, 0.18);
|
|
--input-text: #ffffff;
|
|
--console-bg: rgba(0, 0, 0, 0.40);
|
|
--console-text: #00d4ff;
|
|
--code-bg: rgba(255, 255, 255, 0.06);
|
|
--code-text: #e6f3ff;
|
|
--tooltip-bg: rgba(10, 20, 38, 0.95);
|
|
--tooltip-text: #ffffff;
|
|
|
|
/* Chrome — translucent so the cosmic gradient bleeds through.
|
|
The backdrop-filter on .topbar/.sidebar frosts them into glass. */
|
|
--topbar-bg: rgba(15, 25, 50, 0.40);
|
|
--topbar-text: #ffffff;
|
|
--topbar-border: rgba(255, 255, 255, 0.12);
|
|
--sidebar-bg: rgba(15, 25, 50, 0.30);
|
|
--sidebar-text: #ffffff;
|
|
--sidebar-border: rgba(255, 255, 255, 0.10);
|
|
|
|
/* Legacy aliases — kept so older style.css var() refs resolve */
|
|
--bg-primary: var(--topbar-bg);
|
|
--bg-secondary: var(--surface-elevated);
|
|
--bg-tertiary: var(--surface-hover);
|
|
--text-color: var(--text-primary);
|
|
--primary-color: var(--accent);
|
|
--primary-hover: var(--accent-hover);
|
|
--primary-color-rgb: var(--accent-rgb);
|
|
--border-color: var(--border);
|
|
--content-bg: var(--surface-bg);
|
|
--hover-bg: var(--surface-hover);
|
|
--error-color: var(--status-danger);
|
|
--danger-color: var(--status-danger);
|
|
}
|
|
|
|
/* Every rule below must be prefixed with [data-theme="nebula"] — the
|
|
theme-registry loads every theme's CSS file at boot, so unscoped
|
|
selectors here would apply on light / dark-blue / example as well. */
|
|
|
|
/* Notifications float over arbitrary content (apps grid, task logs, etc.),
|
|
so the default --card-bg gradient — designed for cards inside a container
|
|
— bleeds the page through them. Use the sidebar's navy with a much higher
|
|
opacity so the toast reads as a solid panel without losing the glass feel. */
|
|
[data-theme="nebula"] .notification {
|
|
background: rgba(15, 25, 50, 0.88);
|
|
border-color: rgba(255, 255, 255, 0.14);
|
|
backdrop-filter: blur(12px);
|
|
-webkit-backdrop-filter: blur(12px);
|
|
}
|
|
|
|
/* App-page tab strips: anchor every tab bar to the same surface as
|
|
the sidebar so the chrome reads as one coherent navy surface.
|
|
Using var(--sidebar-bg) directly so any future sidebar tweak
|
|
carries through. Covers both tab idioms on the apps page:
|
|
1. .tab-navigation — main app tabs (Config / Services / …) and
|
|
the Backup-location sub-tabs (Local / Remote 1 / Remote 2).
|
|
2. .tabs-list inside .tabs-wrapper — config sub-tabs from the
|
|
app-config form, plus the fleet Overview's Migrate and Backups
|
|
sub-tabs (all share the canonical .tabs-wrapper markup). */
|
|
[data-theme="nebula"] .tab-navigation,
|
|
[data-theme="nebula"] .tabs-wrapper .tabs-list,
|
|
[data-theme="nebula"] .tabs-content {
|
|
background: var(--sidebar-bg);
|
|
}
|
|
|
|
[data-theme="nebula"] .login-btn {
|
|
color: #ffffff;
|
|
}
|
|
|
|
/* Amber-tinted secondary — same translucent design as Update Configuration
|
|
(btn-manage). Used by "Back to Apps" (.console-actions), the Config Reset
|
|
button (.config-actions), and "Open backup center" on the per-app Backups
|
|
tab (.backup-title-actions) so all the "step away from the current page"
|
|
secondary actions read as one family. */
|
|
[data-theme="nebula"] .config-actions .btn-secondary,
|
|
[data-theme="nebula"] .console-actions .btn-secondary,
|
|
[data-theme="nebula"] .backup-title-actions .btn-secondary {
|
|
background: rgba(var(--status-warning-rgb), 0.20) !important;
|
|
color: var(--text-primary) !important;
|
|
border: 1px solid rgba(var(--status-warning-rgb), 0.55) !important;
|
|
text-shadow: none;
|
|
font-weight: 600 !important;
|
|
transition: background 0.18s ease, border-color 0.18s ease, transform 0.15s ease !important;
|
|
}
|
|
|
|
[data-theme="nebula"] .config-actions .btn-secondary:hover:not(:disabled),
|
|
[data-theme="nebula"] .console-actions .btn-secondary:hover:not(:disabled),
|
|
[data-theme="nebula"] .backup-title-actions .btn-secondary:hover:not(:disabled) {
|
|
background: rgba(var(--status-warning-rgb), 0.35) !important;
|
|
border-color: rgba(var(--status-warning-rgb), 0.80) !important;
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
/* Advanced badge — bump the red to a brighter coral pink so it pops
|
|
on the cosmic gradient. The theme --status-danger (#dc3545) reads
|
|
as muddy maroon against nebula's blue/indigo, similar to how
|
|
--status-success needed a lift to #86efac for task pills. */
|
|
[data-theme="nebula"] .is-advanced .domains-header h3::after,
|
|
[data-theme="nebula"] .is-advanced .config-category h3::after {
|
|
background: rgba(252, 165, 165, 0.18);
|
|
border-color: rgba(252, 165, 165, 0.65);
|
|
color: #fca5a5;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------
|
|
Outline + tint buttons — copied from the .service-button "Welcome"
|
|
recipe (rgba(<colour>, 0.10) bg + rgba(<colour>, 0.30) border +
|
|
neutral text). Topbar pills use that exact alpha for the light feel;
|
|
in-content CTAs (Install/Manage/Uninstall) bump the alphas a bit and
|
|
add a subtle coloured outer glow so they feel weightier without
|
|
becoming solid.
|
|
------------------------------------------------------------------ */
|
|
|
|
/* --- Topbar pills (light, transparent) --------------------------- */
|
|
[data-theme="nebula"] .topbar .donate-btn {
|
|
background: rgba(var(--accent-rgb), 0.10) !important;
|
|
color: var(--text-primary) !important;
|
|
border: 1px solid rgba(var(--accent-rgb), 0.30) !important;
|
|
box-shadow: none !important;
|
|
text-shadow: none;
|
|
font-weight: 600;
|
|
transition: background 0.18s ease, border-color 0.18s ease, transform 0.15s ease, box-shadow 0.2s ease !important;
|
|
}
|
|
|
|
[data-theme="nebula"] .topbar .donate-btn:hover:not(:disabled) {
|
|
background: rgba(var(--accent-rgb), 0.20) !important;
|
|
border-color: rgba(var(--accent-rgb), 0.55) !important;
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
[data-theme="nebula"] .topbar .logout-btn {
|
|
background: rgba(var(--status-danger-rgb), 0.10) !important;
|
|
color: var(--text-primary) !important;
|
|
border: 1px solid rgba(var(--status-danger-rgb), 0.30) !important;
|
|
box-shadow: none !important;
|
|
text-shadow: none;
|
|
font-weight: 600;
|
|
transition: background 0.18s ease, border-color 0.18s ease, transform 0.15s ease, box-shadow 0.2s ease !important;
|
|
}
|
|
|
|
[data-theme="nebula"] .topbar .logout-btn:hover:not(:disabled) {
|
|
background: rgba(var(--status-danger-rgb), 0.20) !important;
|
|
border-color: rgba(var(--status-danger-rgb), 0.55) !important;
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
/* --- Topbar active nav (App Center / etc.) — translucent pill ----
|
|
Replaces the solid var(--primary-color) fill so the active page
|
|
indicator matches the Donate / Logout pill style. */
|
|
[data-theme="nebula"] .topbar-nav .nav-item.nav-active,
|
|
[data-theme="nebula"] .topbar-nav .nav-item.active {
|
|
background: rgba(var(--accent-rgb), 0.18) !important;
|
|
color: var(--text-primary) !important;
|
|
border-color: rgba(var(--accent-rgb), 0.50) !important;
|
|
}
|
|
|
|
[data-theme="nebula"] .topbar-nav .nav-item.nav-active:hover,
|
|
[data-theme="nebula"] .topbar-nav .nav-item.active:hover {
|
|
background: rgba(var(--accent-rgb), 0.28) !important;
|
|
color: var(--text-primary) !important;
|
|
border-color: rgba(var(--accent-rgb), 0.70) !important;
|
|
}
|
|
|
|
/* --- In-content CTAs (more solid than the topbar but still
|
|
transparent — alpha set above the body's accent-glow so the
|
|
brand colour reads clearly against Nebula's gradient.) ------- */
|
|
[data-theme="nebula"] .install-btn,
|
|
[data-theme="nebula"] .btn-install,
|
|
[data-theme="nebula"] .app-card .install-btn {
|
|
background: rgba(var(--status-success-rgb), 0.55) !important;
|
|
color: var(--text-primary) !important;
|
|
border: 1px solid rgba(var(--status-success-rgb), 0.90) !important;
|
|
text-shadow: none;
|
|
font-weight: 600 !important;
|
|
transition: background 0.18s ease, border-color 0.18s ease, transform 0.15s ease !important;
|
|
}
|
|
|
|
[data-theme="nebula"] .install-btn:hover:not(:disabled),
|
|
[data-theme="nebula"] .btn-install:hover:not(:disabled),
|
|
[data-theme="nebula"] .app-card .install-btn:hover:not(:disabled) {
|
|
background: rgba(var(--status-success-rgb), 0.70) !important;
|
|
border-color: rgba(var(--status-success-rgb), 1.00) !important;
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
/* "Open" button on installed app cards — same success recipe. */
|
|
[data-theme="nebula"] .service-trigger-icon {
|
|
background: rgba(var(--status-success-rgb), 0.35) !important;
|
|
color: var(--text-primary) !important;
|
|
border: 1px solid rgba(var(--status-success-rgb), 0.65) !important;
|
|
}
|
|
|
|
[data-theme="nebula"] .service-trigger:hover .service-trigger-icon,
|
|
[data-theme="nebula"] .service-trigger.open .service-trigger-icon {
|
|
background: rgba(var(--status-success-rgb), 0.50) !important;
|
|
border-color: rgba(var(--status-success-rgb), 0.85) !important;
|
|
}
|
|
|
|
[data-theme="nebula"] .uninstall-btn,
|
|
[data-theme="nebula"] .btn-uninstall,
|
|
[data-theme="nebula"] .btn-danger,
|
|
[data-theme="nebula"] .backup-danger-btn,
|
|
[data-theme="nebula"] .tool-run-btn.destructive {
|
|
background: rgba(var(--status-danger-rgb), 0.35) !important;
|
|
color: var(--text-primary) !important;
|
|
border: 1px solid rgba(var(--status-danger-rgb), 0.65) !important;
|
|
text-shadow: none;
|
|
font-weight: 600 !important;
|
|
transition: background 0.18s ease, border-color 0.18s ease, transform 0.15s ease !important;
|
|
}
|
|
|
|
[data-theme="nebula"] .uninstall-btn:hover:not(:disabled),
|
|
[data-theme="nebula"] .btn-uninstall:hover:not(:disabled),
|
|
[data-theme="nebula"] .btn-danger:hover:not(:disabled),
|
|
[data-theme="nebula"] .backup-danger-btn:hover:not(:disabled),
|
|
[data-theme="nebula"] .tool-run-btn.destructive:hover:not(:disabled) {
|
|
background: rgba(var(--status-danger-rgb), 0.50) !important;
|
|
border-color: rgba(var(--status-danger-rgb), 0.85) !important;
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
/* Tools-page Run buttons — same translucent recipe as service-trigger
|
|
(the small "Open" pill on installed app cards). The 0.12/0.30 alphas
|
|
from tools.css read as muddy green/red against Nebula's cosmic
|
|
gradient, especially with --status-success/danger text — bump to
|
|
0.35/0.65 with neutral text so they pop. .destructive picks up the
|
|
danger recipe above via the chained selector. */
|
|
[data-theme="nebula"] .tool-run-btn {
|
|
background: rgba(var(--status-success-rgb), 0.35) !important;
|
|
color: var(--text-primary) !important;
|
|
border: 1px solid rgba(var(--status-success-rgb), 0.65) !important;
|
|
text-shadow: none;
|
|
font-weight: 600 !important;
|
|
transition: background 0.18s ease, border-color 0.18s ease, transform 0.15s ease !important;
|
|
}
|
|
|
|
[data-theme="nebula"] .tool-run-btn:hover:not(:disabled):not(.destructive) {
|
|
background: rgba(var(--status-success-rgb), 0.50) !important;
|
|
border-color: rgba(var(--status-success-rgb), 0.85) !important;
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
[data-theme="nebula"] .manage-btn,
|
|
[data-theme="nebula"] .btn-manage,
|
|
[data-theme="nebula"] .btn-primary,
|
|
[data-theme="nebula"] .backup-primary-btn,
|
|
[data-theme="nebula"] .app-card .manage-btn,
|
|
[data-theme="nebula"] .app-card-actions .manage-btn {
|
|
background: rgba(var(--accent-rgb), 0.35) !important;
|
|
color: var(--text-primary) !important;
|
|
border: 1px solid rgba(var(--accent-rgb), 0.65) !important;
|
|
text-shadow: none;
|
|
font-weight: 600 !important;
|
|
transition: background 0.18s ease, border-color 0.18s ease, transform 0.15s ease !important;
|
|
}
|
|
|
|
[data-theme="nebula"] .manage-btn:hover:not(:disabled),
|
|
[data-theme="nebula"] .btn-manage:hover:not(:disabled),
|
|
[data-theme="nebula"] .btn-primary:hover:not(:disabled),
|
|
[data-theme="nebula"] .backup-primary-btn:hover:not(:disabled),
|
|
[data-theme="nebula"] .app-card .manage-btn:hover:not(:disabled),
|
|
[data-theme="nebula"] .app-card-actions .manage-btn:hover:not(:disabled) {
|
|
background: rgba(var(--accent-rgb), 0.50) !important;
|
|
border-color: rgba(var(--accent-rgb), 0.85) !important;
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
[data-theme="nebula"] .install-btn:disabled,
|
|
[data-theme="nebula"] .btn-install:disabled,
|
|
[data-theme="nebula"] .uninstall-btn:disabled,
|
|
[data-theme="nebula"] .btn-uninstall:disabled,
|
|
[data-theme="nebula"] .manage-btn:disabled,
|
|
[data-theme="nebula"] .btn-manage:disabled,
|
|
[data-theme="nebula"] .btn-primary:disabled,
|
|
[data-theme="nebula"] .btn-danger:disabled,
|
|
[data-theme="nebula"] .topbar .donate-btn:disabled,
|
|
[data-theme="nebula"] .topbar .logout-btn:disabled {
|
|
opacity: 0.50;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* The cyan accent is bright enough that a dark glyph (text-on-accent
|
|
= #0a1426) still reads, but white matches nebula's white-on-glass
|
|
system better. */
|
|
[data-theme="nebula"] .tooltip::after {
|
|
color: #ffffff;
|
|
}
|