Menu
Dropdown menus with nested submenus and keyboard navigation.
Usage
<w-menu>
<w-slot trigger><button>Actions</button></w-slot>
<w-slot body>
<div>
<w-slot item name="edit"><button>Edit</button></w-slot>
<w-slot item name="duplicate"><button>Duplicate</button></w-slot>
<w-slot item name="delete"><button>Delete</button></w-slot>
</div>
</w-slot>
</w-menu>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Whether the menu is open |
placement | string | "bottom-start" | Menu position relative to trigger |
persistent | boolean | false | Prevent closing via Escape or outside click |
close-on-select | boolean | true | Close menu when item is selected |
portal | boolean | true | Teleport content to body for z-index safety |
Slots
| Slot | Element | Description |
|---|---|---|
trigger | Button | Element that opens the menu |
content | Container | Menu container |
item | Button | Menu item (optionally with name attribute) |
submenu | Container | Nested submenu content |
Events
| Event | Detail | Description |
|---|---|---|
open | - | Fired when menu opens |
close | - | Fired when menu closes |
select | { item, element } | Fired when an item is selected |
Methods
| Method | Description |
|---|---|
show() | Open the menu |
hide() | Close the menu |
toggle(force?) | Toggle open state |
Keyboard
| Key | Action |
|---|---|
Enter / Space / ArrowDown | Open menu (on trigger) |
ArrowUp | Open menu and focus last item |
ArrowDown | Focus next item |
ArrowUp | Focus previous item |
ArrowRight | Open submenu |
ArrowLeft | Close submenu, return to parent |
Home | Focus first item |
End | Focus last item |
Escape | Close menu |
Tab | Close menu |
Examples
With Submenus
<w-menu>
<w-slot trigger><button>File</button></w-slot>
<w-slot body>
<div>
<w-slot item name="new"><button>New</button></w-slot>
<w-slot item name="open"><button>Open</button></w-slot>
<w-slot item name="recent">
<div>
Recent Files
<w-slot sub>
<div>
<w-slot item name="file1"><button>Document.pdf</button></w-slot>
<w-slot item name="file2"><button>Image.png</button></w-slot>
</div>
</w-slot>
</div>
</w-slot>
<w-slot item name="save"><button>Save</button></w-slot>
</div>
</w-slot>
</w-menu>
Custom Placement
<w-menu placement="right-start">
<w-slot trigger><button>Options</button></w-slot>
<w-slot body><div>...</div></w-slot>
</w-menu>
Available placements:
top,top-start,top-endbottom,bottom-start,bottom-endleft,left-start,left-endright,right-start,right-end
Keep Open on Select
<w-menu close-on-select="false">
<!-- Menu stays open after selecting items -->
</w-menu>
Persistent Menu
A persistent menu cannot be dismissed by pressing Escape or clicking outside.
<w-menu persistent>
<w-slot trigger><button>Options</button></w-slot>
<w-slot body>
<div>
<w-slot item name="done"><button>Done</button></w-slot>
</div>
</w-slot>
</w-menu>
CSS Transitions
.menu-enter {
}
.menu-enter-from {
opacity: 0;
transform: translateY(-4px);
}
.menu-enter-to {
opacity: 1;
transform: translateY(0);
}
.menu-leave {
}
.menu-leave-from {
opacity: 1;
}
.menu-leave-to {
opacity: 0;
}
Styling
/* The menu panel — teleported to portal, target by role */
[role="menu"] {
background: white;
border: 1px solid #ddd;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
min-width: 180px;
}
/* Items */
[role="menuitem"] {
padding: 0.5rem 1rem;
}
/* Items that have a submenu — aria-haspopup is set */
[role="menuitem"][aria-haspopup="menu"]::after {
content: "▶";
}
/* Submenu open state */
[role="menuitem"][aria-expanded="true"] {
background: #eef2ff;
}
/* Trigger open state */
w-menu w-slot[trigger] > *[aria-expanded="true"] {
background: #eef2ff;
}
| Selector | Targets |
|---|---|
[role="menu"] | Any menu panel (root or sub) |
[role="menuitem"] | Every menu item |
[role="menuitem"][aria-haspopup="menu"] | Items with a submenu |
[role="menuitem"][aria-expanded="true"] | Item with open submenu |
Accessibility
- Trigger has
aria-haspopup="menu"andaria-expanded - Content has
role="menu" - Items have
role="menuitem" - Submenus linked with
aria-controls - Roving tabindex for navigation
- Auto-positioned to stay in viewport