Theme: add System (OS-follow) option; fix light-theme playhead ring (was white-on-white)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Me Here 2026-05-24 17:10:54 -05:00
parent a7770eaf47
commit c087f11637

View file

@ -5,13 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Stackable Metronome — Mockup</title> <title>Stackable Metronome — Mockup</title>
<script> <script>
// Set theme before first paint (avoids a flash). Stored choice wins; else // Set theme before first paint (avoids a flash). Preference is system|light|dark
// follow the OS preference. // (default system → follows the OS); "system" resolves to the OS scheme here.
(function () { (function () {
try { try {
var t = localStorage.getItem("metronome.theme"); var p = localStorage.getItem("metronome.theme");
if (t !== "light" && t !== "dark") t = matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"; if (p !== "light" && p !== "dark" && p !== "system") p = "system";
document.documentElement.dataset.theme = t; var eff = p === "system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : p;
document.documentElement.dataset.theme = eff;
} catch (e) { document.documentElement.dataset.theme = "dark"; } } catch (e) { document.documentElement.dataset.theme = "dark"; }
})(); })();
</script> </script>
@ -30,11 +31,11 @@
<style> <style>
:root { :root {
--bg:#14171c; --bg2:#1b212b; --panel:#1d222b; --panel-2:#242b36; --edge:#333d4b; --bg:#14171c; --bg2:#1b212b; --panel:#1d222b; --panel-2:#242b36; --edge:#333d4b;
--txt:#c7d0db; --muted:#7f8b9a; --hot:#ffd166; --led-off:#2b323d; --txt:#c7d0db; --muted:#7f8b9a; --hot:#ffd166; --led-off:#2b323d; --ring:#ffffff;
} }
:root[data-theme="light"] { :root[data-theme="light"] {
--bg:#e9edf2; --bg2:#f6f8fb; --panel:#ffffff; --panel-2:#eef2f7; --edge:#d2dae4; --bg:#e9edf2; --bg2:#f6f8fb; --panel:#ffffff; --panel-2:#eef2f7; --edge:#d2dae4;
--txt:#1e2630; --muted:#5c6776; --hot:#a9760a; --led-off:#dbe2ea; --txt:#1e2630; --muted:#5c6776; --hot:#a9760a; --led-off:#cdd6e0; --ring:#16202c;
} }
* { box-sizing: border-box; } * { box-sizing: border-box; }
body { body {
@ -77,7 +78,7 @@
.led.on { background:var(--lc,#888); box-shadow:0 0 8px var(--lc); color:rgba(0,0,0,.55); } .led.on { background:var(--lc,#888); box-shadow:0 0 8px var(--lc); color:rgba(0,0,0,.55); }
.led.accent { box-shadow:0 0 4px #fff, 0 0 14px var(--lc); } .led.accent { box-shadow:0 0 4px #fff, 0 0 14px var(--lc); }
.led.accent::after { content:"▲"; position:absolute; top:-1px; font-size:7px; color:#fff; } .led.accent::after { content:"▲"; position:absolute; top:-1px; font-size:7px; color:#fff; }
.led.playhead { outline:2px solid #fff; outline-offset:1px; } .led.playhead { outline:2px solid var(--ring); outline-offset:1px; }
.led.groupstart { margin-left:16px; } .led.groupstart { margin-left:16px; }
.led.groupstart::before { content:""; position:absolute; left:-9px; top:4px; bottom:4px; width:2px; background:var(--muted); } .led.groupstart::before { content:""; position:absolute; left:-9px; top:4px; bottom:4px; width:2px; background:var(--muted); }
/* meter lanes — compact single-row controls + strip */ /* meter lanes — compact single-row controls + strip */
@ -800,13 +801,18 @@ function syncStartBtn() {
else { startBtn.textContent = "▶ Start"; startBtn.classList.remove("on"); } else { startBtn.textContent = "▶ Start"; startBtn.classList.remove("on"); }
} }
function toggleShortcuts(show) { const o = $("shortcutsOverlay"); o.hidden = (show === undefined) ? !o.hidden : !show; } function toggleShortcuts(show) { const o = $("shortcutsOverlay"); o.hidden = (show === undefined) ? !o.hidden : !show; }
function applyTheme(t) { const THEMES = ["system", "light", "dark"];
document.documentElement.dataset.theme = t; function effectiveTheme(pref) { return pref === "system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : pref; }
try { localStorage.setItem("metronome.theme", t); } catch (e) {} function themePref() { try { const p = localStorage.getItem("metronome.theme"); return (p === "light" || p === "dark" || p === "system") ? p : "system"; } catch (e) { return "system"; } }
$("themeBtn").textContent = t === "light" ? "🌙" : "☀"; // icon = theme you'd switch TO function applyTheme(pref) {
try { localStorage.setItem("metronome.theme", pref); } catch (e) {}
document.documentElement.dataset.theme = effectiveTheme(pref);
$("themeBtn").textContent = pref === "system" ? "🖥" : pref === "light" ? "☀" : "🌙";
$("themeBtn").title = "Theme: " + pref + " (click to cycle: system → light → dark)";
} }
$("themeBtn").addEventListener("click", () => applyTheme(document.documentElement.dataset.theme === "light" ? "dark" : "light")); $("themeBtn").addEventListener("click", () => applyTheme(THEMES[(THEMES.indexOf(themePref()) + 1) % THEMES.length]));
applyTheme(document.documentElement.dataset.theme === "light" ? "light" : "dark"); matchMedia("(prefers-color-scheme: light)").addEventListener("change", () => { if (themePref() === "system") applyTheme("system"); });
applyTheme(themePref());
$("startBtn").addEventListener("click", () => toggleTransport()); $("startBtn").addEventListener("click", () => toggleTransport());
let _taps = []; let _taps = [];
function tapTempo() { function tapTempo() {