fix(ui): native header top safe-area under edge-to-edge
The earlier safe-area fix reached the bottom bars (they apply --tg-safe-bottom) but not the header: its top inset lived only in a Telegram-fullscreen-scoped rule, so on the native build the header .bar had just 5px top padding and its content sat under the status bar (measured: the title at y=11px behind the 54px bar; the back button untappable). Give .bar a top inset from the SystemBars plugin's --safe-area-inset-top (native-only via the plugin var; unset -> 0 on web/PWA/Telegram/VK, so no regression; tg-fullscreen still overrides via specificity). Verified on-device (Pixel_10 / API 37): the title moved from y=11 to y=65, clear of the status bar.
This commit is contained in:
+14
-8
@@ -59,14 +59,20 @@ The native Android app (`ANDROID_PLAN.md`) is a Capacitor 8 wrapper of the `ui`
|
|||||||
On Android **WebView < 140**, `env(safe-area-inset-*)` wrongly reports **0**, so chrome relying on it draws
|
On Android **WebView < 140**, `env(safe-area-inset-*)` wrongly reports **0**, so chrome relying on it draws
|
||||||
under the bars and is untappable: the top nav under the clock, and the game's bottom action bar's **centre**
|
under the bars and is untappable: the top nav under the clock, and the game's bottom action bar's **centre**
|
||||||
button (Hint) under the home-indicator pill (side buttons still work; `navigation_mode`=2 is gesture nav).
|
button (Hint) under the home-indicator pill (side buttons still work; `navigation_mode`=2 is gesture nav).
|
||||||
Fix is CSS-only: Capacitor 8's **SystemBars** plugin (built into `@capacitor/core`, `insetsHandling:'css'`
|
Fix is CSS-only, in TWO parts: (1) Capacitor 8's **SystemBars** plugin (built into `@capacitor/core`,
|
||||||
default — no dep, no config) injects correct `--safe-area-inset-*`; consume them as
|
`insetsHandling:'css'` default — no dep, no config) injects correct `--safe-area-inset-*`; consume them as
|
||||||
`--tg-safe-*: var(--safe-area-inset-*, env(safe-area-inset-*, 0px))` (`ui/src/app.css`). **Reproduces ONLY on
|
`--tg-safe-*: var(--safe-area-inset-*, env(safe-area-inset-*, 0px))` (`ui/src/app.css`) — fixes any consumer
|
||||||
WebView < 140** — an emulator whose WebView auto-updated (Play Store) to ≥140 (e.g. Chrome 149) will NOT
|
that ALREADY applies the token (the bottom bars: `Game.svelte`/`Screen.svelte` `--tg-safe-bottom`).
|
||||||
show it (env works there), so verify via the injected var, not the visual. **Inspect a live debug WebView**
|
(2) But a consumer that never applied the top inset on the native path is NOT fixed by the token alone — the
|
||||||
over CDP: `adb forward tcp:9222 localabstract:$(adb shell cat /proc/net/unix | grep -o
|
**header's** top inset was Telegram-fullscreen-scoped only, so the native header sat under the status bar on
|
||||||
'webview_devtools_remote_[0-9]*' | head -1)`, then Playwright `chromium.connectOverCDP('http://localhost:9222')`
|
EVERY WebView; it needed its own `.bar { padding-top: calc(var(--safe-area-inset-top, 0px) + 5px) }`
|
||||||
→ `page.evaluate` (read `--safe-area-inset-*` vs `env(...)`).
|
(`Header.svelte`, native-only via the plugin var; tg-fullscreen still overrides via specificity). **Measure
|
||||||
|
per element, don't eyeball** — a centred title at y=11 under a 54px bar reads as "fine" in a screenshot but
|
||||||
|
is overlapping; an emulator WebView auto-updated to ≥140 (Chrome 149) also hides the `env()`=0 half (so the
|
||||||
|
BOTTOM looks fine there while the user's < 140 device overlaps). **Inspect a live debug WebView** over CDP:
|
||||||
|
`adb forward tcp:9222 localabstract:$(adb shell cat /proc/net/unix | grep -o 'webview_devtools_remote_[0-9]*'
|
||||||
|
| head -1)`, then Playwright `chromium.connectOverCDP('http://localhost:9222')` → `page.evaluate` (read each
|
||||||
|
element's `getBoundingClientRect().top`, and `--safe-area-inset-*` vs `env(...)`).
|
||||||
- **`sharp` is whitelisted** in `ui/pnpm-workspace.yaml` (`allowBuilds: sharp: true`) — `@capacitor/assets`
|
- **`sharp` is whitelisted** in `ui/pnpm-workspace.yaml` (`allowBuilds: sharp: true`) — `@capacitor/assets`
|
||||||
uses it for `pnpm android:assets` (launcher icon/splash); else pnpm 11 raises `ERR_PNPM_IGNORED_BUILDS`.
|
uses it for `pnpm android:assets` (launcher icon/splash); else pnpm 11 raises `ERR_PNPM_IGNORED_BUILDS`.
|
||||||
- **Bundled offline dicts come from the `scrabble-dictionary` release** (`scrabble-dawg-<DICT_VERSION>.tar.gz`,
|
- **Bundled offline dicts come from the `scrabble-dictionary` release** (`scrabble-dawg-<DICT_VERSION>.tar.gz`,
|
||||||
|
|||||||
+4
-2
@@ -441,8 +441,10 @@ airplane mode): cold-boot → offline guest lobby, offline vs_ai played end-to-e
|
|||||||
placed "FEZ", the robot replied "NEEDFIRE"), New Game offers both modes. The smoke surfaced a native-chrome
|
placed "FEZ", the robot replied "NEEDFIRE"), New Game offers both modes. The smoke surfaced a native-chrome
|
||||||
bug on **Android 15+ edge-to-edge** (WebView < 140 reports `env(safe-area-inset-*)`=0 → the top nav draws
|
bug on **Android 15+ edge-to-edge** (WebView < 140 reports `env(safe-area-inset-*)`=0 → the top nav draws
|
||||||
under the status bar and the game's centre Hint button under the gesture-nav home indicator) — **fixed** by
|
under the status bar and the game's centre Hint button under the gesture-nav home indicator) — **fixed** by
|
||||||
consuming the SystemBars core plugin's injected `--safe-area-inset-*` in `app.css` (see `.claude/CLAUDE.md`;
|
consuming the SystemBars core plugin's injected `--safe-area-inset-*`: the bottom via the `--tg-safe-*` token
|
||||||
no dep/config — the plugin ships in `@capacitor/core` v8). Remaining for D: the reconcile-online leg
|
(`app.css`), and the header's top inset via a native-only `.bar` rule (`Header.svelte` — its top inset was
|
||||||
|
Telegram-fullscreen-scoped only, so the token alone did not reach it). No dep/config (the plugin ships in
|
||||||
|
`@capacitor/core` v8); full story in `.claude/CLAUDE.md`. Remaining for D: the reconcile-online leg
|
||||||
on-device (hits prod — a guest mint) and the deferred local-game-visibility decision (§G). **Verify every
|
on-device (hits prod — a guest mint) and the deferred local-game-visibility decision (§G). **Verify every
|
||||||
line ref below against current code.**
|
line ref below against current code.**
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,11 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--gap);
|
gap: var(--gap);
|
||||||
padding: 5px var(--pad);
|
padding: 5px var(--pad);
|
||||||
|
/* Clear the top safe area on the native build: under Android 15+ edge-to-edge the WebView draws
|
||||||
|
behind the status bar, so the header row must drop below it. --safe-area-inset-top is injected by
|
||||||
|
the Capacitor SystemBars plugin (native only — unset, so 0, on web / PWA / Telegram / VK, which do
|
||||||
|
not draw behind our header). Telegram fullscreen overrides this with its own nav-band rule below. */
|
||||||
|
padding-top: calc(var(--safe-area-inset-top, 0px) + 5px);
|
||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.05rem;
|
font-size: 1.05rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user