<!-- Generated by tools/docs.php from docs/src/menu.html - edit the source, not this file. -->

# Menus

A menu is a short list of actions or choices that opens from a button: the browser's popover, placed beside the button by CSS anchor positioning, driven by the keyboard as the WAI-ARIA menu button.

## Examples

The button opens the menu with the browser's command - `commandfor` names the menu, `command="toggle-popover"` opens and closes it - and the library keeps its `aria-expanded`, which Firefox does not give such a button. No script is needed to open a menu; the library adds the arrow keys.

```html
<button class="btn" type="button" commandfor="menu-actions-list" command="toggle-popover" aria-haspopup="menu"><i class="bi bi-three-dots" aria-hidden="true"></i>Actions</button>
<div id="menu-actions-list" class="menu" popover role="menu" aria-label="Actions">
	<button class="menu-item" type="button" role="menuitem"><i class="bi bi-pencil" aria-hidden="true"></i>Edit</button>
	<button class="menu-item" type="button" role="menuitem"><i class="bi bi-files" aria-hidden="true"></i>Duplicate</button>
	<button class="menu-item" type="button" role="menuitem" aria-disabled="true"><i class="bi bi-archive" aria-hidden="true"></i>Archive</button>
	<a class="menu-item" href="#menu-actions" role="menuitem"><i class="bi bi-box-arrow-up-right" aria-hidden="true"></i>Open on the site</a>
	<hr class="menu-divider">
	<button class="menu-item menu-item-danger" type="button" role="menuitem"><i class="bi bi-trash3" aria-hidden="true"></i>Delete</button>
</div>
```

### Choices and headers

A choice of one is a set of `menuitemradio` items, the chosen one `aria-checked="true"` with a check drawn after it. `menu-header` titles a part of the menu. `menu-start` lines the menu up with its button's start edge instead of its end.

```html
<button class="btn btn-text" type="button" commandfor="menu-choice-list" command="toggle-popover" aria-haspopup="menu"><span class="dot dot-success"></span>All sites<i class="bi bi-chevron-down" aria-hidden="true"></i></button>
<div id="menu-choice-list" class="menu menu-start" popover role="menu" aria-label="Work context">
	<div class="menu-header">Work context</div>
	<button class="menu-item" type="button" role="menuitemradio" aria-checked="true"><i class="bi bi-grid-3x3-gap" aria-hidden="true"></i>All sites</button>
	<button class="menu-item" type="button" role="menuitemradio" aria-checked="false"><i class="bi bi-globe2" aria-hidden="true"></i>lagoon.example</button>
	<button class="menu-item" type="button" role="menuitemradio" aria-checked="false"><i class="bi bi-globe2" aria-hidden="true"></i>northbay.example</button>
</div>
```

### Items that stay checked

A `menuitemcheckbox` chosen flips its `aria-checked`, and a `menuitemradio` in a `role="group"` checks itself and unchecks the others of its group; the menu stays open for the next choice, and the page hears `redz:check` with `detail.item` and `detail.checked`. A `menuitemradio` outside a group is a choice of one, as above: it checks itself and closes the menu.

```html
<button class="btn" type="button" commandfor="menu-checks-list" command="toggle-popover" aria-haspopup="menu"><i class="bi bi-layout-three-columns" aria-hidden="true"></i>Columns<i class="bi bi-chevron-down" aria-hidden="true"></i></button>
<div id="menu-checks-list" class="menu" popover role="menu" aria-label="Columns">
	<button class="menu-item" type="button" role="menuitemcheckbox" aria-checked="true">Guest</button>
	<button class="menu-item" type="button" role="menuitemcheckbox" aria-checked="true">Tour</button>
	<button class="menu-item" type="button" role="menuitemcheckbox" aria-checked="false">Pickup</button>
	<div class="menu-header" id="menu-checks-sort">Sort by</div>
	<div role="group" aria-labelledby="menu-checks-sort">
		<button class="menu-item" type="button" role="menuitemradio" aria-checked="true">Date</button>
		<button class="menu-item" type="button" role="menuitemradio" aria-checked="false">Name</button>
	</div>
</div>
```

## States

The items of an open menu. `:focus-visible` is where the arrow keys are; `aria-current="true"` marks the page a menu of links is on.

## Keyboard and accessibility

