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.
<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.
<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.
<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
popoverwithrole="menu"and a name; its items aremenuitem,menuitemradioormenuitemcheckbox. A list of site sections is navigation, not a menu - use links. - The button carries
aria-haspopup="menu"; the library keeps itsaria-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.jsplaces 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, 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" |