From c154ea86a2e5243cd8fbedca7ce5ae3e37bbf493 Mon Sep 17 00:00:00 2001 From: jessikitty Date: Thu, 21 May 2026 10:12:47 +1000 Subject: [PATCH] feat: Light/dark mode CSS with theme toggle support --- wwwroot/css/admin.css | 131 +++++++++++++++++++++++++++++++++--------- 1 file changed, 104 insertions(+), 27 deletions(-) diff --git a/wwwroot/css/admin.css b/wwwroot/css/admin.css index 6986648..71c781e 100644 --- a/wwwroot/css/admin.css +++ b/wwwroot/css/admin.css @@ -1,67 +1,144 @@ /* === Scratching Post Admin — Sunbeam Theme === */ -:root { - --sb-bg: #0f0f1a; --sb-surface: #1a1a2e; --sb-surface2: #232340; - --sb-border: rgba(255, 255, 255, 0.08); --sb-text: #e0e0e0; --sb-text-muted: #8888aa; - --sb-accent: #f0a030; --sb-accent2: #ff6b6b; --sb-primary: #5b8def; --sidebar-width: 240px; +/* Dark mode (default) */ +:root, [data-theme="dark"] { + --sb-bg: #0f0f1a; + --sb-surface: #1a1a2e; + --sb-surface2: #232340; + --sb-border: rgba(255, 255, 255, 0.08); + --sb-text: #e0e0e0; + --sb-text-muted: #8888aa; + --sb-accent: #f0a030; + --sb-accent2: #ff6b6b; + --sb-primary: #5b8def; + --sb-btn-text: #fff; + --sb-input-bg: #0f0f1a; + --sb-card-header-bg: rgba(255, 255, 255, 0.02); + --sb-hover-bg: rgba(255, 255, 255, 0.04); + --sb-active-bg: rgba(240, 160, 48, 0.08); + --sidebar-width: 240px; } + +/* Light mode */ +[data-theme="light"] { + --sb-bg: #f4f6f9; + --sb-surface: #ffffff; + --sb-surface2: #e9ecf0; + --sb-border: rgba(0, 0, 0, 0.1); + --sb-text: #1a1a2e; + --sb-text-muted: #6b7280; + --sb-accent: #d48806; + --sb-accent2: #e04040; + --sb-primary: #3b6fd4; + --sb-btn-text: #fff; + --sb-input-bg: #ffffff; + --sb-card-header-bg: rgba(0, 0, 0, 0.02); + --sb-hover-bg: rgba(0, 0, 0, 0.03); + --sb-active-bg: rgba(212, 136, 6, 0.08); +} + * { box-sizing: border-box; } -body { margin: 0; font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, sans-serif; background: var(--sb-bg); color: var(--sb-text); min-height: 100vh; } +body { margin: 0; font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, sans-serif; background: var(--sb-bg); color: var(--sb-text); min-height: 100vh; transition: background 0.3s, color 0.3s; } + +/* === Layout === */ .admin-wrapper { display: flex; min-height: 100vh; } -.admin-sidebar { width: var(--sidebar-width); background: var(--sb-surface); border-right: 1px solid var(--sb-border); display: flex; flex-direction: column; position: fixed; top: 0; left: 0; bottom: 0; z-index: 100; } + +/* === Sidebar === */ +.admin-sidebar { width: var(--sidebar-width); background: var(--sb-surface); border-right: 1px solid var(--sb-border); display: flex; flex-direction: column; position: fixed; top: 0; left: 0; bottom: 0; z-index: 100; transition: background 0.3s; } .sidebar-brand { padding: 1.5em 1.2em 0.3em; font-size: 1.4em; font-weight: 700; color: var(--sb-accent); display: flex; align-items: center; gap: 0.5em; } .sidebar-brand i { font-size: 1.2em; } .sidebar-subtitle { padding: 0 1.2em 1.2em; font-size: 0.75em; color: var(--sb-text-muted); text-transform: uppercase; letter-spacing: 0.1em; } .sidebar-nav { list-style: none; padding: 0; margin: 0; flex: 1; } .sidebar-nav li a { display: flex; align-items: center; gap: 0.75em; padding: 0.8em 1.2em; color: var(--sb-text-muted); text-decoration: none; border-left: 3px solid transparent; transition: all 0.15s ease; font-size: 0.95em; } -.sidebar-nav li a:hover { background: rgba(255, 255, 255, 0.04); color: var(--sb-text); } -.sidebar-nav li a.active { background: rgba(240, 160, 48, 0.08); color: var(--sb-accent); border-left-color: var(--sb-accent); } +.sidebar-nav li a:hover { background: var(--sb-hover-bg); color: var(--sb-text); } +.sidebar-nav li a.active { background: var(--sb-active-bg); color: var(--sb-accent); border-left-color: var(--sb-accent); } .sidebar-nav li a i { font-size: 1.1em; width: 1.2em; text-align: center; } .nav-hint { margin-left: auto; font-size: 0.75em; opacity: 0.5; } -.sidebar-footer { padding: 1em 1.2em; border-top: 1px solid var(--sb-border); color: var(--sb-text-muted); font-size: 0.75em; } +.sidebar-footer { padding: 1em 1.2em; border-top: 1px solid var(--sb-border); color: var(--sb-text-muted); font-size: 0.75em; display: flex; align-items: center; justify-content: space-between; } + +/* === Theme Toggle === */ +.theme-toggle { background: none; border: 1px solid var(--sb-border); color: var(--sb-text-muted); border-radius: 6px; padding: 0.3em 0.5em; cursor: pointer; font-size: 1em; transition: all 0.2s; display: flex; align-items: center; } +.theme-toggle:hover { color: var(--sb-accent); border-color: var(--sb-accent); } + +/* === Main Content === */ .admin-main { margin-left: var(--sidebar-width); flex: 1; display: flex; flex-direction: column; min-height: 100vh; } .admin-header { padding: 1.5em 2em 1em; border-bottom: 1px solid var(--sb-border); display: flex; justify-content: space-between; align-items: center; } .admin-header h1 { font-size: 1.5em; font-weight: 600; margin: 0; } .admin-content { padding: 1.5em 2em 3em; flex: 1; } -.card { background: var(--sb-surface); border: 1px solid var(--sb-border); border-radius: 10px; color: var(--sb-text); } -.card-header { background: rgba(255, 255, 255, 0.02); border-bottom: 1px solid var(--sb-border); padding: 0.9em 1.2em; } + +/* === Cards === */ +.card { background: var(--sb-surface); border: 1px solid var(--sb-border); border-radius: 10px; color: var(--sb-text); transition: background 0.3s; } +.card-header { background: var(--sb-card-header-bg); border-bottom: 1px solid var(--sb-border); padding: 0.9em 1.2em; } .card-body { padding: 1.2em; } -.card-footer { background: rgba(255, 255, 255, 0.02); border-top: 1px solid var(--sb-border); padding: 0.8em 1.2em; } -.stat-card { background: var(--sb-surface); border: 1px solid var(--sb-border); border-radius: 12px; padding: 1.5em; display: flex; align-items: center; gap: 1.2em; transition: transform 0.2s; } +.card-footer { background: var(--sb-card-header-bg); border-top: 1px solid var(--sb-border); padding: 0.8em 1.2em; } + +/* === Stat Cards === */ +.stat-card { background: var(--sb-surface); border: 1px solid var(--sb-border); border-radius: 12px; padding: 1.5em; display: flex; align-items: center; gap: 1.2em; transition: transform 0.2s, background 0.3s; } .stat-card:hover { transform: translateY(-2px); } -.stat-card.accent { border-color: var(--sb-accent); background: rgba(240, 160, 48, 0.06); } +.stat-card.accent { border-color: var(--sb-accent); background: var(--sb-active-bg); } .stat-icon { font-size: 2em; color: var(--sb-accent); width: 1.5em; text-align: center; } .stat-value { font-size: 1.8em; font-weight: 700; line-height: 1; } .stat-label { color: var(--sb-text-muted); font-size: 0.85em; margin-top: 0.2em; } -.table { color: var(--sb-text); --bs-table-bg: transparent; --bs-table-hover-bg: rgba(255, 255, 255, 0.03); } + +/* === Tables === */ +.table { color: var(--sb-text); --bs-table-bg: transparent; --bs-table-hover-bg: var(--sb-hover-bg); } .table thead th { border-bottom-color: var(--sb-border); color: var(--sb-text-muted); font-weight: 500; font-size: 0.85em; text-transform: uppercase; letter-spacing: 0.05em; } .table td { border-color: var(--sb-border); } -.form-control, .form-select { background: var(--sb-bg); border-color: var(--sb-border); color: var(--sb-text); } -.form-control:focus, .form-select:focus { background: var(--sb-bg); border-color: var(--sb-accent); color: var(--sb-text); box-shadow: 0 0 0 0.2rem rgba(240, 160, 48, 0.15); } + +/* === Forms === */ +.form-control, .form-select { background: var(--sb-input-bg); border-color: var(--sb-border); color: var(--sb-text); } +.form-control:focus, .form-select:focus { background: var(--sb-input-bg); border-color: var(--sb-accent); color: var(--sb-text); box-shadow: 0 0 0 0.2rem rgba(240, 160, 48, 0.15); } .form-label { font-weight: 500; font-size: 0.9em; } .form-text { color: var(--sb-text-muted); } .input-group-text { background: var(--sb-surface2); border-color: var(--sb-border); color: var(--sb-text-muted); } +.form-check-input { background-color: var(--sb-input-bg); border-color: var(--sb-border); } .form-check-input:checked { background-color: var(--sb-accent); border-color: var(--sb-accent); } -.btn-primary { background: var(--sb-primary); border-color: var(--sb-primary); } -.btn-primary:hover { background: #4a7ddf; border-color: #4a7ddf; } + +/* === Buttons === */ +.btn-primary { background: var(--sb-primary); border-color: var(--sb-primary); color: var(--sb-btn-text); } +.btn-primary:hover { filter: brightness(0.9); } .btn-outline-secondary { color: var(--sb-text-muted); border-color: var(--sb-border); } -.btn-outline-secondary:hover { background: rgba(255, 255, 255, 0.05); color: var(--sb-text); border-color: var(--sb-text-muted); } +.btn-outline-secondary:hover { background: var(--sb-hover-bg); color: var(--sb-text); border-color: var(--sb-text-muted); } + +/* === List Groups === */ .list-group-item { background: transparent; border-color: var(--sb-border); color: var(--sb-text); } -.list-group-item:hover { background: rgba(255, 255, 255, 0.02); } -.alert-success { background: rgba(40, 167, 69, 0.15); border-color: rgba(40, 167, 69, 0.3); color: #6bdb8a; } -.alert-danger { background: rgba(220, 53, 69, 0.15); border-color: rgba(220, 53, 69, 0.3); color: #f08090; } -.alert-warning { background: rgba(255, 193, 7, 0.15); border-color: rgba(255, 193, 7, 0.3); color: #ffd54f; } -.badge { font-weight: 500; } .badge-sm { font-size: 0.7em; padding: 0.2em 0.5em; } +.list-group-item:hover { background: var(--sb-hover-bg); } + +/* === Alerts === */ +.alert-success { background: rgba(40, 167, 69, 0.12); border-color: rgba(40, 167, 69, 0.25); color: #28a745; } +.alert-danger { background: rgba(220, 53, 69, 0.12); border-color: rgba(220, 53, 69, 0.25); color: #dc3545; } +.alert-warning { background: rgba(255, 193, 7, 0.12); border-color: rgba(255, 193, 7, 0.25); color: #d48806; } +[data-theme="dark"] .alert-success { color: #6bdb8a; } +[data-theme="dark"] .alert-danger { color: #f08090; } +[data-theme="dark"] .alert-warning { color: #ffd54f; } + +/* === Badges === */ +.badge { font-weight: 500; } +.badge-sm { font-size: 0.7em; padding: 0.2em 0.5em; } + +/* === Playlist Items === */ .playlist-item { transition: background 0.15s; } -.playlist-item.sortable-ghost { background: rgba(240, 160, 48, 0.1); opacity: 0.6; } +.playlist-item.sortable-ghost { background: var(--sb-active-bg); opacity: 0.6; } .drag-handle { cursor: grab; color: var(--sb-text-muted); font-size: 1.2em; padding: 0.2em; } .drag-handle:hover { color: var(--sb-accent); } .duration-input input { text-align: center; } + +/* === Device Cards === */ .device-card { transition: transform 0.2s, border-color 0.2s; } .device-card:hover { transform: translateY(-2px); border-color: var(--sb-accent); } + +/* === Empty State === */ .empty-state { text-align: center; padding: 4em 2em; color: var(--sb-text-muted); } + +/* === Code in light mode === */ +[data-theme="light"] code { background: rgba(0,0,0,0.06); padding: 0.15em 0.4em; border-radius: 3px; font-size: 0.9em; } + +/* === Modal overrides === */ +.modal-content { background: var(--sb-surface); color: var(--sb-text); border-color: var(--sb-border); } + +/* === Responsive === */ @media (max-width: 768px) { .admin-sidebar { width: 60px; overflow: hidden; } - .admin-sidebar span, .admin-sidebar .sidebar-subtitle, .admin-sidebar .sidebar-footer, .admin-sidebar .nav-hint { display: none; } + .admin-sidebar span, .admin-sidebar .sidebar-subtitle, .admin-sidebar .sidebar-footer small, .admin-sidebar .nav-hint { display: none; } .sidebar-brand { justify-content: center; padding: 1em 0.5em; } .sidebar-nav li a { justify-content: center; padding: 0.8em; } .admin-main { margin-left: 60px; }