fix(ui): modal bottom clipping/overflow on finance modals - #54
Merged
Conversation
Modal surface filled the viewport (inset-0 / h-full) while also applying paddingTop: env(safe-area-inset-top) on the same flex container. With content-box sizing the safe-area padding inflated the box past the viewport, pushing the flex-shrink-0 footer below the visible bottom edge where it was clipped (no bottom padding / rounded-corner spacing). Fix: add box-border so safe-area padding is absorbed within the height instead of overflowing, and clamp the mobile surface to the dynamic viewport (max-h-[100dvh]) so the footer always renders inside the rounded panel with comfortable spacing. Layout/overflow only — no behavior, field, or action changes.
|
🔥 Firebase Hosting preview: https://open-ten99--pr-54-ikgg5sab.web.app |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
The modal surface is a flex column that fills the viewport (
inset-0on thefinance modals,
h-fullon the sharedModal) and appliesstyle={{ paddingTop: 'env(safe-area-inset-top)' }}to that same flexcontainer. With content-box sizing the safe-area top padding is added on top
of the viewport-sized height, so the rendered box becomes
100vh + safe-area-inset-top. The header is fixed-height, the body isflex-1, and the footer isflex-shrink-0last — so the extra height pushesthe footer (Cancel / Add Income / Create Invoice) below the visible viewport
bottom, where it is clipped flush against the edge with no bottom padding or
rounded-corner spacing. The shared
Modalhasoverflow-hiddenso ithard-clips the footer; the per-modal versions clip at the viewport edge.
Fix
box-borderso the safe-areapaddingTopis absorbed within theelement height instead of inflating the box past the viewport.
max-h-[100dvh]) so thefooter always renders inside the panel and mobile browser chrome is handled
correctly (vs
100vhover-reporting).Modalkeepsmd:max-h-[88vh]; finance modals keepmd:max-h-[85vh]. Footer retains its existingpaddingBottom: max(…, env(safe-area-inset-bottom))for comfortablebottom spacing inside the rounded corners.
Layout/overflow only — no behavior, field, or action changes.
Files fixed
web/src/components/common/Modal.tsx(shared wrapper)web/src/components/finance/AddIncomeModal.tsxweb/src/components/finance/NewInvoiceModal.tsxShared vs individual
AddIncomeModalandNewInvoiceModaldo not use the sharedcommon/Modalwrapper — they hand-roll the same surface markup, so each waspatched with the identical minimal fix for consistency. The shared
common/Modal(used by NewWorkOrderModal, AppFormModal, NotionPagePicker,Clients/Quotes/TimeLogs) was fixed once so all its consumers benefit.
CsvImportModalusesinset-4(inset panel, no safe-area top padding) and isnot affected by this pattern — left untouched to stay in scope.
Test plan
cd web && npx tsc -b→ exit 0npm run linton touched files → no new errors (2 reported problems arepre-existing on
mainin an unrelated retaineruseEffect)npx vitest run→ 75/75 passedvisible with bottom spacing inside rounded corners; body scrolls when
content is tall, footer stays pinned and uncut.