Menu
A menu is a list of actions that opens from a button. Choosing an item runs a command; nothing is saved as a value. It is also called a dropdown, action, or overflow menu.
<tk-menu>
<tk-tooltip slot="trigger" type="label">
<tk-icon-button label="Row actions"><tk-icon name="more-horizontal"></tk-icon></tk-icon-button>
</tk-tooltip>
<tk-menu-item>Rename</tk-menu-item>
<tk-menu-item>Duplicate</tk-menu-item>
<tk-menu-item>Archive</tk-menu-item>
</tk-menu>Anatomy
| Part | What it is |
|---|---|
| trigger | your button in the trigger slot; it gets the menu ARIA |
| panel | the list; 160–288px wide, sized to its content |
| item | tk-menu-item: one row, at least 32px tall |
| check | the ✓ for checked items; when any item can be checked, every row keeps room for it |
| icon | an optional 16px icon in the icon slot |
| description | an optional second line |
| trailing | an optional hint or badge in the trailing slot |
| divider | tk-divider or <hr>: a line between groups |
| group | tk-menu-group: a titled section |
| submenu | a nested tk-menu, opened from a parent row |
Items
<tk-menu>
<tk-button slot="trigger">View<tk-icon slot="icon-end" name="chevron-down"></tk-icon></tk-button>
<tk-menu-item type="checkbox" checked>Show grid</tk-menu-item>
<tk-menu-item type="checkbox">Snap to grid</tk-menu-item>
<tk-divider></tk-divider>
<tk-menu-group label="Sort by">
<tk-menu-item type="radio" checked>Name</tk-menu-item>
<tk-menu-item type="radio">Date modified</tk-menu-item>
</tk-menu-group>
</tk-menu>command(default): runs an action and closes the menu.checkbox: turns a view option on or off, such as showing a grid. The menu stays open, so people can change several.radio: picks one option from itstk-menu-groupand closes the menu. Put radios in a titled group.- Checked items change how people see the screen (sort order, visible columns,
density), and the change applies at once. They never hold a form value; that's a
Select . preventDefault()on an item'sclickstops both the check change and the close, for example to ask first. Settingcheckedfrom code fires nothing.
Danger
<tk-menu> <tk-button slot="trigger">Project</tk-button> <tk-menu-item>Rename</tk-menu-item> <tk-menu-item>Duplicate</tk-menu-item> <tk-divider></tk-divider> <tk-menu-item danger>Delete project…</tk-menu-item> </tk-menu>
dangermarks an action that loses data. It works oncommanditems only.- Put it last, after a divider.
- Always confirm it with a
confirm dialog ; end the label with "…" to show that a step follows. - Red means data loss, not leaving: "Leave team" isn't red. Name the action in the label.
Icons, descriptions, and hints
<tk-menu> <tk-button slot="trigger">Edit</tk-button> <tk-menu-item><tk-icon slot="icon" name="copy"></tk-icon>Copy<span slot="trailing">⌘C</span></tk-menu-item> <tk-menu-item description="Everyone with the link can view"><tk-icon slot="icon" name="link"></tk-icon>Copy link</tk-menu-item> <tk-menu-item disabled><tk-icon slot="icon" name="trash"></tk-icon>Move to trash</tk-menu-item> </tk-menu>
- Icons: give every item an icon or none. The menu sizes and colors them.
- Descriptions: keep them rare and short; most menus are one line per item.
- Trailing: a shortcut hint or a badge, shown as text. It doesn't set up the shortcut; wire the key yourself. Screen readers read it after the label, so keep it short. Nothing interactive here.
States
Hover and keyboard focus look the same: a light tint on the row. Pointing at an item moves focus to it.
disabled: the item stays in the menu and can still be reached with the arrow keys, but does nothing. People can see the command exists. If every item is unavailable for the same reason, disable the trigger instead.- Hide items people will never be allowed to use, such as admin-only actions.
checked: shows the ✓ oncheckboxandradioitems.
Opening and closing
Opening moves focus to the first item (the last with ↑).
| Close path | Focus goes to |
|---|---|
Esc |
the trigger; a dialog around the menu stays open |
| choosing a command or radio item | the trigger, unless the action moves focus, such as opening a dialog |
| choosing a checkbox item | the menu stays open |
Tab / Shift+Tab |
the next or previous element; nothing runs |
| click outside | where the click landed |
| clicking the trigger again | the trigger |
- The menu opens below the trigger and moves above near the bottom of the screen. It follows the trigger on scroll and is never clipped by its container.
- Long labels are cut with an ellipsis. Menus don't scroll; keep them to about 12 items.
Triggers
- A
tk-buttonwith a label and achevron-downicon for page actions. Don't repeat its word in the items: "Export" opens "PDF" and "CSV", not "Export PDF". - A
more-horizontaltk-icon-buttonin atype="label"Tooltip for overflow actions, such as on a table row. Name the scope: "Row actions", "More actions".
Context menus
<tk-menu trigger="context" label="File actions"> <div slot="trigger">Quarterly report.pdf</div> <tk-menu-item>Open</tk-menu-item> <tk-menu-item>Rename</tk-menu-item> <tk-menu-item>Download</tk-menu-item> </tk-menu>
With trigger="context", the trigger slot holds the area people right-click. The menu is
otherwise the same. It opens three ways:
- Right-click in the area: the menu opens at the pointer.
- Long press on touch, for 500ms; moving or scrolling cancels it.
Shift+F10or theContextMenukey while focus is in the area. The menu makes the area focusable if it isn't.
Rules for context menus:
- Name the menu with
label, oraria-labelon the area. The area's text is never used as the name. - Every command must also be reachable from the main screen. People can't discover a right-click, and touch and keyboard users may never find it.
- Hide items that don't apply here, instead of disabling them.
- No shortcut hints.
- Put the most likely commands first, nearest the pointer.
The browser's own context menu stays available everywhere outside the area.
Submenus
<tk-menu>
<tk-button slot="trigger">Actions</tk-button>
<tk-menu-item>Rename</tk-menu-item>
<tk-menu>
<tk-menu-item slot="trigger">Share</tk-menu-item>
<tk-menu-item>Email</tk-menu-item>
<tk-menu-item>Copy link</tk-menu-item>
</tk-menu>
</tk-menu>A submenu is a tk-menu inside a menu, with a tk-menu-item as its trigger: the parent
row. The row gets its chevron and ARIA, and the submenu takes its name from the row.
- Hover, click,
Enter,Space, or→open it. A parent row only opens its submenu; it never runs a command. - Moving diagonally from the row into the open submenu doesn't close it.
←orEscclose the submenu and return focus to its row.- Choosing an item closes every level and returns focus to the trigger.
- It opens on the side with room. A disabled parent row never opens.
- Radio sets and the ✓ column are per level.
Use submenus sparingly:
- One level only, with at most about 5 items.
- Not to pick a value, and not just to tidy up; use a group title instead.
- If people open the menu to reach the submenu, make the submenu the menu.
- Avoid them on touch-first screens.
When to use
- Use a menu for 3 to about 12 related actions that would take too much space on screen, such as row actions in a table. With fewer, show the buttons. With more, split the menu or rethink the screen.
- Keep the main action of a view visible, never inside a menu.
- Use a
Select orCombobox to pick a value that is saved or sent with a form. - Use a
Navigation menu orSidebar for links. If an item does something, it's a menu item; if it goes somewhere, it's a link. - Open a menu from a button, on click. Never from a link, never on hover.
Content
- Start labels with a verb, add a noun when the verb alone is unclear. No articles, sentence case.
- End the label with "…" when the action needs more input before it's done.
- Put frequent and important items first, keep related items together, and put danger last.
- Group titles: one or two words, sentence case, no punctuation.
More in
Do / Don't
| ✅ Do | ❌ Don't |
|---|---|
| Keep the main action visible | Hide it in a menu |
| Put a danger item last, with "…" and a confirm | Delete anything straight from a menu |
| Use a Select for a form value | Use checked items as a form's answer |
| Give every item an icon, or none | Mix rows with and without icons |
| "Export" → "PDF", "CSV" | "Export" → "Export PDF", "Export CSV" |
| Open from a button, on click | Open from a link or on hover |
| Keep submenus to one level | Nest a submenu in a submenu |
| Offer context menu commands elsewhere too | Make right-click the only way to an action |
Related
Select andCombobox : pick a value.Navigation menu andSidebar : links, not actions.Icon button andTooltip : the overflow trigger.Dialog : the confirm a danger item opens.Split button : a main action with a menu of related ones.
API
tk-menu
| Attribute | Type | Default | What it does |
|---|---|---|---|
label |
string | the trigger's name | the menu's accessible name; in context mode the fallback is the area's aria-label, never its text |
trigger |
button | context |
button |
opens from a button, or from right-click, long press, and Shift+F10 on an area |
| Slot | Content |
|---|---|
trigger |
one button; a tk-menu-item for a submenu; the target area in context mode |
| default | tk-menu-item, tk-menu-group, tk-divider or <hr>, and nested tk-menu |
CSS: ::part(menu), the panel. No methods; the trigger opens the menu.
tk-menu-item
| Attribute | Type | Default | What it does |
|---|---|---|---|
type |
command | checkbox | radio |
command |
what choosing it does |
checked |
boolean | false |
the checked state of checkbox and radio items |
description |
string | none | a second line |
danger |
boolean | false |
marks a destructive command |
disabled |
boolean | false |
reachable by keyboard, does nothing |
| Slot | Content |
|---|---|
| default | the label |
icon |
one tk-icon |
trailing |
a short hint or badge, not interactive |
CSS: ::part(row), the row.
tk-menu-group
| Attribute | Type | Default | What it does |
|---|---|---|---|
label |
string | none | the section title; required. It also scopes radio items |
Events
No custom events. Listen for click on an item.
Tokens
| Part | Tokens |
|---|---|
| panel | surface/raised, shadow-2, radius-16, space-4 padding; in high contrast, a 1px border/default |
| item | text/default, Body Small; icon icon/secondary; radius-12 rows |
| hover, focus | state/hover |
| press | state/press |
| danger | text/error, icon/error; state/error/hover on hover and focus, state/error/press on press |
| disabled | text/disabled, icon/disabled |
| check | icon/accent, icon-size-16 |
| description, hint, group title | text/secondary |
| divider | border/default |
Values per theme:
Accessibility
Built in:
- The trigger gets
aria-haspopup="menu"andaria-expanded. The panel is amenunamed bylabelor the trigger; items aremenuitem,menuitemcheckbox, ormenuitemradiowith their checked and disabled states. - The menu, its submenus, and its items are one
Tabstop. Focus moves in on open and returns as the close table says. - Disabled items stay reachable and show focus. The ✓ and chevrons are hidden from screen readers.
- A context area gets
aria-haspopup,aria-keyshortcuts="Shift+F10", and a tab stop if it had none. - Every row is at least 32px tall. The panel stays inside the visible viewport when zoomed.
Your part:
- Name a context menu with
label, and make every command reachable without it. - Name the destructive action in a danger item; color alone doesn't tell everyone.
- Keep descriptions short; they're read after the label.
| Key | Trigger | Open menu |
|---|---|---|
Enter / Space |
opens; focus on the first item | chooses the item |
↓ / ↑ |
opens; focus on the first or last item | next or previous item, including disabled ones; no wrap |
Home / End |
— | first or last item |
| letters | — | jumps to the next item starting with them |
Esc |
— | closes, or closes only the submenu |
Tab |
leaves | closes without running anything |
→ |
— | opens a submenu |
← |
— | closes a submenu |
Shift+F10 / ContextMenu |
opens a context menu | — |
In right-to-left text, → and ← swap.