From 61c1246b5bc54db7a525819bd7f954b5a4e34a19 Mon Sep 17 00:00:00 2001 From: screentinker Date: Thu, 16 Jul 2026 12:12:52 -0500 Subject: [PATCH] fix(ui): make modals scroll instead of overflowing the viewport (#194) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The base .modal had no max-height/overflow on desktop — only the mobile media query capped it — so a tall modal (e.g. the directory-board widget editor with many tenant entries) grew past the screen with no scroll, stranding the lower entries and the Save button off-screen ("unusable"). Cap .modal to 90vh and lay it out as a flex column so .modal-body becomes the scroll region (flex + min-height:0 + overflow-y:auto) while the header and footer (Cancel / Save) stay pinned and always reachable. Moved the cap onto the base rule and dropped the now-redundant overflow from the mobile override. Shared across all modals (all use the header/body/footer structure); short modals are unchanged since max-height is a ceiling, not a fixed height. Verified in real Chrome at a 700px viewport: modal capped to 630px, body scrollable (overflow=auto), Save footer on-screen. CSS-only, no JS. Co-authored-by: Claude Opus 4.8 --- frontend/css/main.css | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/frontend/css/main.css b/frontend/css/main.css index bc86c56..e81b6c9 100644 --- a/frontend/css/main.css +++ b/frontend/css/main.css @@ -1145,6 +1145,12 @@ body { width: 440px; max-width: 90vw; box-shadow: var(--shadow); + /* Cap to the viewport and lay out as a column so a tall form (e.g. the directory-board + widget with many entries) scrolls inside .modal-body while the header/footer stay + pinned — otherwise the modal grows past the screen and Save becomes unreachable. */ + max-height: 90vh; + display: flex; + flex-direction: column; } .modal-header { @@ -1153,6 +1159,7 @@ body { justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--border); + flex-shrink: 0; } .modal-header h3 { @@ -1162,6 +1169,11 @@ body { .modal-body { padding: 20px; + /* Scroll region between the pinned header and footer. min-height:0 lets the flex item + shrink below its content so overflow-y actually engages inside the column. */ + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; } .modal-description { @@ -1176,6 +1188,7 @@ body { gap: 8px; padding: 16px 20px; border-top: 1px solid var(--border); + flex-shrink: 0; } /* Form Elements */ @@ -1465,7 +1478,8 @@ body { .info-grid { grid-template-columns: 1fr; } .remote-container { flex-direction: column; } .remote-controls { width: 100%; flex-direction: row; flex-wrap: wrap; } - .modal { width: 95vw; max-height: 90vh; overflow-y: auto; } + /* max-height + scrolling body are now on the base .modal (pinned header/footer); just widen. */ + .modal { width: 95vw; } .tabs { overflow-x: auto; -webkit-overflow-scrolling: touch;