Modal-Scroll: Wrapper als definite-height Scroll-Container (Safari-safe)

Ursache: Tailwinds min-h-screen (min-height:100vh) am Flex-Wrapper wächst
in Safari nicht mit dem Panel – das Panel ragt sichtbar aus dem Bild, der
Container meldet aber keinen Overflow → nichts scrollt, Speichern-Button
ist unerreichbar (Chromium wächst dagegen mit, daher dort scheinbar ok).

Fix: Die innere .min-h-screen-Box wird zum Scroll-Container mit
DEFINITE height:100vh + overflow-y:auto (+ -webkit-overflow-scrolling:touch).
Der Backdrop hält overflow:hidden. margin:auto am Panel zentriert kurze
Modals und vermeidet Top-Clipping bei langen. Wirkt in Safari, Chrome,
Firefox, iOS und Android (verifiziert via Chromium desktop + iPhone-Touch).

Zusätzlich Cache-Busting für styles.css (?v=<mtime>) am <link>, damit ein
hart zwischengespeichertes altes Stylesheet nach einem Deploy garantiert
neu geladen wird.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-20 09:14:12 +02:00
co-authored by Claude
parent 811188f492
commit a6a2fabf05
3 changed files with 28 additions and 12 deletions
+15 -11
View File
@@ -452,20 +452,24 @@ button.loading::after {
============================================ */ ============================================ */
/* Modals: the overlay locks body scroll (overflow:hidden on <body>), so a panel /* Modals: the overlay locks body scroll (overflow:hidden on <body>), so a panel
taller than the viewport would have its bottom cut off and the Save button taller than the viewport must scroll. The scroll container is the INNER
unreachable. Let the OVERLAY itself scroll like a page — the panel grows to wrapper, not the backdrop, and it gets a DEFINITE height:100vh + overflow-y:auto.
its natural height, and the overlay scrolls to reveal it. The panel uses A mere min-height:100vh (Tailwind's min-h-screen) is not enough: in Safari the
`margin:auto`, which centers a short panel but — unlike `align-items:center` flex wrapper then does NOT grow to fit the panel, the panel overflows visibly
— does NOT clip the top when the panel is taller than the viewport (the ("card geht aus dem Bild") but the container reports no overflow — so nothing
classic flexbox top-clip bug), so both the close button (top) and the Save scrolls and the Save button is unreachable. A definite height + overflow
button (bottom) stay reachable. Applies on every screen size: the forces a real scroll container in every browser. The panel keeps margin:auto
Bewerbung-hinzufügen form has enough fields (Datum bis Interne Notizen) to so a SHORT modal stays centered, while a tall one starts at the top (no
overflow even on a laptop, not just a phone. */ top-clip) and scrolls to the bottom. -webkit-overflow-scrolling gives older
iOS momentum touch scrolling. */
.fixed.inset-0 { .fixed.inset-0 {
overflow-y: auto !important; overflow: hidden !important;
} }
.fixed.inset-0 > .min-h-screen { .fixed.inset-0 > .min-h-screen {
min-height: 100vh; height: 100vh !important;
min-height: 0 !important;
overflow-y: auto !important;
-webkit-overflow-scrolling: touch;
align-items: flex-start !important; align-items: flex-start !important;
} }
.fixed.inset-0 > .min-h-screen > .max-w-lg, .fixed.inset-0 > .min-h-screen > .max-w-lg,
+12
View File
@@ -158,6 +158,18 @@ app.use('/api/indeed-import', (req, res, next) => {
// Set EJS as template engine // Set EJS as template engine
app.set('view engine', 'ejs'); app.set('view engine', 'ejs');
// Cache-busting-Version für styles.css: die mtime der Datei beim Start. Wirkt
// nur als Query-Parameter am <link href>, also nach jedem Deploy (neuer
// Container → neue mtime → neue URL) erzwingt sie einen echten NeuDownload —
// "no-cache" allein reicht manchmal nicht, wenn ein Browser die alte Datei
// hart zwischengespeichert hat. Innerhalb eines Laufs bleibt der Wert stabil.
try {
const cssStat = fs.statSync(path.join(__dirname, 'public', 'css', 'styles.css'));
app.locals.assetVersion = String(cssStat.mtimeMs || Date.now());
} catch (e) {
app.locals.assetVersion = String(Date.now());
}
app.set('views', path.join(__dirname, 'views')); app.set('views', path.join(__dirname, 'views'));
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
+1 -1
View File
@@ -20,4 +20,4 @@
tailwind.config = { darkMode: 'class' }; tailwind.config = { darkMode: 'class' };
</script> </script>
<!-- Custom CSS --> <!-- Custom CSS -->
<link rel="stylesheet" href="/css/styles.css"> <link rel="stylesheet" href="/css/styles.css?v=<%= assetVersion %>">