Put the whole game inside the safe area

Insetting only the HUD while the canvas stayed full-bleed wasn't what was
asked for. The canvas itself is position:fixed and inset by
env(safe-area-inset-*) now, so nothing renders under the notch, the status bar
or the home indicator, and render.js no longer offsets the HUD as well — doing
both would subtract the notch twice.

The bottom strip was a second bug: the canvas was sized by computing a scale
from innerWidth/innerHeight and setting explicit pixels, so it was laid out
against whatever innerHeight reported at load, before Safari's toolbars had
settled, and stayed short of the bottom forever after. CSS stretches it now
(100vw x 100dvh minus the insets) and fitViewport derives the logical grid from
the box it was actually given, so the aspect always matches exactly — no
letterbox, no distortion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-07-30 17:59:43 +02:00
1 parent 89667c4018
commit 90aefdce27
3 files changed
+46 -21

No files matched your search

+16 -3
View File
@@ -20,13 +20,26 @@
src: url('assets/fonts/Silkscreen-Regular.ttf') format('truetype'); }
@font-face { font-family: 'Silkscreen'; font-weight: 700; font-display: block;
src: url('assets/fonts/Silkscreen-Bold.ttf') format('truetype'); }
html, body { margin: 0; height: 100%; height: 100dvh; background: #10141c; display: grid; place-items: center; }
html, body { margin: 0; padding: 0; background: #10141c; overflow: hidden; }
/* invisible probe: env() is only readable from CSS, so park the insets on an element and let
render.js measure them. The canvas stays FULL-BLEED (the stands under the notch look good) —
it is the HUD that has to move, see SAFE in render.js. */
#safe { position: fixed; top: 0; left: 0; width: 0; height: 0; pointer-events: none; visibility: hidden;
padding-top: env(safe-area-inset-top, 0px); padding-bottom: env(safe-area-inset-bottom, 0px); }
canvas { image-rendering: pixelated; width: min(96vw, calc(96vh * 16 / 9)); aspect-ratio: 16/9; touch-action: none; }
/* The canvas STRETCHES to the viewport and render.js derives its logical height from the box it
actually got. Sizing it the other way round (compute a scale from innerWidth/innerHeight, then set
explicit px) meant it was laid out against whatever innerHeight reported at load — before iOS
Safari's toolbars had settled — and it stayed a strip short of the bottom forever after.
100dvh is the dynamic viewport height, so it tracks those toolbars. */
/* The canvas lives ENTIRELY INSIDE the safe area — never under the notch, the status bar or the
home indicator. Doing the inset here rather than offsetting the HUD in canvas space keeps it in
ONE place: render.js then just matches its logical grid to whatever box it was given.
100dvh is the dynamic viewport height, so it tracks iOS Safari's toolbars appearing/hiding. */
canvas { image-rendering: pixelated; display: block; position: fixed; touch-action: none;
top: env(safe-area-inset-top, 0px);
left: env(safe-area-inset-left, 0px);
width: calc(100vw - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px));
height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)); }
#hint { position: fixed; bottom: 8px; width: 100%; text-align: center;
font: 400 10px Silkscreen, monospace; color: #6b7688; }
@media (pointer: coarse) { #hint, #intro .kb { display: none; } }
@@ -66,6 +79,6 @@
<p class="start">TAP TO START</p>
</div>
</div>
<script type="module" src="main.js?v=45"></script>
<script type="module" src="main.js?v=46"></script>
</body>
</html>