Dropdown
Build accessible action menus with focus management and complete keyboard navigation.
Dropdown
Dropdowns can stand alone or attach to a primary action as a split button. They support pointer dismissal, item selection, Escape, Tab, arrow keys, Home, and End. Disabled items are skipped.
<div class="bs-dropdown bs-btn-group" data-bs-dropdown>
<button class="bs-btn bs-btn-primary" type="button">Save changes</button>
<button class="bs-btn bs-btn-primary bs-btn-split bs-btn-caret"
id="save-toggle" type="button" data-bs-toggle="dropdown"
aria-controls="save-menu" aria-label="More save options"></button>
<div class="bs-dropdown-menu" id="save-menu" role="menu"
aria-labelledby="save-toggle" data-bs-dropdown-menu hidden>
<button class="bs-dropdown-item" type="button" role="menuitem">Save and publish</button>
</div>
</div>
<div class="bs-dropdown bs-btn-group" x-data="bsDropdown" x-bind="root">
<button class="bs-btn bs-btn-primary" type="button">Save changes</button>
<button class="bs-btn bs-btn-primary bs-btn-split bs-btn-caret"
id="save-toggle" type="button" data-bs-toggle="dropdown" x-bind="trigger"
aria-controls="save-menu" aria-label="More save options"></button>
<div class="bs-dropdown-menu" id="save-menu" role="menu"
aria-labelledby="save-toggle" data-bs-dropdown-menu x-bind="menu" hidden>
<button class="bs-dropdown-item" type="button" role="menuitem">Save and publish</button>
</div>
</div>
import { useDropdown } from "@boobstrap/react";
function SaveActions() {
const dropdown = useDropdown({ id: "save-menu" });
return (
<div className="bs-dropdown bs-btn-group" {...dropdown.getRootProps()}>
<button className="bs-btn bs-btn-primary" type="button">Save changes</button>
<button className="bs-btn bs-btn-primary bs-btn-split bs-btn-caret"
aria-label="More save options" {...dropdown.getTriggerProps()} />
<div className="bs-dropdown-menu"
{...dropdown.getMenuProps({ "aria-label": "Save options" })}>
<button className="bs-dropdown-item" type="button" role="menuitem">
Save and publish
</button>
</div>
</div>
);
}
<script setup>
import { useDropdown } from "@boobstrap/vue";
const dropdown = useDropdown({ id: "save-menu" });
</script>
<template>
<div class="bs-dropdown bs-btn-group" v-bind="dropdown.getRootProps()">
<button class="bs-btn bs-btn-primary" type="button">Save changes</button>
<button class="bs-btn bs-btn-primary bs-btn-split bs-btn-caret"
aria-label="More save options" v-bind="dropdown.getTriggerProps()"></button>
<div class="bs-dropdown-menu"
v-bind="dropdown.getMenuProps({ 'aria-label': 'Save options' })">
<button class="bs-dropdown-item" type="button" role="menuitem">
Save and publish
</button>
</div>
</div>
</template>
Structure
For a split action, combine .bs-dropdown and .bs-btn-group. The first button owns the default action; the second owns only the menu.
Class map
.bs-btn-split narrows the attached trigger, .bs-btn-caret draws its indicator, and .bs-dropdown-menu-end changes popup alignment.
Accessibility
Name the narrow trigger by purpose—“More save options,” not “Dropdown.” Use menu roles only for action menus, not ordinary site navigation.
Initialize and observe dropdown
The controller manages outside clicks, Escape, focus restoration, and action-menu keyboard navigation.
import { initBoobstrap } from "@boobstrap/boobstrap/js";
const boobstrap = initBoobstrap(document);
const menu = document.querySelector("#save-menu");
menu.addEventListener("bs:dropdown:shown", (event) => {
console.log("Menu opened", event.detail.controller);
});
// Later: boobstrap.destroy();Dropdown API
Import Dropdown from @boobstrap/boobstrap/js/dropdown.
| Contract | Purpose |
|---|---|
show() / hide() | Open or close the menu; hide can optionally restore focus. |
toggle() | Invert the current menu state. |
bs:dropdown:* | Observe or cancel menu lifecycle transitions. |