Light theme

This commit is contained in:
2026-03-01 11:27:23 +01:00
parent 424efcd1ca
commit 78484efe39

View File

@@ -20,14 +20,98 @@
--label: #5a6480;
--mono: 'Share Tech Mono', monospace;
--sans: 'Barlow Condensed', sans-serif;
--logo-color: #ffffff;
/* Component tokens */
--header-bg: rgba(10,12,14,0.96);
--scanline: rgba(0,0,0,0.07);
--hover-desc-color: rgba(10,12,14,0.65);
/* Canvas */
--canvas-bg: #0d1013;
--canvas-fade: rgba(13,16,19,0.25);
--canvas-grid: rgba(31,36,40,0.6);
--canvas-center: rgba(58,64,80,0.5);
--canvas-idle-text: rgba(90,100,128,0.7);
--trail-rgb: 0,229,255;
--trail-idle-rgb: 58,64,80;
--canvas-dot: #00e5ff;
--canvas-dot-idle: #3a4050;
--canvas-dot-glow: rgba(0,229,255,0.35);
/* Tap flashes */
--tap-left: rgba(0,229,255,0.35);
--tap-right: rgba(255,61,113,0.35);
}
/* ── Light theme (explicit) ──────────────────────────────────────────────── */
:root.theme-light {
--bg: #f0f2f5;
--panel: #ffffff;
--panel2: #e8eaed;
--border: #b8bec8;
--accent: #006699;
--accent2: #c01a50;
--warn: #b36a00;
--ok: #007040;
--dim: #848ea4;
--text: #0f1118;
--label: #4e566e;
--logo-color: #0f1118;
--header-bg: rgba(240,242,245,0.96);
--scanline: rgba(0,0,0,0.03);
--hover-desc-color: rgba(240,242,245,0.7);
--canvas-bg: #e8eaed;
--canvas-fade: rgba(232,234,237,0.35);
--canvas-grid: rgba(195,200,210,0.7);
--canvas-center: rgba(155,162,178,0.6);
--canvas-idle-text: rgba(100,112,140,0.8);
--trail-rgb: 0,100,170;
--trail-idle-rgb: 155,162,178;
--canvas-dot: #0077aa;
--canvas-dot-idle: #a8b0c0;
--canvas-dot-glow: rgba(0,100,170,0.3);
--tap-left: rgba(0,100,170,0.35);
--tap-right: rgba(200,30,80,0.35);
}
/* ── Auto light (OS hint; explicit class overrides) ──────────────────────── */
@media (prefers-color-scheme: light) {
:root:not(.theme-dark) {
--bg: #f0f2f5;
--panel: #ffffff;
--panel2: #e8eaed;
--border: #b8bec8;
--accent: #006699;
--accent2: #c01a50;
--warn: #b36a00;
--ok: #007040;
--dim: #848ea4;
--text: #0f1118;
--label: #4e566e;
--logo-color: #0f1118;
--header-bg: rgba(240,242,245,0.96);
--scanline: rgba(0,0,0,0.03);
--hover-desc-color: rgba(240,242,245,0.7);
--canvas-bg: #e8eaed;
--canvas-fade: rgba(232,234,237,0.35);
--canvas-grid: rgba(195,200,210,0.7);
--canvas-center: rgba(155,162,178,0.6);
--canvas-idle-text: rgba(100,112,140,0.8);
--trail-rgb: 0,100,170;
--trail-idle-rgb: 155,162,178;
--canvas-dot: #0077aa;
--canvas-dot-idle: #a8b0c0;
--canvas-dot-glow: rgba(0,100,170,0.3);
--tap-left: rgba(0,100,170,0.35);
--tap-right: rgba(200,30,80,0.35);
}
}
* { box-sizing:border-box; margin:0; padding:0; }
body { background:var(--bg); color:var(--text); font-family:var(--mono); min-height:100vh; overflow-x:hidden; }
body::before { content:''; position:fixed; inset:0; pointer-events:none; z-index:9999;
background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(0,0,0,0.07) 2px,rgba(0,0,0,0.07) 4px); }
background:repeating-linear-gradient(0deg,transparent,transparent 2px,var(--scanline) 2px,var(--scanline) 4px); }
header { border-bottom:1px solid var(--border); padding:16px 28px; display:flex; align-items:center; gap:20px; position:sticky; top:0; background:rgba(10,12,14,0.96); backdrop-filter:blur(10px); z-index:100; }
.logo { font-family:var(--sans); font-weight:900; font-size:22px; letter-spacing:0.08em; color:#fff; text-transform:uppercase; line-height:1; }
header { border-bottom:1px solid var(--border); padding:16px 28px; display:flex; align-items:center; gap:20px; position:sticky; top:0; background:var(--header-bg); backdrop-filter:blur(10px); z-index:100; }
.logo { font-family:var(--sans); font-weight:900; font-size:22px; letter-spacing:0.08em; color:var(--logo-color); text-transform:uppercase; line-height:1; }
.logo span { color:var(--accent); }
.logo-sub { font-size:10px; color:var(--label); letter-spacing:0.25em; text-transform:uppercase; margin-top:3px; }
.header-right { margin-left:auto; display:flex; align-items:center; gap:10px; flex-wrap:wrap; justify-content:flex-end; }
@@ -53,6 +137,8 @@
.btn:disabled { border-color:var(--dim); color:var(--dim); cursor:not-allowed; }
.btn:disabled::before { display:none; }
.btn:disabled:hover { color:var(--dim); }
.btn-theme { border:1px solid var(--dim); color:var(--label); min-width:72px; text-align:center; }
.btn-theme::before { background:var(--text); }
.batt-bar { display:flex; align-items:center; gap:8px; font-size:11px; color:var(--label); }
.batt-cells { display:flex; gap:2px; }
@@ -117,7 +203,7 @@
.cmd-btn::before { content:''; position:absolute; inset:0; opacity:0; transition:opacity 0.2s; }
.cmd-btn:hover::before { opacity:1; }
.cmd-btn:hover { color:var(--bg); }
.cmd-btn:hover .cmd-desc { color:rgba(10,12,14,0.65); }
.cmd-btn:hover .cmd-desc { color:var(--hover-desc-color); }
.cmd-btn.calibrate::before { background:var(--accent); }
.cmd-btn.calibrate:hover { border-color:var(--accent); }
.cmd-btn.reset::before { background:var(--accent2); }
@@ -187,8 +273,8 @@
body.disconnected .cmd-grid { opacity:0.45; pointer-events:none; transition:opacity 0.3s; }
.tap-flash { position:absolute; inset:0; pointer-events:none; opacity:0; transition:opacity 0.25s; }
.tap-flash.left { background:radial-gradient(circle at center, rgba(0,229,255,0.35) 0%, transparent 70%); }
.tap-flash.right { background:radial-gradient(circle at center, rgba(255,61,113,0.35) 0%, transparent 70%); }
.tap-flash.left { background:radial-gradient(circle at center, var(--tap-left) 0%, transparent 70%); }
.tap-flash.right { background:radial-gradient(circle at center, var(--tap-right) 0%, transparent 70%); }
.tap-flash.show { opacity:1; }
.viz-wrap { position:relative; }
</style>
@@ -207,6 +293,7 @@
<div class="batt-cells" id="battCells"></div>
<span id="battPct">--%</span>
</div>
<button class="btn btn-theme" id="themeBtn" onclick="cycleTheme()"><span>AUTO</span></button>
<div class="status-pill" id="statusPill"><div class="dot"></div><span id="statusText">DISCONNECTED</span></div>
<button class="btn btn-connect" id="connectBtn" onclick="doConnect()"><span>Connect</span></button>
<button class="btn btn-disconnect" id="disconnectBtn" onclick="doDisconnect()" style="display:none"><span>Disconnect</span></button>
@@ -382,6 +469,7 @@ function log(msg, type='') {
el.appendChild(d); el.scrollTop=el.scrollHeight;
}
const p2=n=>String(n).padStart(2,'0'), p3=n=>String(n).padStart(3,'0');
function cssVar(n) { return getComputedStyle(document.documentElement).getPropertyValue(n).trim(); }
// ── Connection ───────────────────────────────────────────────────────────────
async function doConnect() {
@@ -712,26 +800,28 @@ function updateAxisBar(axis, val, max) {
function drawViz(idle) {
const W=canvas.width, H=canvas.height;
ctx.fillStyle='rgba(13,16,19,0.25)'; ctx.fillRect(0,0,W,H);
ctx.strokeStyle='rgba(31,36,40,0.6)'; ctx.lineWidth=0.5;
ctx.fillStyle=cssVar('--canvas-fade'); ctx.fillRect(0,0,W,H);
ctx.strokeStyle=cssVar('--canvas-grid'); ctx.lineWidth=0.5;
for(let x=0;x<W;x+=40){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke();}
for(let y=0;y<H;y+=40){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke();}
ctx.strokeStyle='rgba(58,64,80,0.5)'; ctx.lineWidth=0.5;
ctx.strokeStyle=cssVar('--canvas-center'); ctx.lineWidth=0.5;
ctx.beginPath();ctx.moveTo(W/2,0);ctx.lineTo(W/2,H);ctx.stroke();
ctx.beginPath();ctx.moveTo(0,H/2);ctx.lineTo(W,H/2);ctx.stroke();
const now=Date.now();
const trailRgb=cssVar('--trail-rgb'), trailIdleRgb=cssVar('--trail-idle-rgb');
for(let i=1;i<trail.length;i++){
const age=(now-trail[i].t)/1200, alpha=Math.max(0,1-age); if(alpha<=0) continue;
ctx.strokeStyle=trail[i].idle?`rgba(58,64,80,${alpha*0.4})`:`rgba(0,229,255,${alpha*0.7})`;
ctx.strokeStyle=trail[i].idle?`rgba(${trailIdleRgb},${alpha*0.4})`:`rgba(${trailRgb},${alpha*0.7})`;
ctx.lineWidth=1.5;
ctx.beginPath();ctx.moveTo(trail[i-1].x,trail[i-1].y);ctx.lineTo(trail[i].x,trail[i].y);ctx.stroke();
}
const dotColor=idle?'#3a4050':'#00e5ff', dotGlow=idle?'transparent':'rgba(0,229,255,0.35)';
const dotColor=idle?cssVar('--canvas-dot-idle'):cssVar('--canvas-dot');
const dotGlow=idle?'transparent':cssVar('--canvas-dot-glow');
ctx.shadowColor=dotGlow; ctx.shadowBlur=12;
ctx.fillStyle=dotColor;
ctx.beginPath();ctx.arc(cursorX,cursorY,idle?3:5,0,Math.PI*2);ctx.fill();
ctx.shadowBlur=0;
if(idle){ctx.fillStyle='rgba(90,100,128,0.7)';ctx.font='10px Share Tech Mono,monospace';ctx.textAlign='center';ctx.fillText('IDLE',W/2,H-10);ctx.textAlign='left';}
if(idle){ctx.fillStyle=cssVar('--canvas-idle-text');ctx.font='10px Share Tech Mono,monospace';ctx.textAlign='center';ctx.fillText('IDLE',W/2,H-10);ctx.textAlign='left';}
}
function flashTap(side){
@@ -739,17 +829,39 @@ function flashTap(side){
el.classList.add('show'); setTimeout(()=>el.classList.remove('show'),300);
}
(function initCanvas(){
function drawInitState() {
const W=canvas.width,H=canvas.height;
ctx.fillStyle='#0d1013';ctx.fillRect(0,0,W,H);
ctx.strokeStyle='rgba(31,36,40,0.5)';ctx.lineWidth=0.5;
ctx.fillStyle=cssVar('--canvas-bg');ctx.fillRect(0,0,W,H);
ctx.strokeStyle=cssVar('--canvas-grid');ctx.lineWidth=0.5;
for(let x=0;x<W;x+=40){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke();}
for(let y=0;y<H;y+=40){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke();}
ctx.strokeStyle='rgba(58,64,80,0.4)';
ctx.strokeStyle=cssVar('--canvas-center');
ctx.beginPath();ctx.moveTo(W/2,0);ctx.lineTo(W/2,H);ctx.stroke();
ctx.beginPath();ctx.moveTo(0,H/2);ctx.lineTo(W,H/2);ctx.stroke();
ctx.fillStyle='rgba(58,64,80,0.6)';ctx.font='10px Share Tech Mono,monospace';
ctx.fillStyle=cssVar('--canvas-idle-text');ctx.font='10px Share Tech Mono,monospace';
ctx.textAlign='center';ctx.fillText('connect to activate stream',W/2,H/2+4);ctx.textAlign='left';
}
// ── Theme ─────────────────────────────────────────────────────────────────────
const THEMES = ['auto','dark','light'];
const THEME_LABELS = {auto:'AUTO',dark:'DARK',light:'LIGHT'};
let themeIdx = 0;
function cycleTheme() {
themeIdx = (themeIdx + 1) % 3;
applyTheme(THEMES[themeIdx]);
}
function applyTheme(t) {
document.documentElement.classList.remove('theme-dark','theme-light');
if (t === 'dark') document.documentElement.classList.add('theme-dark');
if (t === 'light') document.documentElement.classList.add('theme-light');
document.getElementById('themeBtn').querySelector('span').textContent = THEME_LABELS[t];
localStorage.setItem('theme', t);
if (!chars.imuStream) drawInitState();
}
(function(){
const saved = localStorage.getItem('theme') ?? 'auto';
themeIdx = Math.max(0, THEMES.indexOf(saved));
applyTheme(saved);
})();
if (!navigator.bluetooth) {