# Dropdown Menu Component Usage
## Overview
The dropdown menu component displays a list of actions or options in a floating panel triggered by a button. It supports keyboard navigation, submenus, separators, and icon integration. Dropdown menus use JavaScript for positioning and interaction.
## JavaScript Requirements
### Required Scripts
```html
<!-- Include Basecoat CSS and JavaScript -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/basecoat-css@latest/dist/basecoat.cdn.min.css">
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@latest/dist/js/basecoat.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@latest/dist/js/dropdown-menu.min.js" defer></script>
```
## HTML Structure
### Basic Dropdown Menu
```html
<div class="dropdown-menu">
<button type="button" class="btn-outline" aria-haspopup="menu" aria-expanded="false">
Options
<svg class="size-4 ml-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="m6 9 6 6 6-6"></path>
</svg>
</button>
<nav role="menu">
<button type="button" role="menuitem">New File</button>
<button type="button" role="menuitem">Open</button>
<button type="button" role="menuitem">Save</button>
<hr role="separator">
<button type="button" role="menuitem">Exit</button>
</nav>
</div>
```
### Menu Items with Icons
```html
<div class="dropdown-menu">
<button type="button" class="btn-outline" aria-haspopup="menu" aria-expanded="false">
Actions
</button>
<nav role="menu">
<button type="button" role="menuitem">
<svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
</svg>
New File
</button>
<button type="button" role="menuitem">
<svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
</svg>
Open Folder
</button>
<button type="button" role="menuitem">
<svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
<polyline points="17 21 17 13 7 13 7 21"></polyline>
<polyline points="7 3 7 8 15 8"></polyline>
</svg>
Save
</button>
</nav>
</div>
```
## Menu Item Types
### Standard Items
```html
<button type="button" role="menuitem">Standard Item</button>
<a href="/page" role="menuitem">Link Item</a>
```
### Items with Keyboard Shortcuts
```html
<button type="button" role="menuitem">
<svg class="size-4"><!-- icon --></svg>
New File
<kbd class="ml-auto text-muted-foreground tracking-widest">⌘N</kbd>
</button>
```
### Disabled Items
```html
<button type="button" role="menuitem" aria-disabled="true">
Disabled Action
</button>
```
### Destructive Items
```html
<button type="button" role="menuitem" class="text-destructive hover:bg-destructive/10 focus:bg-destructive/10 focus:text-destructive [&_svg]:!text-destructive">
<svg class="size-4"><!-- trash icon --></svg>
Delete
</button>
```
### Checkbox Menu Items
```html
<div role="menuitemcheckbox" aria-checked="true" tabindex="0">
<svg class="size-4"><!-- check icon when checked --></svg>
Show Toolbar
</div>
```
### Radio Menu Items
```html
<div role="group" aria-label="View options">
<div role="menuitemradio" aria-checked="true" tabindex="0">
<svg class="size-4"><!-- dot icon when selected --></svg>
List View
</div>
<div role="menuitemradio" aria-checked="false" tabindex="-1">
<svg class="size-4 invisible"><!-- placeholder --></svg>
Grid View
</div>
</div>
```
## Implementation Examples
### File Menu
```html
<div class="dropdown-menu">
<button type="button" class="btn-ghost" aria-haspopup="menu" aria-expanded="false">
File
</button>
<nav role="menu" class="min-w-48">
<button type="button" role="menuitem">
<svg class="size-4"><!-- file-plus icon --></svg>
New
<kbd class="ml-auto text-muted-foreground tracking-widest">⌘N</kbd>
</button>
<button type="button" role="menuitem">
<svg class="size-4"><!-- folder-open icon --></svg>
Open
<kbd class="ml-auto text-muted-foreground tracking-widest">⌘O</kbd>
</button>
<button type="button" role="menuitem">
<svg class="size-4"><!-- save icon --></svg>
Save
<kbd class="ml-auto text-muted-foreground tracking-widest">⌘S</kbd>
</button>
<button type="button" role="menuitem">
Save As...
<kbd class="ml-auto text-muted-foreground tracking-widest">⇧⌘S</kbd>
</button>
<hr role="separator">
<button type="button" role="menuitem">
<svg class="size-4"><!-- printer icon --></svg>
Print
<kbd class="ml-auto text-muted-foreground tracking-widest">⌘P</kbd>
</button>
<hr role="separator">
<button type="button" role="menuitem">
Exit
</button>
</nav>
</div>
```
### Context Menu
```html
<div class="dropdown-menu">
<button type="button" class="btn-icon-ghost" aria-haspopup="menu" aria-expanded="false" aria-label="More options">
<svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="1"></circle>
<circle cx="19" cy="12" r="1"></circle>
<circle cx="5" cy="12" r="1"></circle>
</svg>
</button>
<nav role="menu" data-align="end">
<button type="button" role="menuitem">
<svg class="size-4"><!-- edit icon --></svg>
Edit
</button>
<button type="button" role="menuitem">
<svg class="size-4"><!-- copy icon --></svg>
Duplicate
</button>
<button type="button" role="menuitem">
<svg class="size-4"><!-- share icon --></svg>
Share
</button>
<hr role="separator">
<button type="button" role="menuitem">
<svg class="size-4"><!-- archive icon --></svg>
Archive
</button>
<button type="button" role="menuitem" class="text-destructive hover:bg-destructive/10 focus:bg-destructive/10 [&_svg]:!text-destructive">
<svg class="size-4"><!-- trash icon --></svg>
Delete
</button>
</nav>
</div>
```
### User Menu
```html
<div class="dropdown-menu">
<button type="button" class="flex items-center gap-2" aria-haspopup="menu" aria-expanded="false">
<img src="/avatar.png" alt="" class="size-8 rounded-full">
<span class="hidden sm:inline">John Doe</span>
<svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="m6 9 6 6 6-6"></path>
</svg>
</button>
<nav role="menu" class="w-56" data-align="end">
<div class="px-2 py-1.5">
<p class="text-sm font-medium">John Doe</p>
<p class="text-xs text-muted-foreground">john@example.com</p>
</div>
<hr role="separator">
<a href="/profile" role="menuitem">
<svg class="size-4"><!-- user icon --></svg>
Profile
<kbd class="ml-auto text-muted-foreground tracking-widest">⇧⌘P</kbd>
</a>
<a href="/settings" role="menuitem">
<svg class="size-4"><!-- settings icon --></svg>
Settings
<kbd class="ml-auto text-muted-foreground tracking-widest">⌘,</kbd>
</a>
<a href="/billing" role="menuitem">
<svg class="size-4"><!-- credit-card icon --></svg>
Billing
</a>
<hr role="separator">
<a href="/team" role="menuitem">
<svg class="size-4"><!-- users icon --></svg>
Team
</a>
<a href="/invite" role="menuitem">
<svg class="size-4"><!-- user-plus icon --></svg>
Invite users
</a>
<hr role="separator">
<a href="/docs" role="menuitem">
<svg class="size-4"><!-- book icon --></svg>
Documentation
</a>
<a href="/support" role="menuitem">
<svg class="size-4"><!-- help-circle icon --></svg>
Support
</a>
<hr role="separator">
<button type="button" role="menuitem">
<svg class="size-4"><!-- log-out icon --></svg>
Log out
<kbd class="ml-auto text-muted-foreground tracking-widest">⇧⌘Q</kbd>
</button>
</nav>
</div>
```
### Menu with Groups
```html
<div class="dropdown-menu">
<button type="button" class="btn-outline" aria-haspopup="menu" aria-expanded="false">
Insert
</button>
<nav role="menu" class="min-w-48">
<div role="group" aria-labelledby="text-group">
<span role="heading" id="text-group" class="px-2 py-1.5 text-xs font-medium text-muted-foreground">Text</span>
<button type="button" role="menuitem">Heading</button>
<button type="button" role="menuitem">Paragraph</button>
<button type="button" role="menuitem">Quote</button>
</div>
<hr role="separator">
<div role="group" aria-labelledby="media-group">
<span role="heading" id="media-group" class="px-2 py-1.5 text-xs font-medium text-muted-foreground">Media</span>
<button type="button" role="menuitem">Image</button>
<button type="button" role="menuitem">Video</button>
<button type="button" role="menuitem">Audio</button>
</div>
</nav>
</div>
```
## Positioning
### Position and Alignment
```html
<!-- Bottom aligned to end (common for user menus) -->
<nav role="menu" data-side="bottom" data-align="end">
<!-- menu items -->
</nav>
<!-- Top aligned to start -->
<nav role="menu" data-side="top" data-align="start">
<!-- menu items -->
</nav>
```
## JavaScript API
### Handling Menu Item Clicks
```javascript
document.querySelectorAll('[role="menuitem"]').forEach(item => {
item.addEventListener('click', function(e) {
const action = this.textContent.trim();
console.log('Menu action:', action);
// Menu closes automatically after click
});
});
```
### Checkbox Menu Items
```javascript
document.querySelectorAll('[role="menuitemcheckbox"]').forEach(item => {
item.addEventListener('click', function() {
const isChecked = this.getAttribute('aria-checked') === 'true';
this.setAttribute('aria-checked', !isChecked);
});
});
```
### Radio Menu Items
```javascript
document.querySelectorAll('[role="group"]').forEach(group => {
const radios = group.querySelectorAll('[role="menuitemradio"]');
radios.forEach(radio => {
radio.addEventListener('click', function() {
radios.forEach(r => r.setAttribute('aria-checked', 'false'));
this.setAttribute('aria-checked', 'true');
});
});
});
```
## Accessibility Guidelines
### Required ARIA Attributes
```html
<div class="dropdown-menu">
<button
type="button"
id="menu-trigger"
aria-haspopup="menu"
aria-expanded="false"
aria-controls="menu-list"
>
Options
</button>
<nav role="menu" id="menu-list" aria-labelledby="menu-trigger">
<button type="button" role="menuitem" tabindex="-1">Item 1</button>
<button type="button" role="menuitem" tabindex="-1">Item 2</button>
</nav>
</div>
```
### Keyboard Navigation
- **Enter/Space**: Open menu, activate item
- **Arrow Down**: Move to next item
- **Arrow Up**: Move to previous item
- **Home**: Move to first item
- **End**: Move to last item
- **Escape**: Close menu
- **Type character**: Jump to item starting with that character
## Best Practices
### When to Use Dropdown Menus
- Actions on an item (edit, delete, share)
- Navigation submenus
- User account menus
- Settings menus
- Context menus
### When to Use Alternatives
- Simple selection: Use Select
- Yes/No confirmation: Use Alert Dialog
- Complex content: Use Popover or Dialog
### Content Guidelines
- Keep menu items concise
- Use icons consistently
- Group related items
- Order items logically
- Include keyboard shortcuts for common actions
### Visual Guidelines
- Maintain consistent item height
- Use adequate touch targets on mobile
- Indicate destructive actions clearly
- Show disabled states appropriately
## Jinja/Nunjucks Macro
### Using the Macro
```jinja
{% from "dropdown-menu.njk" import dropdown_menu %}
{% set trigger %}
Options
<svg><!-- chevron-down --></svg>
{% endset %}
{% call dropdown_menu(
trigger=trigger,
trigger_attrs={"class": "btn-outline"},
popover_attrs={"data-align": "end"}
) %}
<button type="button" role="menuitem">
<svg><!-- icon --></svg>
Edit
</button>
<button type="button" role="menuitem">
<svg><!-- icon --></svg>
Duplicate
</button>
<hr role="separator">
<button type="button" role="menuitem" class="text-destructive">
<svg><!-- icon --></svg>
Delete
</button>
{% endcall %}
```
### Using Items Array
```jinja
{{ dropdown_menu(
trigger="Actions",
trigger_attrs={"class": "btn-outline"},
items=[
{ type: "item", label: "Edit", icon: icon_edit },
{ type: "item", label: "Duplicate", icon: icon_copy },
{ type: "separator" },
{ type: "item", label: "Delete", destructive: true, icon: icon_trash }
]
) }}
```