From b20fe5b91eb3d6811efecae99a36de140e012c41 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Mon, 1 Jun 2026 21:45:25 -0400 Subject: [PATCH] =?UTF-8?q?zebra-report:=20dark/light=20invert-everything?= =?UTF-8?q?=20toggle=20=E2=80=94=20mirror=20source?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zebra-report/zebra-spaces.html | 49 ++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/zebra-report/zebra-spaces.html b/zebra-report/zebra-spaces.html index 9df1f42..170dfc7 100644 --- a/zebra-report/zebra-spaces.html +++ b/zebra-report/zebra-spaces.html @@ -16,6 +16,24 @@ font-family: monospace; background: #fff; color: #000; padding: 1rem 1.5rem; max-width: none; margin: 0 auto; } + /* Dark mode = literal invert. CSS filter flips every painted colour on + * the page in one shot, then hue-rotate corrects what would otherwise + * read as cyan instead of orange / magenta instead of green. Re-invert + * the things we want to look RIGHT instead of merely opposite: video + * tiles, the QR canvas, and any inline raster. */ + html.theme-dark body { filter: invert(1) hue-rotate(180deg); background: #fff; } + html.theme-dark video, + html.theme-dark canvas, + html.theme-dark img { filter: invert(1) hue-rotate(180deg); } + /* the theme toggle button itself sits inside the inverted area, so + * its own colours flip automatically — no extra rule needed */ + .theme-toggle { + position: fixed; top: 0.6rem; right: 0.8rem; z-index: 9999; + background: #fff; color: #000; border: 1px solid #000; + padding: 0.2rem 0.55rem; font-family: monospace; font-size: 0.7rem; + cursor: pointer; line-height: 1; + } + .theme-toggle:hover { background: #f0f0f0; } /* two-column page on desktop: left = timeline (1fr, takes remaining * space — hosts screen shares + the running event feed), right = * controls (kept narrow so the screen share gets maximum pixels). @@ -233,8 +251,18 @@ .mod-actions { padding-left: 5.55rem; } } + +
@@ -433,6 +461,23 @@ const HANDLE_KEY = 'zebra-spaces-handle-v1'; const MUSIC_MODE_KEY = 'zebra-spaces-music-mode-v1'; const MIC_DEV_KEY = 'zebra-spaces-mic-device-v1'; const CAM_DEV_KEY = 'zebra-spaces-cam-device-v1'; +const THEME_KEY = 'zebra-spaces-theme-v1'; + +/* theme toggle. The class is already applied pre-paint by the head + * script, so this just wires the click + keeps the button label in + * sync with the current state. */ +function applyThemeLabel(){ + const dark = document.documentElement.classList.contains('theme-dark'); + const btn = $('btn-theme'); if (btn) btn.textContent = dark ? 'light' : 'dark'; +} +applyThemeLabel(); +$('btn-theme').addEventListener('click', () => { + const root = document.documentElement; + const goDark = !root.classList.contains('theme-dark'); + root.classList.toggle('theme-dark', goDark); + try { localStorage.setItem(THEME_KEY, goDark ? 'dark' : 'light'); } catch(_){} + applyThemeLabel(); +}); const VAULT_PREFIX = 'zspc-id-v1|'; const PBKDF2_ITER = 600000; @@ -2073,8 +2118,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');