feat: add dark mode with light/dark/auto theme setting
Theme preference stored in localStorage (auto follows the OS setting). The chosen data-theme attribute is applied synchronously in <head> to avoid any flash of unstyled content. CSS custom properties handle all surface, text, border and input colours across every page. The Appearance section on the profile page lets each user switch modes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -456,6 +456,67 @@
|
||||
display: flex; justify-content: flex-end; background: #f8f8f8;
|
||||
}
|
||||
.mpick-none { padding: 10px; font-size: .82em; color: #aaa; text-align: center; }
|
||||
|
||||
/* ── Dark mode ── */
|
||||
html[data-theme="dark"] h1 { color: var(--text); }
|
||||
html[data-theme="dark"] .subtitle { color: var(--text-sec); }
|
||||
html[data-theme="dark"] .sidebar-nav a { color: var(--text-sec); }
|
||||
html[data-theme="dark"] .sidebar-nav a:hover { background: var(--surface-3); color: var(--link); }
|
||||
html[data-theme="dark"] .sidebar-nav a.active { background: #1a3255; color: #60a5fa; }
|
||||
html[data-theme="dark"] .sidebar-toggle { background: var(--surface-3); color: var(--text-sec); }
|
||||
html[data-theme="dark"] .sidebar-nav { background: var(--surface); }
|
||||
html[data-theme="dark"] .section { background: var(--surface); box-shadow: 0 1px 4px var(--shadow); }
|
||||
html[data-theme="dark"] .section-header { border-bottom-color: var(--border); }
|
||||
html[data-theme="dark"] .section-title { color: var(--text-2); }
|
||||
html[data-theme="dark"] .section-desc { color: var(--text-muted); }
|
||||
html[data-theme="dark"] .section-footer { border-top-color: var(--border-3); }
|
||||
html[data-theme="dark"] .field-row { border-bottom-color: var(--border-4); }
|
||||
html[data-theme="dark"] .field-label { color: var(--text-sec); }
|
||||
html[data-theme="dark"] .field-value { color: var(--text); }
|
||||
html[data-theme="dark"] .field-desc { color: var(--text-muted); }
|
||||
html[data-theme="dark"] .val-boolean.on { background: #0d2e17; color: #66bb6a; }
|
||||
html[data-theme="dark"] .val-boolean.off { background: #2e0d0d; color: #ef9a9a; }
|
||||
html[data-theme="dark"] .val-tag { background: #1a2d5a; color: #7aa8f0; }
|
||||
html[data-theme="dark"] .val-empty { color: var(--text-dim); }
|
||||
html[data-theme="dark"] .val-masked { color: var(--text-muted); }
|
||||
html[data-theme="dark"] .mini-table th { background: var(--surface-3); color: var(--text-sec); border-bottom-color: var(--border); }
|
||||
html[data-theme="dark"] .mini-table td { border-bottom-color: var(--border-3); color: var(--text); }
|
||||
html[data-theme="dark"] .mini-table tbody tr:hover { background: var(--surface-2); }
|
||||
html[data-theme="dark"] .badge-admin { background: #1a3255; color: #7aa8f0; }
|
||||
html[data-theme="dark"] .badge-user { background: var(--surface-3); color: var(--text-sec); }
|
||||
html[data-theme="dark"] .channel-card { border-color: var(--border); }
|
||||
html[data-theme="dark"] .channel-header { background: var(--surface-2); border-bottom-color: var(--border); }
|
||||
html[data-theme="dark"] .channel-name-text { color: var(--text); }
|
||||
html[data-theme="dark"] .channel-field { border-bottom-color: var(--border-4); }
|
||||
html[data-theme="dark"] .channel-field-label { color: var(--text-muted); }
|
||||
html[data-theme="dark"] .channel-field-value { color: var(--text); }
|
||||
html[data-theme="dark"] .thresh-cfg-card { border-color: var(--border); }
|
||||
html[data-theme="dark"] .thresh-cfg-header { background: var(--surface-2); border-bottom-color: var(--border); }
|
||||
html[data-theme="dark"] .thresh-cfg-name-label { color: #60a5fa; }
|
||||
html[data-theme="dark"] .crud-table th { background: var(--surface-3); color: var(--text-sec); border-bottom-color: var(--border); }
|
||||
html[data-theme="dark"] .crud-table td { border-bottom-color: var(--border-3); color: var(--text); }
|
||||
html[data-theme="dark"] .yaml-editor { background: var(--input-bg); border-color: var(--input-border); color: var(--text); }
|
||||
html[data-theme="dark"] .pending-banner { background: #2d2400; border-color: #a08020; }
|
||||
html[data-theme="dark"] .pending-banner .pending-msg { color: #e8c840; }
|
||||
html[data-theme="dark"] .modal-box,
|
||||
html[data-theme="dark"] .ch-modal-box { background: var(--surface); color: var(--text); }
|
||||
html[data-theme="dark"] .modal-box h3,
|
||||
html[data-theme="dark"] .ch-modal-box h3 { color: var(--text); }
|
||||
html[data-theme="dark"] .ch-form-row label { color: var(--text-sec); }
|
||||
html[data-theme="dark"] .ch-form-divider { color: var(--text-muted); border-top-color: var(--border); }
|
||||
html[data-theme="dark"] .backup-row { border-bottom-color: var(--border-3); }
|
||||
html[data-theme="dark"] .mpick-display { background: var(--input-bg); border-color: var(--input-border); }
|
||||
html[data-theme="dark"] .mpick-display:hover { border-color: var(--link); background: var(--surface-2); }
|
||||
html[data-theme="dark"] .mpick-tag { background: #1a2d5a; color: #7aa8f0; }
|
||||
html[data-theme="dark"] .mpick-more,
|
||||
html[data-theme="dark"] .mpick-empty { color: var(--text-muted); }
|
||||
html[data-theme="dark"] .mpick-panel { background: var(--surface); border-color: var(--border); }
|
||||
html[data-theme="dark"] .mpick-panel-header { background: var(--surface-3); color: var(--text-sec); border-bottom-color: var(--border); }
|
||||
html[data-theme="dark"] .mpick-item { border-bottom-color: var(--border-4); color: var(--text); }
|
||||
html[data-theme="dark"] .mpick-item-avail:hover { background: #0d2e17; }
|
||||
html[data-theme="dark"] .mpick-item-sel:hover { background: #2e0d0d; }
|
||||
html[data-theme="dark"] .mpick-panel-footer { background: var(--surface-2); border-top-color: var(--border); }
|
||||
html[data-theme="dark"] .mpick-none { color: var(--text-dim); }
|
||||
</style>
|
||||
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user