From 4fb21fe01d311e02f5fac113a3079e6722eebc56 Mon Sep 17 00:00:00 2001 From: Twarner491 Date: Fri, 5 Jun 2026 16:34:08 -0700 Subject: [PATCH] [FEAT] theme: light/charcoal dark mode with a persistent switcher --- avian/frontend/apt.js | 29 ++++++++++++++++++++++++++++- avian/frontend/index.html | 3 +++ avian/frontend/styles.css | 34 ++++++++++++++++++++++++++++++---- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/avian/frontend/apt.js b/avian/frontend/apt.js index 4017aef..d61bdb4 100644 --- a/avian/frontend/apt.js +++ b/avian/frontend/apt.js @@ -1517,6 +1517,20 @@ + '
' + btns + '
' + ''; } + // Client-side theme switcher row. Reuses the .seg look but is tagged + // data-theme-seg so wireSettingsControls skips it - it applies instantly + // and is NOT part of the Pi config save flow. + function themeRow() { + var cur = currentTheme(); + var btn = function (v, label) { + return ''; + }; + return '' + + ''; + } function wireSettingsControls(scope) { scope = scope || document; scope.querySelectorAll('.switch').forEach(function (sw) { @@ -1537,7 +1551,7 @@ setSaveState('change pending'); }); }); - scope.querySelectorAll('.seg').forEach(function (seg) { + scope.querySelectorAll('.seg:not([data-theme-seg])').forEach(function (seg) { seg.querySelectorAll('button').forEach(function (b) { b.addEventListener('click', function () { seg.querySelectorAll('button').forEach(function (x) { x.setAttribute('aria-current', x === b ? 'true' : 'false'); }); @@ -2053,6 +2067,7 @@ var preserve = cfg.preserve; adminBody.innerHTML = '
' + + themeRow() + settingsToggle('preserve', 'Preserve all recordings', "don't auto-delete", preserve) + settingsSlider('CONFIDENCE', 'Confidence threshold', 'min score to log a detection', v.CONFIDENCE, 0.1, 0.95, 0.05, 2) + settingsSlider('SENSITIVITY', 'Sensitivity', 'analyzer sensitivity', v.SENSITIVITY, 0.5, 1.5, 0.05, 2) @@ -2067,6 +2082,18 @@ + '
' + ''; wireSettingsControls(adminBody); + adminBody.querySelectorAll('.seg').forEach(wireToggleAdvance); // open-space advance + // Theme switcher applies + persists immediately (separate from the + // Pi config save below). + var themeSeg = adminBody.querySelector('[data-theme-seg]'); + if (themeSeg) themeSeg.addEventListener('click', function (ev) { + var b = ev.target.closest('button[data-theme]'); + if (!b) return; + applyTheme(b.getAttribute('data-theme')); + [].forEach.call(themeSeg.querySelectorAll('button'), function (x) { + x.setAttribute('aria-current', x === b ? 'true' : 'false'); + }); + }); var saveBtn = document.getElementById('saveBtn'); if (saveBtn) saveBtn.addEventListener('click', saveSettings); }) diff --git a/avian/frontend/index.html b/avian/frontend/index.html index 9246ffb..e175704 100644 --- a/avian/frontend/index.html +++ b/avian/frontend/index.html @@ -7,6 +7,9 @@ + + diff --git a/avian/frontend/styles.css b/avian/frontend/styles.css index dd46c3c..8db083d 100644 --- a/avian/frontend/styles.css +++ b/avian/frontend/styles.css @@ -8,12 +8,38 @@ --ink-soft: #908576; --accent: #4a3f31; /* warm dark grey - matches ink-2, no longer red */ --accent-2: #1a1612; /* near-black ink - same hue as the title text */ + --hairline: rgba(26,22,18,0.14); /* faint divider lines + chart gridlines */ + --pill: #fcfcfb; /* active-toggle sliding pill */ /* Depth shadow recipes - replace flat 1px borders */ --edge: inset 0 0 0 1px rgba(255,255,255,0.55), 0 0 0 1px rgba(26,22,18,0.04); --edge-lg: inset 0 0 0 1px rgba(255,255,255,0.6), 0 0 0 1px rgba(26,22,18,0.06), 0 8px 28px rgba(26,22,18,0.06); --recess: inset 0 1px 2px rgba(26,22,18,0.06), inset 0 0 0 1px rgba(26,22,18,0.04); --raised: inset 0 0 0 1px rgba(255,255,255,0.8), 0 0 0 1px rgba(26,22,18,0.05), 0 1px 2px rgba(26,22,18,0.07); } + /* ---- Charcoal dark theme. Set via data-theme="dark" on (an + inline script in index.html applies the saved choice before paint; + the switcher lives in Settings). The whole UI is variable-driven, so + flipping ink<->paper inverts cleanly: text reads light, the stats + squares become light marks on dark, the collage cutouts (transparent + PNGs) sit on the charcoal directly. Shadow recipes are re-cut for a + dark surface (faint light edges, deeper drop shadows). ---- */ + :root[data-theme="dark"] { + color-scheme: dark; + --paper: #17181c; /* charcoal page bg */ + --paper-2: #212329; /* recessed track */ + --paper-3: #2c2f36; /* placeholder fill */ + --ink: #ece8e1; /* warm off-white text / marks */ + --ink-2: #b7afa2; /* secondary text */ + --ink-soft: #837c70; /* muted text */ + --accent: #d8d2c6; /* light warm - pills read light-on-dark */ + --accent-2: #ece8e1; + --hairline: rgba(236,232,225,0.20); /* faint light divider on charcoal */ + --pill: #3a3e48; /* raised, clearly lighter than the track */ + --edge: inset 0 0 0 1px rgba(255,255,255,0.05), 0 0 0 1px rgba(0,0,0,0.4); + --edge-lg: inset 0 0 0 1px rgba(255,255,255,0.06), 0 0 0 1px rgba(0,0,0,0.5), 0 10px 30px rgba(0,0,0,0.5); + --recess: inset 0 1px 2px rgba(0,0,0,0.5), inset 0 0 0 1px rgba(255,255,255,0.04); + --raised: inset 0 0 0 1px rgba(255,255,255,0.07), 0 0 0 1px rgba(0,0,0,0.4), 0 1px 2px rgba(0,0,0,0.5); + } * { box-sizing: border-box; } html, body { margin: 0; padding: 0; height: 100%; background: var(--paper); color: var(--ink); } body { @@ -144,7 +170,7 @@ raised-paper look; buttons just toggle their text colour. */ .seg-pill { position: absolute; top: 4px; bottom: 4px; left: 0; - background: var(--paper); border-radius: 999px; + background: var(--pill); border-radius: 999px; box-shadow: var(--raised); transition: transform 320ms cubic-bezier(.7,.05,.2,1), width 320ms cubic-bezier(.7,.05,.2,1); will-change: transform, width; @@ -280,7 +306,7 @@ .switch::after { content: ''; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; - background: var(--paper); border-radius: 999px; + background: var(--pill); border-radius: 999px; box-shadow: var(--raised); transition: transform 220ms cubic-bezier(.7,.05,.2,1), background 200ms ease; } @@ -289,7 +315,7 @@ } .switch[aria-checked="true"]::after { transform: translateX(18px); - background: var(--paper); + background: var(--pill); } /* Slider - title row (label + value badge with breathing room) and @@ -361,7 +387,7 @@ } .seg button[aria-current="true"] { color: var(--ink); - background: var(--paper); + background: var(--pill); box-shadow: var(--raised); }