Components
YA Dropdown Menu
A dropdown menu component for displaying a list of actions or options
The YaDropdownMenu is a floating menu in the YaVendio Design System. It supports text items, checkbox items, radio items, submenus, labels, separators, and keyboard navigation. Built on top of Radix UI primitives for full accessibility.
Quick Start
pnpm dlx shadcn@latest add @yavendio/ya-dropdown-menunpx shadcn@latest add @yavendio/ya-dropdown-menuyarn dlx shadcn@latest add @yavendio/ya-dropdown-menuimport {
YaDropdownMenu,
YaDropdownMenuTrigger,
YaDropdownMenuContent,
YaDropdownMenuItem,
} from '@/components/ui/ya-dropdown-menu'
import { YaButton } from '@/components/ui/ya-button'
export default function Example() {
return (
<YaDropdownMenu>
<YaDropdownMenuTrigger asChild>
<YaButton variant="secondary">Open Menu</YaButton>
</YaDropdownMenuTrigger>
<YaDropdownMenuContent>
<YaDropdownMenuItem>Choose service</YaDropdownMenuItem>
<YaDropdownMenuItem>Add promotion</YaDropdownMenuItem>
<YaDropdownMenuItem>View details</YaDropdownMenuItem>
</YaDropdownMenuContent>
</YaDropdownMenu>
)
}Basic Dropdown Menu
Item Types
Text Item with Icons
Items can include icons on the left for better visual hierarchy.
import { User, Settings, Folder, LogOut } from 'lucide-react'
<YaDropdownMenuItem icon={<User className="size-[14px]" />}>Profile</YaDropdownMenuItem>
<YaDropdownMenuItem icon={<Settings className="size-[14px]" />}>Settings</YaDropdownMenuItem>
<YaDropdownMenuItem icon={<Folder className="size-[14px]" />}>Documents</YaDropdownMenuItem>
<YaDropdownMenuItem icon={<LogOut className="size-[14px]" />}>Logout</YaDropdownMenuItem>Item with Description
Items can have a secondary description text for additional context.
<YaDropdownMenuItem
icon={<Folder className="size-[14px]" />}
description="View all your documents"
>
Documents
</YaDropdownMenuItem>Selected Item
Items can show a selected state with a checkmark.
<YaDropdownMenuItem selected>Selected option</YaDropdownMenuItem>
<YaDropdownMenuItem>Other option</YaDropdownMenuItem>Checkbox Items
For multi-select options within the menu.
import { useState } from 'react'
import {
YaDropdownMenu,
YaDropdownMenuTrigger,
YaDropdownMenuContent,
YaDropdownMenuCheckboxItem,
} from '@/components/ui/ya-dropdown-menu'
export default function CheckboxExample() {
const [showStatus, setShowStatus] = useState(true)
const [showActivity, setShowActivity] = useState(false)
const [showPanel, setShowPanel] = useState(false)
return (
<YaDropdownMenu>
<YaDropdownMenuTrigger asChild>
<YaButton variant="tertiary">View Options</YaButton>
</YaDropdownMenuTrigger>
<YaDropdownMenuContent>
<YaDropdownMenuCheckboxItem
checked={showStatus}
onCheckedChange={setShowStatus}
>
Show Status Bar
</YaDropdownMenuCheckboxItem>
<YaDropdownMenuCheckboxItem
checked={showActivity}
onCheckedChange={setShowActivity}
>
Show Activity Panel
</YaDropdownMenuCheckboxItem>
<YaDropdownMenuCheckboxItem
checked={showPanel}
onCheckedChange={setShowPanel}
>
Show Side Panel
</YaDropdownMenuCheckboxItem>
</YaDropdownMenuContent>
</YaDropdownMenu>
)
}Radio Items
For single-select options within a group.
import { useState } from 'react'
import {
YaDropdownMenu,
YaDropdownMenuTrigger,
YaDropdownMenuContent,
YaDropdownMenuRadioGroup,
YaDropdownMenuRadioItem,
} from '@/components/ui/ya-dropdown-menu'
export default function RadioExample() {
const [position, setPosition] = useState('center')
return (
<YaDropdownMenu>
<YaDropdownMenuTrigger asChild>
<YaButton variant="tertiary">Position: {position}</YaButton>
</YaDropdownMenuTrigger>
<YaDropdownMenuContent>
<YaDropdownMenuRadioGroup value={position} onValueChange={setPosition}>
<YaDropdownMenuRadioItem value="top">Top</YaDropdownMenuRadioItem>
<YaDropdownMenuRadioItem value="center">Center</YaDropdownMenuRadioItem>
<YaDropdownMenuRadioItem value="bottom">Bottom</YaDropdownMenuRadioItem>
</YaDropdownMenuRadioGroup>
</YaDropdownMenuContent>
</YaDropdownMenu>
)
}Groups and Labels
Organize items into logical groups with labels and separators.
<YaDropdownMenuContent>
<YaDropdownMenuLabel>Categories</YaDropdownMenuLabel>
<YaDropdownMenuSeparator />
<YaDropdownMenuItem icon={<Folder />} description="All active projects">
Projects
</YaDropdownMenuItem>
<YaDropdownMenuItem icon={<Folder />} description="Archived items">
Archive
</YaDropdownMenuItem>
<YaDropdownMenuLabel>Time Period</YaDropdownMenuLabel>
<YaDropdownMenuSeparator />
<YaDropdownMenuItem>Last 7 days</YaDropdownMenuItem>
<YaDropdownMenuItem>Last 30 days</YaDropdownMenuItem>
</YaDropdownMenuContent>Submenus
Create nested menus for hierarchical navigation.
import {
YaDropdownMenu,
YaDropdownMenuTrigger,
YaDropdownMenuContent,
YaDropdownMenuItem,
YaDropdownMenuSub,
YaDropdownMenuSubTrigger,
YaDropdownMenuSubContent,
} from '@/components/ui/ya-dropdown-menu'
import { Folder } from 'lucide-react'
export default function SubmenuExample() {
return (
<YaDropdownMenu>
<YaDropdownMenuTrigger asChild>
<YaButton variant="secondary">Open Menu</YaButton>
</YaDropdownMenuTrigger>
<YaDropdownMenuContent>
<YaDropdownMenuItem icon={<Folder className="size-[14px]" />}>
New File
</YaDropdownMenuItem>
<YaDropdownMenuSub>
<YaDropdownMenuSubTrigger
icon={<Folder className="size-[14px]" />}
description="Open recent files"
>
Recent Files
</YaDropdownMenuSubTrigger>
<YaDropdownMenuSubContent>
<YaDropdownMenuItem icon={<Folder className="size-[14px]" />}>
document.pdf
</YaDropdownMenuItem>
<YaDropdownMenuItem icon={<Folder className="size-[14px]" />}>
report.xlsx
</YaDropdownMenuItem>
</YaDropdownMenuSubContent>
</YaDropdownMenuSub>
<YaDropdownMenuItem icon={<Folder className="size-[14px]" />}>
Save
</YaDropdownMenuItem>
</YaDropdownMenuContent>
</YaDropdownMenu>
)
}Disabled Items
Items can be disabled to prevent interaction.
<YaDropdownMenuItem>Edit</YaDropdownMenuItem>
<YaDropdownMenuItem>Duplicate</YaDropdownMenuItem>
<YaDropdownMenuItem disabled>Archive (unavailable)</YaDropdownMenuItem>
<YaDropdownMenuItem disabled>Delete (no permission)</YaDropdownMenuItem>Examples
Context Menu Actions
<YaDropdownMenuContent>
<YaDropdownMenuItem icon={<Edit className="size-[14px]" />}>Edit</YaDropdownMenuItem>
<YaDropdownMenuItem icon={<Copy className="size-[14px]" />}>Duplicate</YaDropdownMenuItem>
<YaDropdownMenuSeparator />
<YaDropdownMenuItem icon={<Trash className="size-[14px]" />}>Delete</YaDropdownMenuItem>
</YaDropdownMenuContent>User Account Menu
import { User, Settings, CreditCard, LogOut } from 'lucide-react'
<YaDropdownMenu>
<YaDropdownMenuTrigger asChild>
<YaButton variant="secondary" buttonType="icon">
<User />
</YaButton>
</YaDropdownMenuTrigger>
<YaDropdownMenuContent align="end">
<YaDropdownMenuLabel>My Account</YaDropdownMenuLabel>
<YaDropdownMenuSeparator />
<YaDropdownMenuItem icon={<User className="size-[14px]" />}>Profile</YaDropdownMenuItem>
<YaDropdownMenuItem icon={<CreditCard className="size-[14px]" />}>Billing</YaDropdownMenuItem>
<YaDropdownMenuItem icon={<Settings className="size-[14px]" />}>Settings</YaDropdownMenuItem>
<YaDropdownMenuSeparator />
<YaDropdownMenuItem icon={<LogOut className="size-[14px]" />}>Log out</YaDropdownMenuItem>
</YaDropdownMenuContent>
</YaDropdownMenu>Props
YaDropdownMenu
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | - | Controlled open state |
onOpenChange | (open: boolean) => void | - | Callback when open state changes |
defaultOpen | boolean | false | Default open state (uncontrolled) |
modal | boolean | true | Whether the dropdown should be modal |
YaDropdownMenuContent
| Prop | Type | Default | Description |
|---|---|---|---|
sideOffset | number | 4 | Distance from the trigger in pixels |
side | 'top' | 'right' | 'bottom' | 'left' | 'bottom' | Preferred side of the trigger |
align | 'start' | 'center' | 'end' | 'start' | Preferred alignment against the trigger |
className | string | - | Additional CSS classes |
YaDropdownMenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
icon | React.ReactNode | - | Icon on the left side |
description | string | - | Secondary text below the label |
selected | boolean | false | Shows a checkmark |
inset | boolean | false | Adds left padding for alignment |
disabled | boolean | false | Disables the item |
onSelect | () => void | - | Callback when selected |
YaDropdownMenuCheckboxItem
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | - | Whether checked |
onCheckedChange | (checked: boolean) => void | - | Callback when checked changes |
icon | React.ReactNode | - | Optional icon |
description | string | - | Secondary description |
YaDropdownMenuRadioItem
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | required | Value of the radio item |
icon | React.ReactNode | - | Optional icon |
description | string | - | Secondary description |
YaDropdownMenuSubTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
icon | React.ReactNode | - | Icon on the left side |
description | string | - | Secondary description |
inset | boolean | false | Adds left padding |
YaDropdownMenuLabel
| Prop | Type | Default | Description |
|---|---|---|---|
inset | boolean | false | Adds left padding |
Accessibility
Best Practices
| Do | Don't |
|---|---|
| Limit menu items to 7-10 options | Overload with too many options |
| Use submenus for hierarchical content | Nest more than 2 levels deep |
| Group related items with labels | Mix unrelated actions together |
| Use checkbox for toggleable options | Use checkbox for navigation |
| Use radio for mutually exclusive choices | Mix checkboxes and radios in same group |
| Use clear action verbs (Edit, Delete) | Use vague labels (OK, Click Here) |
| Add icons for better scannability | Use icons inconsistently |
TypeScript
import { type VariantProps } from 'class-variance-authority'
import {
yaDropdownMenuContentVariants,
yaDropdownMenuItemVariants,
} from '@/components/ui/ya-dropdown-menu'
type ContentVariants = VariantProps<typeof yaDropdownMenuContentVariants>
type ItemVariants = VariantProps<typeof yaDropdownMenuItemVariants>