From aa2c3b989d5ffefd7bf752151d2ad51013fe7839 Mon Sep 17 00:00:00 2001 From: Parley Hatch Date: Sun, 31 May 2026 18:40:27 -0600 Subject: [PATCH] Make it full-screen: no scrolling, ever MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lock the viewport (100dvh, overflow:hidden, grid-centered) and scale the active card to fit via a --fit custom property set by fitActiveScreen(). Every screen — setup, game, winner — now always fits the browser at any window size; the card shrinks only when it would otherwise overflow. Refit on screen change, stage switch, resize, orientation, and font load. Verified no vertical/horizontal scroll on all three screens at short-laptop (1366x640) and mobile-portrait (390x844). Co-Authored-By: Claude Opus 4.8 (1M context) --- script.js | 29 ++++++++++++++++++++++++++++- style.css | 20 ++++++++++++++------ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/script.js b/script.js index 749c29f..fc31e6e 100644 --- a/script.js +++ b/script.js @@ -230,6 +230,7 @@ function applyStage(stage, { persist = true } = {}) { opt.setAttribute('aria-checked', opt.dataset.stage === stage ? 'true' : 'false')); startLabel.textContent = STAGES[stage].startLabel; if (persist) saveState(); + scheduleFit(); // font swap (e.g. Metal Mania) changes height → refit } /* ---------- Setup interactions ---------- */ @@ -424,9 +425,34 @@ function applyPlayerCamps() { function showScreen(screen) { [nameScreen, gameScreen, winnerScreen].forEach((s) => s.classList.remove('active')); screen.classList.add('active'); - window.scrollTo({ top: 0, behavior: 'smooth' }); + scheduleFit(); } +/* ---------- Fit-to-viewport ---------- + Scale the active card (via the --fit custom prop) so the whole screen + always fits — it's tic-tac-toe, no scrolling, ever. */ +let fitRAF = 0; +function scheduleFit() { + if (fitRAF) cancelAnimationFrame(fitRAF); + fitRAF = requestAnimationFrame(() => { fitRAF = 0; fitActiveScreen(); }); +} +function fitActiveScreen() { + const screen = document.querySelector('.screen.active'); + if (!screen) return; + const card = screen.querySelector('.card'); + if (!card) return; + // offsetWidth/Height ignore transforms, so we always read the natural size. + const w = card.offsetWidth; + const h = card.offsetHeight; + if (!w || !h) return; + const margin = 16; // small breathing room around the card + const scale = Math.min(1, (window.innerWidth - margin) / w, (window.innerHeight - margin) / h); + card.style.setProperty('--fit', String(scale)); +} +window.addEventListener('resize', scheduleFit); +window.addEventListener('orientationchange', scheduleFit); +if (document.fonts && document.fonts.ready) document.fonts.ready.then(scheduleFit); + /* ---------- Lifecycle ---------- */ function startGame() { player1Name = player1Input.value.trim() || 'Player 1'; @@ -520,5 +546,6 @@ function init() { applyStage(currentStage, { persist: false }); applyPlayerCamps(); updatePlayerHighlight(); + scheduleFit(); } init(); diff --git a/style.css b/style.css index d954bbd..24f04c5 100644 --- a/style.css +++ b/style.css @@ -110,18 +110,22 @@ .camp-sparkle { --pc: var(--sparkle-color); --pg: var(--sparkle-glow); --pt: var(--sparkle-tint); } .camp-shred { --pc: var(--shred-color); --pg: var(--shred-glow); --pt: var(--shred-tint); } -/* ---------- Base layout (scroll-safe centering) ---------- */ +/* ---------- Base layout (full-screen, never scroll) ---------- + The viewport is locked to the screen; the active card is scaled by JS + (--fit) to always fit. It's tic-tac-toe — everything stays in view. */ +html { height: 100%; } body { font-family: var(--body-font); background: var(--bg); background-size: var(--bg-size); animation: gradientShift 15s ease infinite; - min-height: 100vh; - display: flex; - flex-direction: column; - overflow-x: hidden; + height: 100dvh; + min-height: 100dvh; + display: grid; + place-items: center; + overflow: hidden; position: relative; - padding: 28px 16px; + padding: 8px; color: var(--text); transition: color 0.4s ease; } @@ -230,6 +234,10 @@ body { backdrop-filter: blur(10px); border: 3px solid var(--surface-border); transition: background 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease; + /* Scaled to fit the viewport by fitActiveScreen() in script.js */ + transform: scale(var(--fit, 1)); + transform-origin: center center; + will-change: transform; } .name-card { padding: 30px 30px 34px; }