rc-menu
Menu popup for command surfaces with keyboard navigation and typed activation events, following the WAI-ARIA Menu pattern.
- Package
@rcarls/rc-menu- Element
<rc-menu>- Native dependency
- Consumer supplies menu item controls
- State model
- Host focus with an active descendant
- Main events
rc-menu-activaterc-menu-close- Related
- Usage rulesTheme previews
Installation
- npm
- Yarn
- pnpm
npm install @rcarls/rc-webcomponents@next
yarn add @rcarls/rc-webcomponents@next
pnpm add @rcarls/rc-webcomponents@next
import '@rcarls/rc-webcomponents/rc-menu/define';
Usage
Place focusable elements, usually native buttons, as the menu items, and name the menu with
label. Elements that cannot take focus, such as separators, render but are skipped during
keyboard navigation. A menu usually sits inside an rc-menu-button.
<rc-menu label="File">
<button type="button" value="new">New</button>
<button type="button" value="open">Open…</button>
<hr />
<button type="button" value="save-as">Save As…</button>
</rc-menu>
Live demo
Accessibility
rc-menu is the focus target and owns the menu role. It exposes the active
slotted item through aria-activedescendant instead of moving DOM focus between
items. Arrow Down and Arrow Up move the virtual cursor and wrap at the menu
boundaries; Home and End select the first or last item. Enter, Space, and pointer
interactions activate menu items, while Escape requests that the containing popup
close. See the WAI-ARIA Menu pattern.
The public focusFirst(), focusLast(), focusItem(), and focusItemAt()
methods follow the same model: each focuses the host and updates the virtual
cursor. The read-only items getter returns the current navigable elements in
document order.
Menu item content
Direct focusable children become menuitem entries. Use
role="menuitemcheckbox" or role="menuitemradio" with aria-checked for
checked choices; activation updates the checked state before the activation
event is dispatched. A labeled role="group" can contain one level of related
items, and <hr> or role="separator" elements divide sections without joining
keyboard navigation.
<rc-menu label="View">
<button type="button" role="menuitemcheckbox" aria-checked="true" value="details">
Show details
</button>
<hr />
<button type="button" value="duplicate">
<span>Duplicate</span>
<kbd data-menu-shortcut>Ctrl+D</kbd>
</button>
</rc-menu>
Nested menu triggers supply their own visual affordance through
rc-menu-button's indicator slot. rc-menu does not generate an indicator,
leaving iconography and writing-direction behavior to the application or theme.
Events
Both events bubble across shadow boundaries and are not cancelable.
| Event | Detail | When |
|---|---|---|
rc-menu-activate | { item, value, text, checked? } | Enter, Space, or a pointer click activates an enabled item |
rc-menu-close | { reason: 'escape' } | Escape requests that the containing popup close |
The activation value comes from data-value or value, text is the item's
trimmed text content, and checked reports the resulting checkbox or radio
state when applicable.
Theming
The default demo mode shows the component without a package theme. Use the shared preview controls on this page to compare inherited, light, and dark color schemes or to apply the optional Material theme only inside the demo frames.
Slotted rows remain in light DOM. rc-menu injects their structural base styles
once per containing document or shadow root under @layer rc-base, so consumer
styles outside that layer can override them. Inherited --rc-menu-* custom
properties customize the host and rows; the component does not expose CSS
parts. Add trailing keyboard hints with [data-menu-shortcut].
API
Properties
| Property | Markup | Type | Default | Description |
|---|---|---|---|---|
label | label | string | '' | Accessible label for this menu. Synced to `aria-label` on the host. Prefer `aria-labelledby` for a visible label associated via ID. |
items | JS property only | readonly Element[] | Not specified | All navigable menu item elements in document order. |
Methods
| Method | Description |
|---|---|
focusFirst() | Move the virtual cursor and DOM focus to the first navigable item. |
focusLast() | Move the virtual cursor and DOM focus to the last navigable item. |
focusItem($item?: Element | null) | Move the virtual cursor to a specific item. |
focusItemAt(index: number) | Move the virtual cursor to the item at `index`. |
Events
| Event | Detail type | Description |
|---|---|---|
rc-menu-activate | No detail type documented | Fired when a menu item is activated via keyboard (Enter/Space) or pointer click. |
rc-menu-close | No detail type documented | Fired when Escape is pressed. |
Slots
| Name | Description |
|---|---|
default | Menu items, groups, and separators. Supported content model: - Focusable elements (`<button>`, `<a>`, `[tabindex]`) become navigable `role="menuitem"` entries. - `role="menuitemcheckbox"` / `role="menuitemradio"` elements get a trailing indicator column via `::after` (coexists with a leading icon the item supplies itself); `aria-checked` is initialized when missing and updated on activation. - `role="group"` divs group related items; label them with `aria-label` or `aria-labelledby`. Optionally add a visible `[data-group-label]` element as first child of the group for section headings. - `role="separator"` / `<hr>` produce horizontal dividers between sections. - Within any item, use `[data-rc-menu-leading]`, `[data-rc-menu-label]`, and `[data-rc-menu-trailing]` to identify content columns. `[data-rc-menu-shortcut]` (and the legacy `[data-menu-shortcut]`) is pushed to the inline-end edge. - Nested menu triggers supply their own trailing indicator through `rc-menu-button`'s `indicator` slot. |
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--rc-menu-min-width | 10em | Minimum width of the menu panel. |
--rc-menu-padding-block | var(--rc-popup-padding-block) | Block (top/bottom) padding inside the panel. |
--rc-menu-background | var(--rc-popup-background) | Background color. |
--rc-menu-border | var(--rc-popup-border) | Border. |
--rc-menu-radius | var(--rc-popup-radius) | Menu panel border radius. |
--rc-menu-shadow | var(--rc-popup-shadow) | Box shadow. |
--rc-menu-color | var(--rc-popup-color) | Text color. |
--rc-menu-max-inline-size | calc(100dvi - 0.5rem) | Viewport-aware maximum inline size. |
--rc-menu-max-block-size | calc(100dvb - 0.5rem) | Viewport-aware maximum block size; taller menus scroll. |
--rc-menu-item-min-block-size | 0 | Minimum block size for menu item rows. |
--rc-menu-item-padding-block | var(--rc-item-padding-block) | Menu item block-axis padding. |
--rc-menu-item-padding-inline | var(--rc-item-padding-inline) | Menu item inline-axis padding. |
--rc-menu-item-gap | var(--rc-item-gap) | Gap between flex children inside each item. |
--rc-menu-item-transition | Not specified | CSS transition applied to each item row. |
--rc-menu-hover-bg | color-mix(in srgb, Highlight 8%, transparent) | Pointer-hover background. |
--rc-menu-hover-color | inherit | Pointer-hover text color. |
--rc-menu-active-bg | color-mix(in srgb, Highlight 8%, transparent) | Virtual-cursor active background. |
--rc-menu-active-color | inherit | Virtual-cursor active text color. |
--rc-menu-check-size | 1.25em | Width of the trailing indicator column for checkbox/radio items. |
--rc-menu-check-color | inherit | Color of the check/radio symbol. |
--rc-menu-shortcut-size | 0.8em | Font size of `[data-rc-menu-shortcut]` labels. |
--rc-menu-shortcut-color | GrayText | Text color of `[data-rc-menu-shortcut]` labels. |
--rc-menu-disabled-color | GrayText | Disabled menu item text color. |
--rc-menu-disabled-opacity | var(--rc-disabled-opacity) | Disabled menu item opacity. |
--rc-menu-separator-border | 1px solid ButtonBorder | Separator border. |
--rc-menu-separator-margin-block | var(--rc-control-gap) | Separator block-axis margin. |
--rc-menu-separator-margin-inline | 0px | Separator inline margin; set to match item padding for inset separators. |
--rc-menu-group-label-padding-block | 0.1em | Block padding on `[data-group-label]` section headings. |
--rc-menu-group-label-color | GrayText | Text color of `[data-group-label]` section headings. |
--rc-item-font-family | var(--rc-font-family) | Item font family; shared with listbox and adaptive-menu items. |
--rc-item-font-size | var(--rc-font-size) | Item font size; shared with listbox and adaptive-menu items. |
--rc-item-font-weight | inherit | Item font weight; shared with listbox and adaptive-menu items. |
--rc-item-line-height | var(--rc-line-height) | Item line height; shared with listbox and adaptive-menu items. |
--rc-item-letter-spacing | inherit | Item letter spacing; shared with listbox and adaptive-menu items. |
--rc-item-icon-size | inherit | Size of icons marked with `data-rc-icon`, `data-rc-menu-leading`, or `data-rc-menu-icon`. |