
Dropdown Menu
- 6 installs
- 6 repo stars
- Updated February 8, 2026
- ainergiz/design-inspirations
dropdown-menu is a Claude Code skill that builds React dropdown menus with correct click-outside detection and z-index stacking for card and list contexts.
About
dropdown-menu is a Claude Code skill that builds React dropdown menus which work correctly inside cards and list items. It addresses three common bugs: overflow clipping, sibling cards covering the menu, and double-toggle on the trigger click. A developer uses it when adding action, context, or overflow menus to list-based UI.
- Fixes clipping, sibling z-index stacking, and double-toggle dropdown bugs
- Uses click (not mousedown) for click-outside so stopPropagation works
- Elevates the parent card z-index when the menu is open
Dropdown Menu by the numbers
- 6 all-time installs (skills.sh)
- Ranked #1,778 of 2,244 Frontend Development skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
dropdown-menu capabilities & compatibility
- Capabilities
- dropdown menu · click outside · z index stacking
- Use cases
- frontend · ui design
- Pricing
- Free
What dropdown-menu says it does
Creates dropdown menus with proper click-outside detection and z-index stacking for list contexts.
Dropdown menus in list items have three common bugs:
npx skills add https://github.com/ainergiz/design-inspirations --skill dropdown-menuAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 6 |
|---|---|
| repo stars | ★ 6 |
| Last updated | February 8, 2026 |
| Repository | ainergiz/design-inspirations ↗ |
What it does
Building React dropdown menus that handle z-index stacking and click-outside dismissal correctly in card and list contexts.
Who is it for?
Action or context menus inside cards and list items where z-index and click-outside behavior break.
Skip if: Full accessible combobox or select-widget implementations.
When should I use this skill?
When building action menus, context menus, or any dropdown that appears in cards or list items.
What you get
A dropdown that dismisses on outside click, stacks above siblings, and does not double-toggle.
- DropdownMenu component with click-outside handling
- parent card z-index elevation pattern
By the numbers
- Solves 3 common dropdown bugs
- 4 key implementation elements
Files
Dropdown Menu Pattern
Build dropdown menus that work correctly in list/card contexts, handling z-index stacking and click-outside dismissal properly.
Why This Pattern?
Dropdown menus in list items have three common bugs:
1. Clipped by parent's `overflow-hidden` - dropdown gets cut off 2. Covered by sibling cards - z-index doesn't help across stacking contexts 3. Double-toggle on trigger click - menu closes then reopens immediately
This pattern solves all three.
Core Implementation
"use client";
import { useState, useRef, useEffect } from "react";
import { MoreVertical, Pause, X } from "lucide-react";
// The dropdown menu component
function DropdownMenu({
dark = false,
onClose,
}: {
dark?: boolean;
onClose: () => void;
}) {
const menuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
onClose();
}
}
// IMPORTANT: Use "click" not "mousedown" to allow stopPropagation on trigger
document.addEventListener("click", handleClickOutside);
return () => document.removeEventListener("click", handleClickOutside);
}, [onClose]);
return (
<div
ref={menuRef}
className={`absolute right-0 top-full mt-1 z-20 rounded-lg shadow-lg border overflow-hidden ${
dark ? "bg-zinc-800 border-zinc-700" : "bg-white border-zinc-200"
}`}
>
<button
className={`flex items-center gap-2 w-full px-3 py-2 text-xs font-medium transition-colors ${
dark
? "text-zinc-300 hover:bg-zinc-700"
: "text-zinc-700 hover:bg-zinc-50"
}`}
onClick={(e) => {
e.stopPropagation();
onClose();
}}
>
<Pause className="w-3.5 h-3.5" strokeWidth={1.5} />
Pause
</button>
<button
className={`flex items-center gap-2 w-full px-3 py-2 text-xs font-medium transition-colors ${
dark ? "text-red-400 hover:bg-zinc-700" : "text-red-600 hover:bg-red-50"
}`}
onClick={(e) => {
e.stopPropagation();
onClose();
}}
>
<X className="w-3.5 h-3.5" strokeWidth={1.5} />
Cancel
</button>
</div>
);
}Key Elements
1. Click-Outside Detection (Use click, NOT mousedown)
// CORRECT - allows stopPropagation on trigger button
document.addEventListener("click", handleClickOutside);
// WRONG - fires before button's onClick, causing double-toggle
document.addEventListener("mousedown", handleClickOutside);Why? With mousedown, the sequence is: 1. mousedown fires → click-outside closes menu 2. click fires on button → toggle reopens menu
With click, stopPropagation() on the button prevents the document listener from firing.
2. Parent Card Z-Index Elevation
When menu is open, elevate the entire parent card above siblings:
<div
className={`rounded-xl border cursor-pointer relative ${
menuOpen ? "z-30" : "z-0"
}`}
>
{/* card content with dropdown inside */}
</div>Why? Each card creates its own stacking context. The dropdown's z-20 only applies within its card. Sibling cards rendered later in the DOM naturally stack on top.
3. Avoid overflow-hidden on Dropdown Containers
// BAD - clips dropdown regardless of z-index
<div className="rounded-xl overflow-hidden">
<DropdownMenu />
</div>
// GOOD - only use overflow-hidden where needed (e.g., expandable sections)
<div className="rounded-xl">
<div className="relative">
<DropdownMenu />
</div>
<div className="overflow-hidden">
{/* expandable content only */}
</div>
</div>4. Trigger Button with stopPropagation
<div className="relative">
<button
onClick={(e) => {
e.stopPropagation(); // Prevents parent card click AND click-outside
onMenuToggle?.();
}}
className="p-1.5 -m-1.5 rounded-lg hover:bg-zinc-100 transition-colors cursor-pointer"
>
<MoreVertical className="w-5 h-5 text-zinc-400" strokeWidth={1.5} />
</button>
{menuOpen && onMenuClose && <DropdownMenu onClose={onMenuClose} />}
</div>Note the -m-1.5 negative margin - this increases the clickable area without affecting layout.
Full Card Example with Dropdown
interface CardProps {
title: string;
menuOpen?: boolean;
onMenuToggle?: () => void;
onMenuClose?: () => void;
}
function Card({ title, menuOpen = false, onMenuToggle, onMenuClose }: CardProps) {
return (
<div
className={`rounded-xl border border-zinc-200 p-4 cursor-pointer relative ${
menuOpen ? "z-30" : "z-0"
}`}
onClick={() => console.log("card clicked")}
>
<div className="flex items-center justify-between">
<span className="font-medium">{title}</span>
<div className="relative">
<button
onClick={(e) => {
e.stopPropagation();
onMenuToggle?.();
}}
className="p-1.5 -m-1.5 rounded-lg hover:bg-zinc-100 transition-colors"
>
<MoreVertical className="w-5 h-5 text-zinc-400" strokeWidth={1.5} />
</button>
{menuOpen && onMenuClose && <DropdownMenu onClose={onMenuClose} />}
</div>
</div>
</div>
);
}
// Parent component managing which menu is open
function CardList() {
const [openMenu, setOpenMenu] = useState<number | null>(null);
const items = ["Item 1", "Item 2", "Item 3"];
return (
<div className="flex flex-col gap-3">
{items.map((item, index) => (
<Card
key={index}
title={item}
menuOpen={openMenu === index}
onMenuToggle={() => setOpenMenu(openMenu === index ? null : index)}
onMenuClose={() => setOpenMenu(null)}
/>
))}
</div>
);
}Menu Positioning Options
// Below, right-aligned (default)
className="absolute right-0 top-full mt-1"
// Below, left-aligned
className="absolute left-0 top-full mt-1"
// Above, right-aligned
className="absolute right-0 bottom-full mb-1"
// Above, left-aligned
className="absolute left-0 bottom-full mb-1"Related: Tooltips in Stacked Items
When showing tooltips on items that have varying z-indexes (like stacked cards), the tooltip will be trapped in its parent's stacking context. The solution is to render the tooltip outside the item loop as a sibling element, calculating its position based on which item is hovered.
See the stacked-cards skill for the full pattern.
// WRONG - Tooltip trapped in parent's z-index
{items.map((item, i) => (
<div style={{ zIndex: items.length - i }}>
<Card />
{hovered === i && <Tooltip />} {/* Trapped! */}
</div>
))}
// CORRECT - Tooltip outside the loop
{items.map((item, i) => (
<div style={{ zIndex: items.length - i }}>
<Card />
</div>
))}
{hovered !== null && (
<Tooltip style={{ /* calculated position */ }} />
)}Checklist
- [ ] Click-outside uses
clickevent (notmousedown) - [ ] Parent card has conditional
z-30when menu is open - [ ] No
overflow-hiddenon containers that hold the dropdown - [ ] Trigger button has
stopPropagation()in onClick - [ ] Menu items have
stopPropagation()in onClick - [ ] Trigger wrapper has
relativepositioning - [ ] Dropdown has
absolutepositioning withtop-fullorbottom-full - [ ] For stacked items, tooltip rendered outside the loop (see stacked-cards skill)
Related skills
FAQ
What three bugs does dropdown-menu fix?
Clipping by overflow-hidden, sibling cards covering the menu, and double-toggle on the trigger click.
Why use click instead of mousedown?
click allows stopPropagation on the trigger button so the click-outside handler does not reopen the menu.