ComponentsCSS
Dropdown Menu
Action menu with labels, shortcuts, checkable items and a danger tone.
Installation
npx shadcn@latest add https://ui-collections.vercel.app/r/tide/dropdown-menu.jsonAlso installs the theme, cn(). Packages: clsx, tailwind-merge, radix-ui.
Usage
dropdown-menu-demo.tsx
"use client";
import { useState } from "react";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from "@/registry/kit/ui/dropdown-menu";
import { Button } from "@/registry/kit/ui/button";
const icon = (d: string) => (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
<path d={d} />
</svg>
);
export default function DropdownMenuDemo() {
const [logs, setLogs] = useState(true);
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button>Deployment actions</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>Production</DropdownMenuLabel>
<DropdownMenuItem>
{icon("M5 12h14M13 6l6 6-6 6")}
Visit
<DropdownMenuShortcut>⌘O</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
{icon("M4 12a8 8 0 0 1 13.7-5.6M18 3v4h-4")}
Redeploy
<DropdownMenuShortcut>⌘R</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem disabled>
{icon("M12 5v14M5 12h14")}
Promote to production
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuCheckboxItem checked={logs} onCheckedChange={setLogs}>
Stream logs
</DropdownMenuCheckboxItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="danger">
{icon("M5 7h14M10 7V5h4v2M7 7l1 12h8l1-12")}
Delete deployment
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}API
<DropdownMenuItem variant>= "default"
"default" | "danger"Destructive actions turn rose.
PartsCheckboxItem, Label, Separator, Shortcut, GroupStyled Radix menu parts.
Source
components/ui/dropdown-menu.tsx
"use client";
import { DropdownMenu as MenuPrimitive } from "radix-ui";
import type { ComponentProps, HTMLAttributes } from "react";
import { cn } from "@/registry/kit/lib/utils";
export const DropdownMenu = MenuPrimitive.Root;
export const DropdownMenuTrigger = MenuPrimitive.Trigger;
export const DropdownMenuGroup = MenuPrimitive.Group;
export function DropdownMenuContent({
className,
sideOffset = 8,
align = "start",
...props
}: ComponentProps<typeof MenuPrimitive.Content>) {
return (
<MenuPrimitive.Portal>
<MenuPrimitive.Content
sideOffset={sideOffset}
align={align}
className={cn(
"surface-raised z-50 min-w-52 rounded-field p-1.5 text-fg outline-none",
"origin-(--radix-dropdown-menu-content-transform-origin) transition-[opacity,scale] duration-200 ease-theme starting:scale-95 starting:opacity-0 data-[state=closed]:animate-pop-out",
className,
)}
{...props}
/>
</MenuPrimitive.Portal>
);
}
const itemClass = cn(
"relative flex items-center gap-2.5 rounded-[max(0.25rem,calc(var(--r-field)-0.375rem))] px-3 py-2 text-sm text-fg/85 outline-none select-none",
"transition-colors data-highlighted:bg-fg/10 data-highlighted:text-fg data-disabled:pointer-events-none data-disabled:opacity-40",
"[&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted data-highlighted:[&_svg]:text-fg",
);
export function DropdownMenuItem({
className,
variant = "default",
...props
}: ComponentProps<typeof MenuPrimitive.Item> & { variant?: "default" | "danger" }) {
return (
<MenuPrimitive.Item
className={cn(
itemClass,
// The base highlight turns icons to fg; a danger item's icon keeps its colour.
variant === "danger" && "text-danger data-highlighted:bg-danger/15 data-highlighted:text-danger [&_svg]:text-danger data-highlighted:[&_svg]:text-danger",
className,
)}
{...props}
/>
);
}
export function DropdownMenuCheckboxItem({ className, children, ...props }: ComponentProps<typeof MenuPrimitive.CheckboxItem>) {
return (
<MenuPrimitive.CheckboxItem className={cn(itemClass, "pl-9", className)} {...props}>
<span className="absolute left-3 grid size-4 place-items-center">
<MenuPrimitive.ItemIndicator>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round" className="text-accent!" aria-hidden>
<path d="M5 12.5l4.5 4.5L19 7.5" />
</svg>
</MenuPrimitive.ItemIndicator>
</span>
{children}
</MenuPrimitive.CheckboxItem>
);
}
export function DropdownMenuLabel({ className, ...props }: ComponentProps<typeof MenuPrimitive.Label>) {
return <MenuPrimitive.Label className={cn("px-3 pt-2 pb-1 text-xs text-muted", className)} {...props} />;
}
export function DropdownMenuSeparator({ className, ...props }: ComponentProps<typeof MenuPrimitive.Separator>) {
return <MenuPrimitive.Separator className={cn("mx-2 my-1.5 h-px bg-fg/10", className)} {...props} />;
}
export function DropdownMenuShortcut({ className, ...props }: HTMLAttributes<HTMLSpanElement>) {
return <span className={cn("ml-auto pl-4 font-mono text-[11px] text-muted", className)} {...props} />;
}