Dropdown closes when moving mouse problems usually happen because there is a hover gap between the trigger and the menu, the dropdown is positioned too far away, pointer-events are disabled, or the menu is being covered by another layer.
Dropdown Hover Fix
Why is my dropdown closing when I move the mouse?
A dropdown can open correctly and still close the moment you move your mouse toward it. That usually means the mouse leaves the hover area before it reaches the menu. The most common cause is an invisible gap between the trigger and the dropdown, but z-index, pointer-events, positioning, and mobile hover behavior can create the same frustrating bug.
- Hover gap
- Pointer events
- z-index
- Mobile menu
What the bug looks like
The dropdown opens on hover, but disappears before your cursor reaches the menu item you want to click.
Why it happens
The hover state is attached to an area that does not include the full path from trigger to dropdown.
What usually fixes it
Remove the hover gap, add a hover bridge, keep the dropdown inside the hoverable parent, and avoid hover-only menus on mobile.
There is a gap between the trigger and the dropdown
The dropdown closes because the mouse leaves the hoverable parent while crossing the empty space between the button and the menu. Visually, the gap may look tiny, but to CSS hover it is a complete state break.
Broken code
Hover dead zone.nav-item {
position: relative;
}
.dropdown {
position: absolute;
top: 70px;
left: 0;
}
.nav-item:hover .dropdown {
display: block;
}
Broken visual result
Correct code
Hover bridge.nav-item {
position: relative;
}
.dropdown {
position: absolute;
top: 100%;
left: 0;
padding-top: 12px;
}
.dropdown::before {
content: "";
position: absolute;
left: 0;
right: 0;
top: -12px;
height: 12px;
}
Fixed visual result
The dropdown is shown on the wrong hover target
If the hover is attached only to the button, the dropdown closes as soon as the cursor leaves the button. The hover should usually live on a wrapper that contains both the trigger and the menu.
Broken code
Button-only hover.menu-button:hover + .dropdown {
display: block;
}
.dropdown {
display: none;
}
Broken visual result
Correct code
Wrapper hover.nav-item:hover .dropdown,
.nav-item:focus-within .dropdown {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
.nav-item {
position: relative;
}
Fixed visual result
pointer-events makes the dropdown unusable
Some dropdowns are hidden with opacity and visibility, but developers forget to restore pointer-events when the menu opens. The result feels like the dropdown closes or cannot be reached, even though it is visible.
Broken code
Clicks pass through.dropdown {
opacity: 0;
pointer-events: none;
}
.nav-item:hover .dropdown {
opacity: 1;
/* pointer-events still none */
}
Broken visual result
Correct code
Interactive open state.dropdown {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.nav-item:hover .dropdown,
.nav-item:focus-within .dropdown {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
Fixed visual result
The dropdown is hover-only on mobile
Touch devices do not behave like desktop hover. A mobile dropdown that depends only on :hover can open inconsistently, close immediately, or trap the user. Mobile menus usually need a click/tap state.
Broken code
Hover-only mobile menu.nav-item:hover .dropdown {
display: block;
}
@media (max-width: 768px) {
.dropdown {
position: absolute;
}
}
Broken visual result
The menu depends on hover, which is not a reliable mobile interaction.
Tap menuCorrect code
Click/tap state.dropdown {
display: none;
}
.nav-item.is-open .dropdown {
display: block;
}
@media (hover: hover) {
.nav-item:hover .dropdown {
display: block;
}
}
Fixed visual result
The menu can use tap/click state on mobile and hover only where hover exists.
Tap menuA production-minded dropdown hover pattern
A strong dropdown pattern keeps the trigger and menu inside one parent, removes hover gaps, supports keyboard focus with :focus-within, restores pointer events when open, and treats touch screens differently from hover devices.
Premium code
Safe dropdown system.nav-item {
position: relative;
}
.dropdown {
position: absolute;
top: 100%;
left: 0;
min-width: 220px;
padding-top: 12px;
opacity: 0;
visibility: hidden;
pointer-events: none;
transform: translateY(6px);
transition: .18s ease;
z-index: 100;
}
.dropdown::before {
content: "";
position: absolute;
left: 0;
right: 0;
top: -12px;
height: 12px;
}
.nav-item:hover .dropdown,
.nav-item:focus-within .dropdown,
.nav-item.is-open .dropdown {
opacity: 1;
visibility: visible;
pointer-events: auto;
transform: translateY(0);
}
@media (hover: none) {
.nav-item:hover .dropdown {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.nav-item.is-open .dropdown {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
}
Premium visual result
Fast practical rule
If a dropdown closes when moving mouse, look for the gap first. Move the dropdown closer to the trigger, keep both inside the same hoverable parent, and add an invisible hover bridge before touching complicated JavaScript.
Debug checklist
- Move the mouse slowly from the trigger to the dropdown and watch where it closes.
- Check whether there is a visible or invisible gap between the button and the menu.
- Attach hover to the wrapper that contains both the trigger and the dropdown.
- Add
:focus-withinso keyboard users can keep the dropdown open. - Restore
pointer-events:autowhen the menu is open. - Check whether another layer is covering the dropdown with a higher
z-index. - Avoid hover-only behavior on touch screens.
- Use click/tap state for mobile menus and hover as a desktop enhancement.
pointer-events:none is still active.
Final takeaway
A dropdown closes when moving mouse because CSS hover is unforgiving. If the cursor leaves the hoverable area for even a moment, the dropdown closes. Most of the time, the fix is not complicated JavaScript. It is a better hover area.
Keep the trigger and menu inside one wrapper, remove the gap, add a hover bridge, restore pointer events, and use click/tap state for mobile. The dropdown will feel stable because the interaction path is stable.