Prediction Cone for Nested Menus
SHOULD: A submenu that opens to the side must forgive diagonal pointer travel toward it. Do not swap or close it the instant the pointer leaves the parent row: use a safe-triangle/prediction cone (suppress sibling hover while the pointer aims at the open submenu) or, as a cheap approximation, a ~150ms close-delay that any re-entry into the submenu cancels.
When a submenu opens on hover, forgive diagonal pointer travel toward it instead of closing the instant the pointer leaves the parent row
onMouseEnter={(id) => { clearTimeout(t); t = setTimeout(() => setActive(id), 160); }}
// submenu panel: onMouseEnter={() => clearTimeout(t)}Bad
Good
Why it matters
The naive nested menu closes the submenu the moment the pointer leaves the parent item. That is exactly wrong, because the submenu opens to the side, so the shortest path to it runs diagonally across the sibling rows below the one you are pointing at. Each sibling the pointer crosses fires its own hover, swaps the open submenu out for its own, and the option you were reaching for vanishes before you arrive.
The user learns to travel in an L — straight down the border, then across — which is the tell of a menu that does not forgive its own geometry. The fix has two well-known shapes. The "prediction cone" (or "safe triangle", the Amazon mega-menu trick Ben Kamens documented) reads pointer velocity and builds a triangle between the cursor and the top and bottom corners of the open submenu; while the pointer stays inside that triangle it is presumed to be aiming AT the submenu, so sibling hovers are suppressed for a short grace window.
The cheaper approximation is a short close-delay (~100–200ms) on the submenu that any pointer re-entry cancels. This is the same forgiveness principle as interactions-forgiving-design and interactions-hover-content-persistence (hover targets must be reachable and not expire on a timer); it pairs with interactions-mousedown-dropdown, which handles the open, where this handles the traverse.