The palette already lived in CSS custom properties, so dark mode is a token-override block in the new theme.css, applied via data-theme on the document element. A moon/sun toggle sits top right in the navbar; an explicit choice persists in localStorage, otherwise the app follows the OS preference live. An inline head script applies the theme before first paint so a dark reload never flashes light. Token hygiene sweep alongside: literal colours that broke under a dark palette (timeline chip text, color-mix against white, calendar stripes and bands, warning banners, nav background) moved into ten new tokens with light-mode values unchanged. The flip clock keeps its fixed dark look by design; the map keeps standard light tiles in both themes.
76 lines
2.3 KiB
CSS
76 lines
2.3 KiB
CSS
/* Dark/light theme switcher. See docs/API.md "Theme switcher (dark/light,
|
|
frontend-only)". document.documentElement carries data-theme="dark" or
|
|
"light" (set by an inline script in index.html's <head>, before first
|
|
paint). Token names/values mirror styles.css :root — every light token
|
|
defined there (and the hygiene tokens added alongside it, plus --now from
|
|
timeline.css) gets a dark counterpart here. Attribute-selector overrides
|
|
win on specificity regardless of stylesheet link order. */
|
|
|
|
:root[data-theme="light"] {
|
|
color-scheme: light;
|
|
}
|
|
|
|
:root[data-theme="dark"] {
|
|
color-scheme: dark;
|
|
|
|
--brand: #3b82f6;
|
|
--brand-dark: #60a5fa;
|
|
--brand-soft: rgba(59, 130, 246, 0.18);
|
|
--accent: #38bdf8;
|
|
|
|
--bg: #0f172a;
|
|
--surface: #1e293b;
|
|
--surface-2: #253147;
|
|
--border: #334155;
|
|
--border-strong: #475569;
|
|
|
|
--text: #e2e8f0;
|
|
--text-muted: #94a3b8;
|
|
--text-faint: #64748b;
|
|
|
|
--danger: #f87171;
|
|
--danger-soft: rgba(248, 113, 113, 0.16);
|
|
--success: #34d399;
|
|
--warn: #fbbf24;
|
|
|
|
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4), 0 1px 3px rgba(0, 0, 0, 0.35);
|
|
--shadow-md: 0 4px 14px rgba(0, 0, 0, 0.45);
|
|
--shadow-lg: 0 18px 48px rgba(0, 0, 0, 0.55);
|
|
|
|
/* Theme-hygiene tokens (light values in styles.css) */
|
|
--chip-text: #e2e8f0;
|
|
--nav-bg: rgba(15, 23, 42, 0.85);
|
|
--auth-tint: #1c2b4d;
|
|
--stripe-a: #1a2438;
|
|
--stripe-b: #17202f;
|
|
--warn-soft-bg: rgba(251, 191, 36, 0.14);
|
|
--warn-soft-border: rgba(251, 191, 36, 0.35);
|
|
--warn-soft-text: #fbbf24;
|
|
--role-owner-bg: rgba(251, 191, 36, 0.18);
|
|
--chip-price-bg: rgba(255, 255, 255, 0.12);
|
|
|
|
/* timeline.css defines --now on plain :root; override it here too. */
|
|
--now: #f87171;
|
|
}
|
|
|
|
/* ---------- Theme toggle button (top-right of navbar) ---------- */
|
|
.theme-toggle {
|
|
border: none;
|
|
background: var(--surface-2);
|
|
color: var(--text-muted);
|
|
width: 2.1rem;
|
|
height: 2.1rem;
|
|
flex-shrink: 0;
|
|
border-radius: 50%;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.05rem;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
transition: background 0.15s, color 0.15s, transform 0.1s;
|
|
}
|
|
.theme-toggle:hover { background: var(--border); color: var(--text); }
|
|
.theme-toggle:active { transform: scale(0.94); }
|
|
.theme-toggle:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
|