fix: resolve iOS PWA bottom nav positioning via flex layout

Switch .nav-bottom from position: fixed to a flex child of .app-shell.
With position: fixed and will-change: transform (used for the hide/show
animation), iOS compositor layers can misplace the element. As a flex
child (position: relative; flex-shrink: 0) at the end of a height: 100dvh
container the nav is guaranteed to sit flush at the physical screen bottom.

Remove padding-bottom clearance from .app-content, .tasks-page and
.dashboard - no longer needed since the nav no longer overlaps the
content area.
This commit is contained in:
Ulas
2026-04-16 09:59:16 +02:00
parent 62bb3546a8
commit 51f211d72a
5 changed files with 15 additions and 8 deletions
+5
View File
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.20.4] - 2026-04-16
### Fixed
- iOS PWA: bottom navigation bar appeared visually higher than on Android. Changed `.nav-bottom` from `position: fixed` to a flex child of `.app-shell` (`position: relative; flex-shrink: 0`). With `position: fixed` and `will-change: transform` (used for the hide/show animation), iOS's compositor could misplace the nav bar. As a flex child at the end of a `height: 100dvh` container, the nav is guaranteed to sit flush at the physical screen bottom on all platforms. Removed the redundant `padding-bottom` clearance from `.app-content`, `.tasks-page`, and `.dashboard` (no longer needed since the nav no longer overlaps the content area).
## [0.20.3] - 2026-04-16 ## [0.20.3] - 2026-04-16
### Fixed ### Fixed
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "oikos", "name": "oikos",
"version": "0.20.3", "version": "0.20.4",
"description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.", "description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.",
"main": "server/index.js", "main": "server/index.js",
"type": "module", "type": "module",
+1 -1
View File
@@ -16,7 +16,7 @@
* -------------------------------------------------------- */ * -------------------------------------------------------- */
.dashboard { .dashboard {
padding: var(--space-4); padding: var(--space-4);
padding-bottom: calc(var(--nav-bottom-height) + var(--safe-area-inset-bottom) + var(--space-16)); padding-bottom: var(--space-16);
max-width: var(--content-max-width); max-width: var(--content-max-width);
margin: 0 auto; margin: 0 auto;
} }
+7 -5
View File
@@ -96,7 +96,6 @@
.app-content { .app-content {
flex: 1; flex: 1;
min-height: 0; min-height: 0;
padding-bottom: calc(var(--nav-bottom-height) + var(--safe-area-inset-bottom));
overflow-y: auto; overflow-y: auto;
overscroll-behavior-y: contain; overscroll-behavior-y: contain;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
@@ -114,10 +113,13 @@
* Leichter Blur-Effekt für Glassmorphismus. * Leichter Blur-Effekt für Glassmorphismus.
* -------------------------------------------------------- */ * -------------------------------------------------------- */
.nav-bottom { .nav-bottom {
position: fixed; /* Flex-Kind von .app-shell statt position: fixed - zuverlässiger auf iOS PWA.
bottom: 0; * position: fixed kann durch will-change: transform in iOS-Compositor-Layern
left: 0; * falsch referenziert werden. Als Flex-Kind am Ende der 100dvh-App-Shell ist
right: 0; * die Position garantiert am Viewport-Bottom. */
position: relative;
flex-shrink: 0;
width: 100%;
background-color: color-mix(in srgb, var(--color-surface) 85%, transparent); background-color: color-mix(in srgb, var(--color-surface) 85%, transparent);
border-top: 1px solid var(--color-border-subtle); border-top: 1px solid var(--color-border-subtle);
display: flex; display: flex;
+1 -1
View File
@@ -14,7 +14,7 @@
* -------------------------------------------------------- */ * -------------------------------------------------------- */
.tasks-page { .tasks-page {
padding: var(--space-4); padding: var(--space-4);
padding-bottom: calc(var(--nav-bottom-height) + var(--safe-area-inset-bottom) + var(--space-16)); padding-bottom: var(--space-16);
max-width: var(--content-max-width); max-width: var(--content-max-width);
margin: 0 auto; margin: 0 auto;
} }