diff --git a/CHANGELOG.md b/CHANGELOG.md index edc019d..03c194f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.20.11] - 2026-04-19 + +### Fixed +- PWA: modal header (task / calendar event) no longer scrolls out of view when the form content exceeds the modal height. Root cause: `position: sticky` on `.modal-panel__header` fails on iOS WebKit when the scroll container (`.modal-panel`) has `padding-top` applied (a known WebKit quirk). Fixed by restructuring the modal layout: `.modal-panel` is now a `flex-column` container with `overflow: hidden`, and scrolling is handled by `.modal-panel__body` (`overflow-y: auto; flex: 1`). The header is always visible as a non-scrolled flex sibling. Swipe-to-close updated to read scroll position from `.modal-panel__body` instead of `.modal-panel` (closes #50) + ## [0.20.10] - 2026-04-18 ### Changed diff --git a/package.json b/package.json index 0930f82..a23e25c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oikos", - "version": "0.20.10", + "version": "0.20.11", "description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.", "main": "server/index.js", "type": "module", diff --git a/public/components/modal.js b/public/components/modal.js index d30c75d..a1448c4 100644 --- a/public/components/modal.js +++ b/public/components/modal.js @@ -114,12 +114,15 @@ function _wireSheetSwipe(panel) { let startY = 0; let dragging = false; + // Scroll position is now on the body, not the panel itself + const scrollBody = panel.querySelector('.modal-panel__body'); + panel.addEventListener('touchstart', (e) => { // Nur von der Handle-Zone (obere 48px) oder wenn Panel ganz oben → Swipe erlauben const touchY = e.touches[0].clientY; const rect = panel.getBoundingClientRect(); const isHandleZone = touchY - rect.top < 48; - const isScrolledToTop = panel.scrollTop <= 0; + const isScrolledToTop = (scrollBody ? scrollBody.scrollTop : panel.scrollTop) <= 0; if (!isHandleZone && !isScrolledToTop) return; startY = touchY; dragging = true; diff --git a/public/styles/layout.css b/public/styles/layout.css index fb04b81..5075498 100644 --- a/public/styles/layout.css +++ b/public/styles/layout.css @@ -648,7 +648,9 @@ background-color: var(--color-surface); width: 100%; max-height: 90dvh; - overflow-y: auto; + overflow: hidden; + display: flex; + flex-direction: column; border-radius: var(--radius-lg) var(--radius-lg) 0 0; border: 1px solid var(--color-border-subtle); box-shadow: var(--shadow-lg); @@ -673,10 +675,8 @@ justify-content: space-between; padding: var(--space-4); border-bottom: 1px solid var(--color-border-subtle); - position: sticky; - top: 0; background-color: var(--color-surface); - z-index: 1; + flex-shrink: 0; } .modal-panel__title { @@ -706,6 +706,10 @@ display: flex; flex-direction: column; gap: var(--space-4); + flex: 1; + min-height: 0; + overflow-y: auto; + -webkit-overflow-scrolling: touch; } /* Modal-Content-Grids (z. B. 2 Spalten in Formularen) */