Skip to content

fix(ui): modal bottom clipping/overflow on finance modals - #54

Merged
Dev869 merged 1 commit into
mainfrom
fix/modal-bottom-clipping
May 19, 2026
Merged

fix(ui): modal bottom clipping/overflow on finance modals#54
Dev869 merged 1 commit into
mainfrom
fix/modal-bottom-clipping

Conversation

@Dev869

@Dev869 Dev869 commented May 19, 2026

Copy link
Copy Markdown
Owner

Root cause

The modal surface is a flex column that fills the viewport (inset-0 on the
finance modals, h-full on the shared Modal) and applies
style={{ paddingTop: 'env(safe-area-inset-top)' }} to that same flex
container. 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 is
flex-1, and the footer is flex-shrink-0 last — so the extra height pushes
the 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 Modal has overflow-hidden so it
hard-clips the footer; the per-modal versions clip at the viewport edge.

Fix

  • Add box-border so the safe-area paddingTop is absorbed within the
    element height instead of inflating the box past the viewport.
  • Clamp the mobile surface to the dynamic viewport (max-h-[100dvh]) so the
    footer always renders inside the panel and mobile browser chrome is handled
    correctly (vs 100vh over-reporting).
  • Shared Modal keeps md:max-h-[88vh]; finance modals keep
    md:max-h-[85vh]. Footer retains its existing
    paddingBottom: max(…, env(safe-area-inset-bottom)) for comfortable
    bottom 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.tsx
  • web/src/components/finance/NewInvoiceModal.tsx

Shared vs individual

AddIncomeModal and NewInvoiceModal do not use the shared
common/Modal wrapper — they hand-roll the same surface markup, so each was
patched 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.
CsvImportModal uses inset-4 (inset panel, no safe-area top padding) and is
not affected by this pattern — left untouched to stay in scope.

Test plan

  • cd web && npx tsc -b → exit 0
  • npm run lint on touched files → no new errors (2 reported problems are
    pre-existing on main in an unrelated retainer useEffect)
  • npx vitest run → 75/75 passed
  • Manual: open Add Income / New Invoice on mobile + desktop — full footer
    visible with bottom spacing inside rounded corners; body scrolls when
    content is tall, footer stays pinned and uncut.

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.
@github-actions

Copy link
Copy Markdown

🔥 Firebase Hosting preview: https://open-ten99--pr-54-ikgg5sab.web.app
(expires in 7 days)

@Dev869
Dev869 merged commit 627a5d3 into main May 19, 2026
3 checks passed
@Dev869
Dev869 deleted the fix/modal-bottom-clipping branch May 19, 2026 03:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant