Repository navigation
Description
Bug report
Current behavior
When the toast under the mouse is removed by the mouse itself (a click on its close button, or a swipe), the viewport stays expanded (data-expanded) after the pointer leaves it. The stack collapses only after the pointer enters a toast again and leaves.
Steps, with three toasts stacked (timeout: 0), in Chrome with a real mouse:
- Hover the top toast. The viewport expands.
- Remove it with the mouse: click its
Toast.Close, or swipe it away. - Move the pointer off the viewport, e.g. to the page's top-left corner.
- The viewport still has
data-expanded(checked 600ms later).
Expected behavior
The viewport collapses once the pointer has left it, as it does when no toast was removed.
Reproducible example
https://codesandbox.io/p/sandbox/kw2pmc
Steps are on the page. It needs real pointer input: synthetic events don't show it.
It also depends on layout. In the sandbox, the toasts sit in normal flow in a column anchored to the bottom of the viewport, so after the hovered toast is removed, nothing moves under the pointer. With the column anchored to the top, the next toast slides under the pointer, Chrome fires the mouseout, and the click case collapses as expected. The swipe case stays stuck either way.
Base UI version
v1.9.0
Which browser are you using?
Chrome (Chromium via Playwright)
Which OS are you using?
macOS
Which assistive tech are you using (if applicable)?
None
Additional context
What we see in the event log: after the hovered toast's element is removed, the next mouse move dispatches mouseover on the element now under the pointer (outside the viewport), with a relatedTarget still inside the React tree, and no mouseout from the viewport. React's enter/leave plugin ignores a mouseover whose relatedTarget it manages and waits for the matching mouseout, which never comes, so ToastViewport's onMouseLeave (handleMouseLeave) never runs and hovering stays true.
Our workaround: a document listener that tracks whether the pointer was last seen inside the viewport; when the next mouseover lands outside it with no leave in between, it dispatches a mouseout on the viewport (relatedTarget set to the new target), which React turns into the missed onMouseLeave.
A fix in Base UI might listen for native pointerleave/mouseleave on the viewport, or re-check whether the pointer is still over the viewport (e.g. on the next pointermove on the document) after a toast is removed while hovering is set.
Activity
lord-0011 commentedon Nov 7, 2026
Hi! I reproduced the close-button case with @base-ui/react 1.9.0 and real mouse input (Playwright): after closing the hovered toast and moving the pointer away, the viewport keeps
data-expanded(3/3 runs; hover-and-leave without removing a toast collapses normally). I couldn't reproduce the swipe case in my setup.I'd like to fix it: while the viewport is expanded, listen for
mouseoveron the document, and if the pointer lands outside the viewport whilehoveringis still set, run the existing mouse-leave handling (so the transition and touch-swipe deferrals still apply). With that change the stack collapses in all my runs. I'd add a regression test to ToastViewport.test.tsx. Does that approach work for you?