Make hover menus less fragile.
Dropdowns close when your cursor crosses the gap between a trigger and its menu. hover-bridge adds an invisible safe area — one class, zero JavaScript.
Try it yourself
Move from Products to the menu. The left card drops hover; the right card keeps it.
Without
✕ Menu closesHover me
Move from Products to the menu.
With bridge
✓ Menu stays openHover me
Move from Products to the menu.
Breakdown
Five small steps, from a normal hover menu to one that survives a little mouse drift.
01
Start with a normal hover menu
A trigger and dropdown inside one group hover container. Hovering the trigger reveals the menu with group-hover.
<div class="menu group">
<button>Products</button>
<div class="dropdown">...</div>
</div>02
Add a gap between trigger and dropdown
Most menus need breathing room, so a margin-top or offset creates space between the trigger and the panel — and an accidental dead zone with it.
.dropdown {
margin-top: 1rem;
}03
Hover is lost in the gap
When the cursor leaves the trigger and crosses empty space, the parent is no longer hovered. The menu closes before you reach it.
04
Add an invisible pseudo-element bridge
hover-bridge adds a ::before element directly below the trigger. It spans the gap with pointer-events: auto, keeping hover alive without changing layout.
<div class="hover-bridge hover-bridge-md">
<button>Products</button>
<div class="dropdown">...</div>
</div>05
Add debug mode to reveal the bridge
During development, hover-bridge-debug shows the invisible bridge so you can tune --hover-bridge-size for your gap.
<div class="hover-bridge hover-bridge-md hover-bridge-debug">Code
The utility is intentionally small. The base class uses position: relative and a ::before pseudo-element at top: 100%. Size variants like hover-bridge-md control the bridge height through --hover-bridge-size.
/**
* hover-bridge — invisible hover bridge for dropdown menus.
* Source: https://github.com/ShervNariman/hover-bridge
*/
.hover-bridge {
position: relative;
--hover-bridge-size: 1rem;
}
.hover-bridge::before {
content: "";
position: absolute;
top: 100%;
left: 0;
width: 100%;
height: var(--hover-bridge-size);
pointer-events: auto;
}
.hover-bridge-sm {
--hover-bridge-size: 0.5rem;
}
.hover-bridge-md {
--hover-bridge-size: 1rem;
}
.hover-bridge-lg {
--hover-bridge-size: 1.75rem;
}
.hover-bridge.hover-bridge-debug::before {
background: repeating-linear-gradient(
-45deg,
rgba(59, 130, 246, 0.18) 0,
rgba(59, 130, 246, 0.18) 6px,
rgba(59, 130, 246, 0.06) 6px,
rgba(59, 130, 246, 0.06) 12px
);
outline: 1px dashed rgba(59, 130, 246, 0.35);
outline-offset: -1px;
}Usage
hover-bridge is CSS-only — it adds styles, not React components. Install the file, import it, then add the class to your menu markup.
Recommended — install with shadcn:
pnpm dlx shadcn@latest add ShervNariman/hover-bridge/hover-bridgeThis installs styles/hover-bridge.css. Import it in your app layout or global styles:
import "@/styles/hover-bridge.css";Wrap your trigger and dropdown, then match the size variant to your gap:
<div className="hover-bridge hover-bridge-md">Use hover-bridge-sm, hover-bridge-md, or hover-bridge-lg to match the gap.
npm package coming soon.
Fallback — copy registry/hover-bridge/hover-bridge.css from GitHub into your project.
Limitations
- ·Assumes the trigger and menu share one hover container — restructure the markup first if they don't.
- ·A layout aid, not an accessibility layer — pair it with keyboard-navigable menu components.
- ·Sized in fixed steps (
hover-bridge-sm,hover-bridge-md,hover-bridge-lg); very unusual gaps may need a custom--hover-bridge-size.