Morphos
Layout

Menubar

A horizontal menu bar with multiple dropdown menus, following the ARIA menubar pattern.

Interactive example

Installation

npm install @morphos/layout
pnpm add @morphos/layout
yarn add @morphos/layout
bun add @morphos/layout

Import

import {
  Menubar,
  MenubarMenu,
  MenubarTrigger,
  MenubarContent,
  MenubarItem,
  MenubarSeparator,
} from '@morphos/layout'

Usage

@Component()
class MyComponent extends StatefulComponent {
  @State() menubar = new Menubar({ 'aria-label': 'Application menu' })
  @State() fileMenu = new MenubarMenu({ menubar: this.menubar, value: 'file' })
  @State() editMenu = new MenubarMenu({ menubar: this.menubar, value: 'edit' })

  render() {
    return (
      <Menubar menubar={this.menubar}>
        <MenubarMenu menubar={this.menubar} value="file">
          <MenubarTrigger menu={this.fileMenu}>File</MenubarTrigger>
          <MenubarContent menu={this.fileMenu}>
            <MenubarItem menu={this.fileMenu} value="new" label="New" onSelect={() => console.log('new')} />
            <MenubarItem menu={this.fileMenu} value="open" label="Open…" onSelect={() => console.log('open')} />
            <MenubarSeparator />
            <MenubarItem menu={this.fileMenu} value="save" label="Save" onSelect={() => console.log('save')} />
          </MenubarContent>
        </MenubarMenu>

        <MenubarMenu menubar={this.menubar} value="edit">
          <MenubarTrigger menu={this.editMenu}>Edit</MenubarTrigger>
          <MenubarContent menu={this.editMenu}>
            <MenubarItem menu={this.editMenu} value="undo" label="Undo" onSelect={() => console.log('undo')} />
            <MenubarItem menu={this.editMenu} value="redo" label="Redo" onSelect={() => console.log('redo')} />
          </MenubarContent>
        </MenubarMenu>
      </Menubar>
    )
  }
}

MenubarContent renders its dropdown through a Portal (from @praxisjs/runtime), positioned with @morphos/core's computeAnchorPosition against the trigger's bounding rect.

Compound components

ComponentDescription
MenubarRoot — tracks which menu is active (role="menubar")
MenubarMenuOne dropdown in the bar — bridges Menubar and its parts, computes the dropdown's anchored position
MenubarTriggerButton that opens/closes a menu (role="menuitem")
MenubarContentThe dropdown panel, rendered in a Portal and anchored to the trigger
MenubarItemA selectable item inside the dropdown (role="menuitem")
MenubarSeparatorVisual divider (role="separator")

Props — Menubar

PropTypeDefaultDescription
aria-labelstringAccessible label for the menubar
classstringCSS class on the root <div role="menubar">
idstringid on the root element

Methods — Menubar

MethodDescription
open(id: string)Opens the menu with the given value, closing any other open menu
close()Closes the currently open menu
toggle(id: string)Opens the given menu if closed, or closes it if it's the currently open one

Props — MenubarMenu

PropTypeDefaultDescription
menubarMenubarThe shared Menubar root instance
valuestringUnique identifier for this menu
side"top" | "bottom" | "left" | "right""bottom"Which edge of the trigger the menu is placed against
align"start" | "center" | "end""start"Alignment of the menu along the trigger's perpendicular axis
sideOffsetnumber4Gap in pixels between the trigger and the menu
classstringCSS class
idstringHTML id

Props — MenubarTrigger

PropTypeDefaultDescription
menuMenubarMenuThe parent MenubarMenu instance
childrenChildrenButton label
classstringCSS class
idstringHTML id (defaults to the menu's generated trigger id)

Props — MenubarContent

PropTypeDefaultDescription
menuMenubarMenuThe parent MenubarMenu instance
childrenChildrenMenubarItem/MenubarSeparator elements
classstringCSS class applied to the rendered <ul>
idstringHTML id (defaults to the menu's generated content id)

Props — MenubarItem

PropTypeDefaultDescription
menuMenubarMenuThe parent MenubarMenu instance
valuestringItem identifier
labelstringDisplay text, rendered when no children are provided
onSelect() => voidCalled when the item is selected (before the menu closes)
disabledbooleanDisables the item and blocks selection
classstringCSS class
idstringHTML id

Props — MenubarSeparator

PropTypeDefaultDescription
classstringCSS class
idstringHTML id

Keyboard and pointer interaction

InteractionAction
Click a triggerToggle its menu open/closed; opening one menu closes any other
Click outside the trigger and contentClose the active menu
EscapeClose the active menu

Arrow-key navigation between menu items or between adjacent triggers is not implemented — MenubarContent only listens for Escape and outside clicks/mousedown on document while its menu is open.

data-* attributes

AttributeElementWhen present
data-disabledMenubarItemThe item's disabled prop is set

Each MenubarMenu receives both the shared Menubar instance (via the menubar prop) and is itself passed as the menu prop to its MenubarTrigger/MenubarContent/MenubarItem children. MenubarContent's position is recomputed from MenubarTrigger's bounding rect every time the menu opens, using the side/align/sideOffset props on MenubarMenu.

Styling example

.morphos-menubar-trigger:hover,
.morphos-menubar-trigger[aria-expanded="true"] {
  background: var(--morphos-color-bg-hover);
}

.morphos-menubar-content {
  /* Portaled to document.body, so it can't inherit the page's font. */
  font-family: var(--morphos-font);
  z-index: 100;
}

.morphos-menubar-item[data-disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

On this page