ui / overlay
PREVIEW READYDropdown Menu
A compact action menu with outside click and Escape handling.
INSTALL / SOURCE REGISTRY
Add Dropdown Menu to your project
The command adds the component source and its DesignOS base dependency.
$
pnpm dlx shadcn@latest add https://designos.h4rithd.com/r/dropdown-menu.jsonregistry/default/ui/dropdown-menu.tsx
"use client"
import * as React from "react"
import { clsx as cn } from "clsx"
import "@/components/designos/designos.css"
export type DropdownItem = { label: string; icon?: React.ReactNode; onSelect: () => void; disabled?: boolean; danger?: boolean }
export function DropdownMenu({ trigger, items, label = "Actions", className }: { trigger: React.ReactElement; items: DropdownItem[]; label?: string; className?: string }) {
const [open, setOpen] = React.useState(false)
const root = React.useRef<HTMLDivElement>(null)
React.useEffect(() => {
const close = (event: PointerEvent) => { if (!root.current?.contains(event.target as Node)) setOpen(false) }
document.addEventListener("pointerdown", close)
return () => document.removeEventListener("pointerdown", close)
}, [])
return <div ref={root} className={cn("dos-popover", className)} onKeyDown={(event) => { if (event.key === "Escape") setOpen(false) }}>
{React.cloneElement(trigger, { "aria-haspopup": "menu", "aria-expanded": open, onClick: () => setOpen((current) => !current) } as React.HTMLAttributes<HTMLElement>)}
{open && <div role="menu" aria-label={label} className="dos-dropdown">{items.map((item) => <button key={item.label} role="menuitem" type="button" disabled={item.disabled} className="dos-dropdown__item" style={item.danger ? { color: "var(--dos-danger)" } : undefined} onClick={() => { item.onSelect(); setOpen(false) }}>{item.icon}{item.label}</button>)}</div>}
</div>
}
Component contract
- Source-owned after installation
- Dark and light theme variables
- Keyboard and visible-focus behavior
- Reduced-motion fallback
- Responsive from 320px upward
- IBM Plex Sans + JetBrains Mono hierarchy