rc-menu-button
Trigger button that opens an rc-menu popup, following the WAI-ARIA Menu Button pattern.
- Package
@rcarls/rc-menu-button- Element
<rc-menu-button>- Native dependency
- Requires trigger and menu children
- State model
- Controlled or uncontrolled open state
- Main events
rc-menu-button-toggle- 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-button/define';
Usage
Put a native button in the trigger slot and an rc-menu in the default slot.
<rc-menu-button>
<button slot="trigger">File</button>
<rc-menu label="File">
<button>New</button>
<button>Open…</button>
<button>Save</button>
</rc-menu>
</rc-menu-button>
Live demo
Accessibility
The element sets aria-haspopup="menu" on the trigger and keeps its aria-expanded in step
with open. Choosing an item or pressing Escape closes the menu and returns focus to the
trigger; a click outside closes it without moving focus.
| Key | Action |
|---|---|
| Enter, Space | Opens the menu. |
| Down Arrow | Opens the menu and focuses the first item. |
| Up Arrow | Opens the menu and focuses the last item. |
| Escape | Closes the menu and returns focus to the trigger. |
Inside the open menu, the rc-menu keys apply. In a vertical
layout, such as a submenu or a vertical menubar, Right Arrow opens on the first item and Left
Arrow on the last, and these keys turn with right-to-left and vertical writing modes. See the
WAI-ARIA Menu Button pattern.
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.
rc-menu-button keeps the consumer-supplied trigger in light DOM and styles it
through inherited --rc-menu-button-trigger-* custom properties. The popup menu
remains an rc-menu, so menu row styling continues through the --rc-menu-*
contract.
Add icon-only when the trigger has no visible label. Supporting themes can
then size the visible trigger with --rc-menu-button-icon-size while preserving
an independently configurable activation area through the
--rc-menu-button-touch-target-* properties.
At an edge with no neighbor on that side, such as a toolbar's trailing-most
trigger, --rc-menu-button-touch-target-overlap-inline-start and
-inline-end (zero by default) let a theme or consumer give that reserved
space back so the visible trigger sits flush with the edge instead, using the
same pattern as rc-button's own touch-target overlap tokens.
API
Properties
| Property | Markup | Type | Default | Description |
|---|---|---|---|---|
open | open | boolean | Not specified | Applies open state in controlled mode without dispatching an event. |
defaultOpen | default-open | boolean | Not specified | Applies the uncontrolled default before any controlled write occurs. |
orientation | orientation | 'horizontal' | 'vertical' | undefined | Not specified | The layout this menu button sits in, which picks the arrow keys that open and close the menu. `horizontal` runs along the inline axis and `vertical` along the block axis; the keys follow how that layout renders. If not set, inherits from a parent rc-menubar or element with role="menubar". |
placement | placement | AnchorPlacement | 'block-end-start' | Preferred placement of the popup relative to the trigger button. Reads the default until the author sets one. |
Methods
| Method | Description |
|---|---|
openMenu(focusTarget: 'first' | 'last') | Opens the menu and moves focus into it. |
closeMenu(returnFocus: unknown) | Closes the menu. |
toggleMenu() | Toggles the menu between open and closed. |
focus(options?: FocusOptions) | Overrides the default focus() so that programmatic focus calls (e.g. from a roving-tabindex parent navigating back to this element via arrow keys) always reach the trigger. Chrome's delegatesFocus will not delegate to a tabindex="-1" element, so when the toolbar marks this host inactive it suppresses the trigger — and the next focusItem() call silently fails. Lifting the suppression here and directly calling trigger.focus() bypasses that restriction. |
Events
| Event | Detail type | Description |
|---|---|---|
rc-menu-button-toggle | CustomEvent | Fired when the menu opens or closes |
Slots
| Name | Description |
|---|---|
trigger | The button element that triggers the menu |
indicator | Optional decorative indicator rendered at the trigger's inline end |
default | The rc-menu element to display as popup |
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--rc-menu-button-trigger-block-size | var(--rc-control-block-size) | Minimum block size of the trigger |
--rc-menu-button-trigger-padding-block | var(--rc-control-padding-block) | Trigger block-axis padding |
--rc-menu-button-trigger-padding-inline | var(--rc-control-padding-inline) | Trigger inline-axis padding |
--rc-menu-button-trigger-gap | var(--rc-item-gap) | Gap between flex children in the trigger |
--rc-menu-button-trigger-border | var(--rc-border) | Trigger border |
--rc-menu-button-trigger-radius | var(--rc-control-radius) | Trigger border radius |
--rc-menu-button-trigger-background | var(--rc-button-bg) | Trigger background |
--rc-menu-button-trigger-color | var(--rc-button-text) | Trigger text color |
--rc-menu-button-trigger-transition | Not specified | CSS transition applied to the trigger |
--rc-menu-button-trigger-hover-background | color-mix(in srgb, Highlight 8%, transparent) | Trigger hover background |
--rc-menu-button-trigger-hover-color | inherit | Trigger hover text color |
--rc-menu-button-trigger-hover-border-color | currentColor | Trigger hover border color |
--rc-menu-button-trigger-open-background | color-mix(in srgb, Highlight 12%, transparent) | Trigger background when the menu is open |
--rc-menu-button-trigger-open-color | inherit | Trigger text color when the menu is open |
--rc-menu-button-trigger-open-border-color | currentColor | Trigger border color when the menu is open |
--rc-menu-button-indicator-size | 1em | Inline and block size of the slotted indicator |
--rc-menu-button-indicator-color | currentColor | Color of the slotted indicator |
--rc-menu-button-indicator-inset | var(--rc-menu-button-trigger-padding-inline) | Indicator distance from the trigger's inline end |
--rc-menu-button-icon-size | Not specified | Inline size of an `icon-only` trigger's visible box. Square by default (equal to `--rc-menu-button-trigger-block-size`); set narrower or wider to change only the trigger's width, independent of its height. |
--rc-menu-button-touch-target-block-size | 3rem | Minimum accessible touch target block size for an `icon-only` trigger. A floor, not a fixed size: has no effect once `--rc-menu-button-trigger-block-size` already meets or exceeds it. Grows the shadow-DOM trigger wrapper (reserving layout space) and the light-DOM hit-slop (the actual larger clickable region) together; the visible trigger itself stays at its own size, centered. |
--rc-menu-button-touch-target-inline-size | Not specified | Minimum accessible touch target inline size for an `icon-only` trigger. Defers to `--rc-menu-button-touch-target-block-size` when unset. |
--rc-menu-button-touch-target-overlap-inline-start | 0px | Zero by default. A theme or consumer sets this on an `icon-only` trigger that sits at a real leading edge (no neighbor on that side) to let its touch-target inflation overlap into whatever sits just outside the host, such as a container's own edge padding, instead of also reserving layout space there. |
--rc-menu-button-touch-target-overlap-inline-end | 0px | The trailing-edge counterpart to `--rc-menu-button-touch-target-overlap-inline-start`. |
--rc-menu-button-popup-duration | 150ms | Popup open/close fade transition duration. |
CSS Parts
| Part | Description |
|---|---|
root | The root container element |
popup | The popup container element |