| Key | On the button | In the menu |
| --- | --- | --- |
| `Enter`, `Space` | Opens the menu, the focus on the checked or the first item | Runs the item and closes the menu |
| `↓` / `↑` | Opens the menu on the first / the last item | The next / the previous item, round the ends |
| `Home` / `End` | - | The first / the last item |
| A letter | - | The next item that begins with it |
| `Esc` | - | Closes the menu, the focus back on the button |
| `Tab` | - | Closes the menu and moves on |

- The menu is `popover` with `role="menu"` and a name; its items are `menuitem`, `menuitemradio` or `menuitemcheckbox`. A list of site sections is navigation, not a menu - use links.
- The button carries `aria-haspopup="menu"`; the library keeps its `aria-expanded`, and the browser moves the focus back to it on close and closes the menu on a click outside or `Esc`.
- Where an engine does not put the menu beside its button - no anchor positioning below Firefox 147 and Safari 26, no anchor from a popover's button in Chrome 130-132, a table cell in Chrome 135-136, the bottom of a long page in Safari 26 - `js/menu-place.js` places it, again when the page scrolls or the menu or its button changes size, and the probes record each such place.

## Tokens

From [`css/components/menu.css`](https://github.com/redz-tech/REDZ.UI/blob/main/css/components/menu.css), which declares no custom property of its own.

It reads the theme's `--redz-space-4`, `--redz-border`, `--redz-radius-12`, `--redz-bg-raised`, `--redz-shadow-2`, `--redz-text`, `--redz-space-8`, `--redz-text-muted`, `--redz-fs-12`, `--redz-control-height`, `--redz-radius-8`, `--redz-bg-hover`, `--redz-text-faint`, `--redz-primary`, `--redz-danger`, `--redz-motion`, `--redz-ease`.

## From Bootstrap 5.3

| Bootstrap 5.3 | REDZ.UI | Change | Note |
| --- | --- | --- | --- |
| `dropdown` | - | remove | no wrapper: the menu flips by itself |
| `dropdown-center` | - | remove | no wrapper: the menu flips by itself |
| `dropdown-divider` | `menu-divider` | rename |  |
| `dropdown-header` | `menu-header` | rename |  |
| `dropdown-item` | `menu-item` | rename | role="menuitem" |
| `dropdown-item-text` | - | review | menu-header, or plain text |
| `dropdown-menu` | `menu` | rename | with popover and role="menu" |
| `dropdown-menu-dark` | - | remove | the theme sets it |
| `dropdown-menu-end` | - | remove | the menu meets its button's end edge by default |
| `dropdown-menu-start` | `menu-start` | rename |  |
| `dropdown-toggle` | - | review | the button: commandfor + command="toggle-popover" + aria-haspopup="menu"; a chevron is an icon |
| `dropdown-toggle-split` | - | review | a btn-group of the action and a btn-icon that opens the menu |
| `dropend` | - | remove | no wrapper: the menu flips by itself |
| `dropstart` | - | remove | no wrapper: the menu flips by itself |
| `dropup` | - | remove | no wrapper: the menu flips by itself |
| `dropup-center` | - | remove | no wrapper: the menu flips by itself |
| `data-bs-toggle="dropdown"` | `commandfor="ID" command="toggle-popover" aria-haspopup="menu"` | trigger | the menu gets popover and role="menu" |
| `data-bs-placement` | - | remove | anchor positioning places and flips the menu |
| `data-bs-offset` | - | remove | anchor positioning places and flips the menu |
| `data-bs-auto-close` | - | remove | anchor positioning places and flips the menu |
| `data-bs-display` | - | remove | anchor positioning places and flips the menu |
| `data-bs-reference` | - | remove | anchor positioning places and flips the menu |
| `data-bs-boundary` | - | remove | anchor positioning places and flips the menu |
| `data-bs-popper-config` | - | remove | anchor positioning places and flips the menu |
| `bootstrap.Dropdown` | `menu.showPopover(), menu.hidePopover(), menu.togglePopover()` | trigger |  |
| `show.bs.dropdown` | `toggle` | rename | the popover's toggle event; event.newState is "open" or "closed" |
| `shown.bs.dropdown` | `toggle` | rename | the popover's toggle event; event.newState is "open" or "closed" |
| `hide.bs.dropdown` | `toggle` | rename | the popover's toggle event; event.newState is "open" or "closed" |
| `hidden.bs.dropdown` | `toggle` | rename | the popover's toggle event; event.newState is "open" or "closed" |
