---
title: "safe triangles, and the submenu you cannot reach"
url: "https://sanyam.sh/blogs/the-submenu-closes-before-you-get-there"
date: "2026-08-31"
readTime: "4 min read"
description: "Aim at the bottom of a submenu and your cursor cuts across three rows on the way. Every one is a real hover, and the menu believes all of them. The fix is a triangle, and it has a name."
---

# safe triangles, and the submenu you cannot reach

Every menu with a submenu has this bug. You open it, aim at something near the
bottom, and it shuts on you halfway there. So you learn to move sideways first
and then down, in an L, like you are defusing a bomb. Nobody taught you that.

Two menus below. Identical rows, identical submenu. One of them has a fix in it.

_An interactive demo runs here on the page._

## the fix

Your cursor crossed Copy link and Add label on the way. Those are real hovers,
and a menu that answers them closes the submenu, because that is what pointing at
another row means. The bug is that being right about each event adds up to being
wrong about the hand.

So while the cursor is on its way to the submenu, the rows it crosses stop
counting. On its way is a triangle: the point it left the row, and the submenu's
near edge. Three cross products decide whether a point is inside it.

```ts
const side = (a: Point, b: Point, p: Point) =>
  (b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x);

export function inGrace(p: Point, g: Grace): boolean {
  const a = side(g.from, g.top, p);
  const b = side(g.top, g.bottom, p);
  const c = side(g.bottom, g.from, p);
  return (a >= 0 && b >= 0 && c >= 0) || (a <= 0 && b <= 0 && c <= 0);
}
```

It has a name. [Ben Kamens](https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown)
reverse-engineered it out of Amazon's mega dropdown in 2013 and shipped it as a
jQuery plugin called menu-aim, and it is usually called a **safe triangle**. You
will also see safe area, safe polygon, or Amazon's own prediction cone. Component
libraries tend to say polygon rather than triangle, because a submenu that can
open above or below its trigger needs more than three points. Ours only opens to
the right, so three is enough. None of it is new: hierarchical menus on the Mac
were doing this in the mid-eighties.

Do not confuse it with hover intent, which is the other family and the one this
post is arguing against. Those measure how long or how fast you have hovered. Same
problem, opposite signal.

The usual answer instead is a timer, three hundred milliseconds before anything
closes, and it is worse in both directions: it shuts on you if you pause to read
a label, and it hangs around after you have plainly left. Time is not the signal.
Direction is, and a timer cannot see direction.

_An interactive demo runs here on the page._
