Overview
.menu is a bordered list of .menu__item rows (280px max width, scrollable list). Use .menu--caret or .menu--caret-left for popover pointers. .dropdown wraps a field-like trigger, hint, and a floating .dropdown__panel that contains a menu.
| Part | Class | Role |
|---|---|---|
| Menu | .menu | Listbox container |
| List | .menu__list | Scrollable column of items |
| Item | .menu__item | Row button or option |
| Selected | .menu__item--selected | Active row (action surface) |
| Caret | .menu--caret | Centered popover arrow |
| Dropdown | .dropdown | Label + trigger + panel |
| Trigger | .dropdown__trigger | Opens panel (aria-expanded) |
| Panel | .dropdown__panel | Absolute menu host (z-index: 100) |
Prerequisites
- PimentCSS installed, Installation.
- Input fields, dropdown triggers extend
.field(Input fields). - Icons, row icons via
ph()(Phosphor in this doc). - Depth, panels use
box-shadow: var(--shadow-md)(Depth & shadows).
Menu item states
Rows are <button type="button"> elements. Doc-only .menu__item--hover previews hover; live UIs use :hover. Selected pairs with aria-selected="true". Disabled rows use the disabled attribute.
Menu variants
Plain menu, centered caret (.menu--caret), or left-aligned caret (.menu--caret-left). Items can be text-only or include leading and trailing icons.
<div class="menu" role="listbox" aria-label="Choices">
<div class="menu__list"><button type="button" class="menu__item" role="option">Option 1</button><button type="button" class="menu__item menu__item--selected" role="option" aria-selected="true">Option 2</button></div>
</div>
Dropdown
Closed and open states below are static previews. The live example wires toggle, selection, arrow keys, Home/End, Escape, and outside click (same pattern as the date picker and autocomplete).
Hint
Hint
Interactive dropdown
Add data-dropdown-live on .dropdown and call wireAllDropdowns() after load. Copy menu-dropdown-behavior.ts into your app.
import { bindDropdownDismiss, wireAllDropdowns } from './menu-dropdown-behavior';
bindDropdownDismiss();
wireAllDropdowns();
// Markup: .dropdown[data-dropdown-live], button.dropdown__trigger[aria-expanded][aria-controls],
// .menu[role="listbox"]#id, options with role="option" and optional data-label / data-value.
Hint
Class reference
| Class | Description |
|---|---|
.menu | Menu container (border, max 17.5rem) |
.menu__list | Scrollable flex column |
.menu__item | Row: flex, min-height field, dividers |
.menu__item--hover | Doc-only hover preview |
.menu__item--selected | Selected row (action colors) |
.menu__item--disabled | Disabled row |
.menu__item-icon | 24px icon slot |
.menu__item-label | Flexible label column |
.menu--caret | Top-center pointer pseudo-elements |
.menu--caret-left | Pointer offset to the left |
.dropdown | Vertical stack: label, hint, trigger, panel |
.dropdown__hint | Caption under label |
.dropdown__trigger | Field-styled opener |
.dropdown__value | Trigger label (--filled when set) |
.dropdown__chevron | Rotates when open |
.dropdown__panel | Hosts .menu, shadow md |
.dropdown--open | Shows panel, focus ring on trigger |
Customize (Sass)
-
Menu width and scroll
Override list width, item padding, and max height before importing components.@use "pimentcss-design-system" with ( $menu-width: 20rem, $menu-item-padding-x: 1rem, $menu-item-padding-y: 0.5rem, $menu-max-height: 18rem ); -
Rebuild CSS
Run after editing _menu.scss or _dropdown.scss.npm run build:css
Accessibility (RGAA / WCAG)
Menus and listboxes
- Listbox, use
role="listbox"on.menuandrole="option"on items; label witharia-labeloraria-labelledby. - Selection, set
aria-selected="true"on the active option; do not rely on background color alone. - Disabled, use the native
disabledattribute on buttons (not click handlers only). - Dropdown trigger, use a
buttonwitharia-expandedandaria-haspopup="listbox". - Keyboard, Arrow Up/Down, Home/End, Enter, and Escape on the live dropdown; close on outside click for pointer users.
- Values, use
data-labelfor visible trigger text anddata-valuefor the stored value when they differ. - Controls,
aria-controlson the trigger must reference the listboxid. - Focus,
:focus-visiblering is built into.menu__itemand the field trigger.