Summary
After #1481 fixed the close-on-scroll issue, a second bug in OverflowMenu portal mode surfaced: the menu DOM element stretches to fill (or exceed) the entire viewport. Its own bounding box covers the menu item button, intercepting pointer events meant for the item.
Reproduction
Same E2E spec as #1480: e2e/tests/invoices/invoice-deposits.spec.ts:665 (mobile, "Mark paid" flow). Playwright call log after #1481 lands:
```
- attempting click action
- element is visible, enabled and stable
-
…
intercepts pointer events
- retrying click action
-
- Jul 20, 2026
intercepts pointer events
- retrying click action
-
…
intercepts pointer events
```
The all-white failure screenshot from the second attempt visually confirms a viewport-covering element.
Root cause
OverflowMenu.tsx applies BOTH the inline portal positioning AND the .menuTop CSS class regardless of mode:
```jsx
className={`${styles.menu} ${usePortal ? styles.menuFixed : ''} ${placement === 'top-end' ? styles.menuTop : styles.menuBottom}`}
```
`.menuTop` is designed for non-portal (absolute) positioning:
```css
.menuTop {
bottom: 100%;
right: 0;
margin-bottom: var(--spacing-1);
}
```
In portal mode the inline style sets `position: fixed; top: ; right: ; transform: translateY(-100%)`. CSS `.menuTop` adds `bottom: 100%` on top of that. Because both `top` and `bottom` are set on a fixed-positioned element, the menu's height stretches to fill the viewport.
Proposed fix
Apply the `.menuTop`/`.menuBottom` classes only in non-portal mode. In portal mode, positioning is fully controlled by inline styles:
```jsx
className={`${styles.menu} ${usePortal ? styles.menuFixed : ''} ${!usePortal ? (placement === 'top-end' ? styles.menuTop : styles.menuBottom) : ''}`}
```
Acceptance criteria
Scope
- `client/src/components/OverflowMenu/OverflowMenu.tsx` (one-line className change)
- `client/src/components/OverflowMenu/OverflowMenu.test.tsx` (add portal-mode class-assertion test)
Blocks
Summary
After #1481 fixed the close-on-scroll issue, a second bug in
OverflowMenuportal mode surfaced: the menu DOM element stretches to fill (or exceed) the entire viewport. Its own bounding box covers the menu item button, intercepting pointer events meant for the item.Reproduction
Same E2E spec as #1480:
e2e/tests/invoices/invoice-deposits.spec.ts:665(mobile, "Mark paid" flow). Playwright call log after #1481 lands:```
```
The all-white failure screenshot from the second attempt visually confirms a viewport-covering element.
Root cause
OverflowMenu.tsxapplies BOTH the inline portal positioning AND the.menuTopCSS class regardless of mode:```jsx
className={`${styles.menu} ${usePortal ? styles.menuFixed : ''} ${placement === 'top-end' ? styles.menuTop : styles.menuBottom}`}
```
`.menuTop` is designed for non-portal (absolute) positioning:
```css
.menuTop {
bottom: 100%;
right: 0;
margin-bottom: var(--spacing-1);
}
```
In portal mode the inline style sets `position: fixed; top: ; right: ; transform: translateY(-100%)`. CSS `.menuTop` adds `bottom: 100%` on top of that. Because both `top` and `bottom` are set on a fixed-positioned element, the menu's height stretches to fill the viewport.
Proposed fix
Apply the `.menuTop`/`.menuBottom` classes only in non-portal mode. In portal mode, positioning is fully controlled by inline styles:
```jsx
className={`${styles.menu} ${usePortal ? styles.menuFixed : ''} ${!usePortal ? (placement === 'top-end' ? styles.menuTop : styles.menuBottom) : ''}`}
```
Acceptance criteria
Scope
Blocks