From d1bd996675dc5a3e96359a9942c998bab15e68d8 Mon Sep 17 00:00:00 2001 From: Me Here Date: Mon, 25 May 2026 21:42:30 -0500 Subject: [PATCH] Player full-screen: edge-to-edge themed stage + theme toggle in stage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes from on-device testing: 1. Fill the screen. The earlier stage capped the device at min(96vw,168vh) and centred it, leaving big margins on wide phones. Now the device frame goes transparent/borderless at position:absolute inset:0 and the OLED grows (flex:1) so the unit fills the whole viewport edge to edge. 2. Follow light/dark/system in full-screen. The full-screen skin is the themed page gradient (light in light mode, dark in dark, OS-driven on system), and a theme toggle (◐/☀/☾ — same cycle + "metronome.theme" key as the main page) now sits beside the exit ✕, since the top bar that normally holds it is hidden in stage. The themed gradient is painted on the device element rather than the body because a position:fixed body doesn't propagate its background to the canvas (left a white area). The decorative PWR dot is hidden in stage so it doesn't sit under the floating controls. Co-Authored-By: Claude Opus 4.7 (1M context) --- player.html | 67 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/player.html b/player.html index 50ceac6..048b021 100644 --- a/player.html +++ b/player.html @@ -145,40 +145,49 @@ .hint{ font-size:11px; color:var(--muted) } code{ background:var(--field-bg); border:1px solid var(--field-bd); border-radius:4px; padding:1px 5px; font-size:11px } - /* ---- full-screen "stage" mode ---- */ - .fs-exit{ display:none; position:fixed; top:14px; right:14px; z-index:80; - background:rgba(18,21,28,.7); color:#e7edf5; border:1px solid rgba(255,255,255,.28); + /* ---- full-screen "stage" mode: edge-to-edge, follows light/dark/system ---- */ + .fs-ctrl{ display:none; position:fixed; top:max(12px,env(safe-area-inset-top)); z-index:80; + background:rgba(127,139,154,.16); color:var(--txt); border:1px solid rgba(127,139,154,.5); border-radius:50%; width:40px; height:40px; font-size:17px; line-height:1; cursor:pointer; - backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px) } - .fs-exit:hover{ background:rgba(40,48,62,.9) } + backdrop-filter:blur(4px); -webkit-backdrop-filter:blur(4px) } + .fs-ctrl:hover{ background:rgba(127,139,154,.32) } + #fsExit{ right:max(12px,env(safe-area-inset-right)) } + #fsThemeBtn{ right:calc(max(12px,env(safe-area-inset-right)) + 50px) } .rotate-hint{ display:none } - body.stage{ position:fixed; inset:0; padding:2.5vmin; gap:0; justify-content:center; overflow:hidden } - body.stage .topbar, body.stage .panel{ display:none } - body.stage .fs-exit{ display:block } - body.stage .device{ max-width:none; width:min(96vw, 168vh); margin:auto; border-radius:min(3vmin,22px) } - /* enlarge the glanceable bits with viewport-relative units */ - body.stage .screen{ padding:2vh 3vw } + /* the device frame goes transparent → the themed page background IS the full-screen + skin (light in light mode, dark in dark mode); children flex to fill the screen */ + body.stage{ position:fixed; inset:0; padding:0; gap:0; overflow:hidden } + body.stage .topbar, body.stage .panel, body.stage .grille{ display:none } + body.stage .fs-ctrl{ display:block } + body.stage .device{ position:absolute; inset:0; width:auto; max-width:none; margin:0; + background:radial-gradient(circle at 50% -8%, var(--bg1), var(--bg2)); border:none; border-radius:0; box-shadow:none; + display:flex; flex-direction:column; + padding:max(2.2vh,env(safe-area-inset-top)) max(4vw,env(safe-area-inset-right)) + max(2.4vh,env(safe-area-inset-bottom)) max(4vw,env(safe-area-inset-left)) } + body.stage .device::before, body.stage .device::after, body.stage .screw{ display:none } + body.stage .brandrow{ flex:0 0 auto; margin:0 0 2vh } + body.stage .pwr{ display:none } /* declutter the corner the floating controls sit in */ + body.stage .logo .model, body.stage .knob-wrap{ color:var(--muted) } + body.stage .screen{ flex:1 1 auto; display:flex; flex-direction:column; justify-content:space-between; padding:2.4vh 3vw } body.stage .scr-top{ font-size:2.8vh } - body.stage .scr-top .tempo{ font-size:3.4vh } - body.stage .scr-top .tempo b{ font-size:8.5vh } - body.stage .scr-name{ font-size:6vh; margin:1.4vh 0 1.2vh } + body.stage .scr-top .tempo{ font-size:3.6vh } + body.stage .scr-top .tempo b{ font-size:9vh } + body.stage .scr-name{ font-size:7vh; margin:0 } body.stage .scr-bot{ font-size:2.8vh } - body.stage .leds{ gap:1.6vmin; margin:2.6vh 0 1vh } - body.stage .led{ width:4.4vmin; height:4.4vmin } - body.stage .controls{ gap:1.6vmin; margin-top:2.2vh } - body.stage .controls .btn{ font-size:2.6vh; padding:1.5vh 1.8vw; min-width:7vw } - body.stage .controls .btn.play{ min-width:11vw; font-size:3.4vh } + body.stage .leds{ flex:0 0 auto; gap:1.8vmin; margin:2.4vh 0 0 } + body.stage .led{ width:4.6vmin; height:4.6vmin } + body.stage .controls{ flex:0 0 auto; gap:1.8vmin; margin-top:2.2vh } + body.stage .controls .btn{ font-size:2.6vh; padding:1.6vh 2vw; min-width:8vw } + body.stage .controls .btn.play{ min-width:12vw; font-size:3.6vh } body.stage .controls .btn small{ font-size:1.3vh } - body.stage .brandrow{ margin-bottom:2vh } - body.stage .grille{ display:none } /* portrait while staged (mainly iPhone, which can't lock) → prompt to rotate */ @media (orientation: portrait){ - body.stage .device{ filter:blur(3px) brightness(.5); pointer-events:none } + body.stage .device{ filter:blur(3px) brightness(.6); pointer-events:none } body.stage .rotate-hint{ display:flex; position:fixed; inset:0; z-index:90; flex-direction:column; align-items:center; justify-content:center; gap:18px; - background:var(--bg2); color:var(--txt); font-size:20px; text-align:center; padding:24px } + background:var(--bg1); color:var(--txt); font-size:20px; text-align:center; padding:24px } body.stage .rotate-hint .rh-icon{ font-size:64px; line-height:1; color:var(--cyan) } } @@ -242,7 +251,8 @@ - + +
Rotate your device to landscape @@ -417,10 +427,13 @@ function themePref(){ try{ const p=localStorage.getItem("metronome.theme"); retu function applyTheme(p){ try{ localStorage.setItem("metronome.theme",p); }catch(e){} document.documentElement.dataset.theme = effectiveTheme(p); - $("themeBtn").textContent = p==="system" ? "◐" : p==="light" ? "☀" : "☾"; - $("themeBtn").title = "Theme: "+p+" (click to cycle: system → light → dark)"; + const glyph = p==="system" ? "◐" : p==="light" ? "☀" : "☾"; + const title = "Theme: "+p+" (click to cycle: system → light → dark)"; + for(const id of ["themeBtn","fsThemeBtn"]){ const b=$(id); if(b){ b.textContent=glyph; b.title=title; } } } -$("themeBtn").onclick = ()=> applyTheme(THEMES[(THEMES.indexOf(themePref())+1)%THEMES.length]); +const cycleTheme = ()=> applyTheme(THEMES[(THEMES.indexOf(themePref())+1)%THEMES.length]); +$("themeBtn").onclick = cycleTheme; +$("fsThemeBtn").onclick = cycleTheme; matchMedia("(prefers-color-scheme: light)").addEventListener("change", ()=>{ if(themePref()==="system") applyTheme("system"); }); applyTheme(themePref());