Fix all remaining dark-theme colour bugs across the templates

Follow-up to the player-modal / maintenance-page fix: the same root cause --
colours hard-coded for the light theme -- affected the whole app, in two
places.

1) Inline style attributes (inline outranks the theme rules, so the light
   value survives dark mode and the inherited text colour turns
   light-on-light). base.html now carries a single, property-scoped
   dark-mode override layer that maps every light literal found in the
   templates to the theme variables: light backgrounds/whites -> dark
   surfaces, semantic pastels -> dark equivalents, dark text -> light text,
   light borders -> dark borders, plus .back-link. Scoped to
   body.dark-mode, so the light theme is untouched. This covers all
   templates, including ones not edited here and any added later.

2) Page-level <style> blocks with no dark support:
   * admin/https_config.html hard-coded a light-only palette (blue-tint
     panels, dark labels) -- added a body.dark-mode block for it.
   * dashboard.html / help.html / manage_player.html coloured text links
     with --primary-color (#7c3aed in dark = too dim). Introduced a
     dedicated --link-color (#667eea light / #a78bfa dark) and used it for
     text links, leaving --primary-color for button/background use.

Verified with an automated luminance scan of all 18 pages in both themes:
dark mode has 0 light backgrounds and 0 dark-on-dark text (the only
remaining light surfaces are the intentional white buttons on the admin
gradient cards), and light mode is unchanged.
This commit is contained in:
2026-10-14 22:45:09 +03:00
parent 5b69ccb02b
commit 4dc8506c9b
5 changed files with 258 additions and 10 deletions
+86
View File
@@ -577,6 +577,92 @@
width: 100%; width: 100%;
} }
} }
/* ------------------------------------------------------------------
Dark theme
------------------------------------------------------------------
This page predates the shared dark-mode variables and hard-coded its
own light palette (light panel/blue-tint backgrounds with dark text).
The rules below re-map those to dark equivalents so the page matches
the rest of the app when the dark theme is active.
------------------------------------------------------------------ */
body.dark-mode .back-link { color: #90cdf4; }
body.dark-mode .status-detection {
background: #1e3a5f;
border-left-color: #90cdf4;
}
body.dark-mode .badge-success { background: #1a4d2e; color: #86efac; }
body.dark-mode .badge-warning { background: #4a3800; color: #fbbf24; }
body.dark-mode .status-card { border-left-color: #4a5568; }
body.dark-mode .status-enabled {
background: linear-gradient(135deg, #1a4d2e 0%, #22543d 100%);
}
body.dark-mode .status-disabled {
background: linear-gradient(135deg, #4a3800 0%, #5a4a1e 100%);
}
body.dark-mode .status-badge { background: #276749; color: #e2e8f0; }
body.dark-mode .status-badge-inactive { background: #b45309; color: #fff; }
body.dark-mode .status-details code { background: rgba(255, 255, 255, 0.12); }
body.dark-mode .info-text {
background: #1e3a5f;
border-left-color: #90cdf4;
color: #cbd5e1;
}
body.dark-mode .form-group label { color: var(--text-color); }
body.dark-mode .form-input {
background: var(--card-bg);
color: var(--text-color);
border-color: var(--border-color);
}
body.dark-mode .form-input:focus { border-color: #90cdf4; }
body.dark-mode .form-hint { color: var(--text-secondary); }
body.dark-mode .required { color: #fca5a5; }
body.dark-mode .toggle-slider { background: #4a5568; }
body.dark-mode .toggle-slider::after { background: #e2e8f0; }
body.dark-mode .toggle-input:checked + .toggle-slider { background: #276749; }
body.dark-mode .toggle-text { color: var(--text-color); }
body.dark-mode .preview-section {
background: var(--bg-color);
border-color: #90cdf4;
}
body.dark-mode .preview-section h3 { color: #90cdf4; }
body.dark-mode .access-points li {
background: var(--card-bg);
border-left-color: #90cdf4;
}
body.dark-mode .access-points code { background: #1e3a5f; color: #90cdf4; }
body.dark-mode .form-actions { border-top-color: var(--border-color); }
body.dark-mode .info-card {
background: linear-gradient(135deg, #1e3a5f 0%, #162032 100%);
}
body.dark-mode .info-card h2 { color: #90cdf4; }
body.dark-mode .info-card li { color: var(--text-secondary); }
body.dark-mode .nginx-status-card {
background: linear-gradient(135deg, #162032 0%, #1e3a5f 100%);
border-left-color: #90cdf4;
}
body.dark-mode .nginx-status-card h2 { color: #90cdf4; }
body.dark-mode .status-item {
background: var(--card-bg);
border-left-color: #90cdf4;
}
body.dark-mode .status-item strong { color: var(--text-color); }
body.dark-mode .status-item code { background: #1e3a5f; color: #90cdf4; }
body.dark-mode .info-section h3 { color: #90cdf4; }
body.dark-mode .info-section li { color: var(--text-secondary); }
</style> </style>
<script> <script>
+166
View File
@@ -18,6 +18,10 @@
--primary-color: #667eea; --primary-color: #667eea;
--primary-dark: #5568d3; --primary-dark: #5568d3;
--secondary-color: #764ba2; --secondary-color: #764ba2;
/* Colour for text links. Kept separate from --primary-color so the
primary colour can stay dark enough for button backgrounds while
links remain legible on the matching background. */
--link-color: #667eea;
--bg-color: #f5f7fa; --bg-color: #f5f7fa;
--card-bg: #ffffff; --card-bg: #ffffff;
--text-color: #2d3748; --text-color: #2d3748;
@@ -34,6 +38,9 @@
--primary-color: #7c3aed; --primary-color: #7c3aed;
--primary-dark: #6d28d9; --primary-dark: #6d28d9;
--secondary-color: #8b5cf6; --secondary-color: #8b5cf6;
/* Dark background needs a lighter link colour (#7c3aed is too dim
on a dark surface). */
--link-color: #a78bfa;
--bg-color: #1a202c; --bg-color: #1a202c;
--card-bg: #2d3748; --card-bg: #2d3748;
--text-color: #e2e8f0; --text-color: #e2e8f0;
@@ -369,6 +376,165 @@
color: var(--text-secondary); color: var(--text-secondary);
font-size: 0.9rem; font-size: 0.9rem;
} }
/* ================================================================
Dark-mode fixes for INLINE colours
----------------------------------------------------------------
A recurring pattern across the templates is to set colours with
inline style="..." attributes (a few JS blocks build nodes the
same way). An inline declaration outranks every normal rule --
including the body.dark-mode rules above -- so those literals keep
their LIGHT value in dark mode. The text, which inherits the (now
light) --text-color, then ends up light-on-light: unreadable.
Inline styles can only be beaten with !important, so the light
literals are mapped to the theme variables here, centrally, with
the property scoped so a colour used for two things (e.g. #1a202c
is both a dark surface and dark text) is only changed where it is
wrong. This fixes every template at once -- including any future
page that repeats the pattern -- instead of patching each file.
Everything is scoped to body.dark-mode, so the LIGHT theme is
completely untouched.
Ordering matters: selectors have equal specificity, so later rules
win. The bare "#fff" rule is therefore placed BEFORE the "#fff3cd"
/"#fff8e1" ones, because "#fff" is a string prefix of them.
================================================================ */
/* --- White / near-white surfaces -> dark card --------------------- */
body.dark-mode [style*="background: white"], body.dark-mode [style*="background-color: white"],
body.dark-mode [style*="background: #fff"], body.dark-mode [style*="background-color: #fff"],
body.dark-mode [style*="background: #ffffff"], body.dark-mode [style*="background-color: #ffffff"],
body.dark-mode [style*="background: #fefefe"], body.dark-mode [style*="background-color: #fefefe"],
body.dark-mode [style*="background: #f5f7fa"], body.dark-mode [style*="background-color: #f5f7fa"],
body.dark-mode [style*="background: #e9ecef"], body.dark-mode [style*="background-color: #e9ecef"],
body.dark-mode [style*="background: #e0e0e0"], body.dark-mode [style*="background-color: #e0e0e0"],
body.dark-mode [style*="background: #f0f2ff"], body.dark-mode [style*="background-color: #f0f2ff"],
body.dark-mode [style*="background: #e8ebff"], body.dark-mode [style*="background-color: #e8ebff"],
body.dark-mode [style*="background: #e7e9ff"], body.dark-mode [style*="background-color: #e7e9ff"],
body.dark-mode [style*="background: #ede9fe"], body.dark-mode [style*="background-color: #ede9fe"] {
background: var(--card-bg) !important;
}
/* --- Light grey panels -> dark page background -------------------- */
body.dark-mode [style*="background: #f8f9fa"], body.dark-mode [style*="background-color: #f8f9fa"],
body.dark-mode [style*="background: #f0f0f0"], body.dark-mode [style*="background-color: #f0f0f0"],
body.dark-mode [style*="background: #f5f5f5"], body.dark-mode [style*="background-color: #f5f5f5"],
body.dark-mode [style*="background: #f4f4f4"], body.dark-mode [style*="background-color: #f4f4f4"],
body.dark-mode [style*="background: #f8fafc"], body.dark-mode [style*="background-color: #f8fafc"] {
background: var(--bg-color) !important;
}
/* --- Semantic pastel backgrounds -> dark equivalents -------------- */
/* info / blue */
body.dark-mode [style*="background: #d1ecf1"], body.dark-mode [style*="background-color: #d1ecf1"],
body.dark-mode [style*="background: #cce5ff"], body.dark-mode [style*="background-color: #cce5ff"],
body.dark-mode [style*="background: #e7f3ff"], body.dark-mode [style*="background-color: #e7f3ff"],
body.dark-mode [style*="background: #f0f7ff"], body.dark-mode [style*="background-color: #f0f7ff"] {
background: #1e3a5f !important;
}
/* success / green */
body.dark-mode [style*="background: #d4edda"], body.dark-mode [style*="background-color: #d4edda"],
body.dark-mode [style*="background: #d1f0e0"], body.dark-mode [style*="background-color: #d1f0e0"],
body.dark-mode [style*="background: #c6f6d5"], body.dark-mode [style*="background-color: #c6f6d5"] {
background: #1a4d2e !important;
}
/* warning / amber (must stay AFTER the "#fff" rule above) */
body.dark-mode [style*="background: #fff3cd"], body.dark-mode [style*="background-color: #fff3cd"],
body.dark-mode [style*="background: #fff8e1"], body.dark-mode [style*="background-color: #fff8e1"],
body.dark-mode [style*="background: #ffeaa7"], body.dark-mode [style*="background-color: #ffeaa7"],
body.dark-mode [style*="background: #fef3c7"], body.dark-mode [style*="background-color: #fef3c7"],
body.dark-mode [style*="background: #feebc8"], body.dark-mode [style*="background-color: #feebc8"] {
background: #4a3800 !important;
}
/* danger / red */
body.dark-mode [style*="background: #f8d7da"], body.dark-mode [style*="background-color: #f8d7da"],
body.dark-mode [style*="background: #fed7d7"], body.dark-mode [style*="background-color: #fed7d7"] {
background: #4a1a1a !important;
}
/* mid-grey chips / knobs */
body.dark-mode [style*="background: #ccc"], body.dark-mode [style*="background-color: #ccc"],
body.dark-mode [style*="background: #6c757d"], body.dark-mode [style*="background-color: #6c757d"] {
background: #4a5568 !important;
}
/* --- Dark text -> light text -------------------------------------- */
body.dark-mode [style*="color: black"] {
color: var(--text-color) !important;
}
body.dark-mode [style*="color: #6c757d"], body.dark-mode [style*="color:#6c757d"],
body.dark-mode [style*="color: #666"], body.dark-mode [style*="color: #888"],
body.dark-mode [style*="color: #999"], body.dark-mode [style*="color: #aaa"],
body.dark-mode [style*="color: #495057"], body.dark-mode [style*="color: #718096"],
body.dark-mode [style*="color: #7f8c8d"], body.dark-mode [style*="color: #5a6a78"],
body.dark-mode [style*="color: #4a5568"] {
color: var(--text-secondary) !important;
}
body.dark-mode [style*="color: #555"], body.dark-mode [style*="color: #475569"],
body.dark-mode [style*="color: #64748b"] {
color: #cbd5e1 !important;
}
body.dark-mode [style*="color: #333"], body.dark-mode [style*="color: #2c3e50"],
body.dark-mode [style*="color: #000"], body.dark-mode [style*="color: #1a202c"],
body.dark-mode [style*="color: #2d3748"] {
color: var(--text-color) !important;
}
/* links / info blue */
body.dark-mode [style*="color: #0066cc"], body.dark-mode [style*="color: #007bff"],
body.dark-mode [style*="color: #0056b3"], body.dark-mode [style*="color: #0c5460"],
body.dark-mode [style*="color: #004085"], body.dark-mode [style*="color: #3498db"] {
color: #90cdf4 !important;
}
/* success text */
body.dark-mode [style*="color: #155724"], body.dark-mode [style*="color: #156b2e"],
body.dark-mode [style*="color: #28a745"], body.dark-mode [style*="color: #27ae60"],
body.dark-mode [style*="color: #2f855a"] {
color: #86efac !important;
}
/* warning text */
body.dark-mode [style*="color: #856404"], body.dark-mode [style*="color: #92400e"],
body.dark-mode [style*="color: #c05621"], body.dark-mode [style*="color: #e67e22"] {
color: #fbbf24 !important;
}
/* danger text */
body.dark-mode [style*="color: #721c24"], body.dark-mode [style*="color: #dc3545"],
body.dark-mode [style*="color: #e74c3c"], body.dark-mode [style*="color: #e53e3e"],
body.dark-mode [style*="color:#e53e3e"], body.dark-mode [style*="color: #c53030"],
body.dark-mode [style*="color: #b91c1c"] {
color: #fca5a5 !important;
}
/* purple accents */
body.dark-mode [style*="color: #5b21b6"] {
color: #a78bfa !important;
}
/* --- Light borders -> dark borders ------------------------------- */
body.dark-mode [style*="border-top-color: white"] {
border-top-color: var(--border-color) !important;
}
body.dark-mode [style*="#e2e8f0"], body.dark-mode [style*="#dee2e6"],
body.dark-mode [style*="#ddd"], body.dark-mode [style*="#ccc"],
body.dark-mode [style*="#ced4da"], body.dark-mode [style*="#e0e0e0"],
body.dark-mode [style*="#eee"], body.dark-mode [style*="#e9ecef"],
body.dark-mode [style*="#f0f0f0"], body.dark-mode [style*="#f0f2ff"],
body.dark-mode [style*="#888"], body.dark-mode [style*="#999"] {
border-color: #4a5568 !important;
}
body.dark-mode [style*="#c3e6cb"], body.dark-mode [style*="#d1f0e0"] {
border-color: #2f855a !important;
}
body.dark-mode [style*="#f5c6cb"] {
border-color: #c53030 !important;
}
body.dark-mode [style*="#bee5eb"], body.dark-mode [style*="#cce5ff"] {
border-color: #2c5282 !important;
}
/* --- "Back" links (class used across the admin pages) ------------- */
body.dark-mode .back-link {
color: #90cdf4 !important;
}
</style> </style>
{% block extra_css %}{% endblock %} {% block extra_css %}{% endblock %}
</head> </head>
+2 -2
View File
@@ -315,7 +315,7 @@ body.dark-mode .compact-btn:not(.btn-success) .compact-btn-icon {
.playlist-name { .playlist-name {
font-weight: 600; font-weight: 600;
font-size: 1rem; font-size: 1rem;
color: var(--primary-color); color: var(--link-color);
text-decoration: none; text-decoration: none;
word-break: break-word; word-break: break-word;
} }
@@ -374,7 +374,7 @@ body.dark-mode .player-chip.is-offline .status-dot { background: #718096; }
font-size: 0.85rem; font-size: 0.85rem;
} }
.unassigned-note a { .unassigned-note a {
color: var(--primary-color); color: var(--link-color);
} }
/* Sidebar collapses below the main column when space runs out. */ /* Sidebar collapses below the main column when space runs out. */
+3 -3
View File
@@ -51,7 +51,7 @@
} }
.help-toc a:hover { .help-toc a:hover {
background: var(--bg-color); background: var(--bg-color);
color: var(--primary-color); color: var(--link-color);
} }
.help-toc a.active { .help-toc a.active {
background: var(--primary-color); background: var(--primary-color);
@@ -79,7 +79,7 @@
font-size: 1.4rem; font-size: 1.4rem;
margin: 2.25rem 0 0.85rem; margin: 2.25rem 0 0.85rem;
padding-top: 1rem; padding-top: 1rem;
color: var(--primary-color); color: var(--link-color);
scroll-margin-top: 20px; scroll-margin-top: 20px;
} }
.help-content h3 { .help-content h3 {
@@ -99,7 +99,7 @@
border-top: 1px solid var(--border-color); border-top: 1px solid var(--border-color);
margin: 2rem 0; margin: 2rem 0;
} }
.help-content a { color: var(--primary-color); } .help-content a { color: var(--link-color); }
.help-content code { .help-content code {
background: var(--bg-color); background: var(--bg-color);
padding: 0.15rem 0.4rem; padding: 0.15rem 0.4rem;
+1 -5
View File
@@ -363,11 +363,7 @@
} }
.modal-player { .modal-player {
color: var(--primary-color); color: var(--link-color);
}
body.dark-mode .modal-player {
color: #a78bfa;
} }
.modal-warning { .modal-warning {