Skip to content

Menubar ​

A desktop-app-style top menu bar (File / Edit / View). Click or hover expands submenus (cascading popups), with arrow key support, Alt access keys and a focus trap.

Multi-group radio (independent checkmarks per group) ​

The value of a type: "group" item acts as a group id. Leaves inside the same group record their selection independently; when value is a JSON object string ({"group-id":"selected"}), groups don't interfere — both "Mode" and "Theme" can show a checkmark at once.

Multi-group radio

mode: preview, theme: dark

Action items (kind: "action") ​

Leaves with kind: "action" render as plain actions (menuitem): no checkmark, clicks do not write back value, and only oas-select is dispatched (detail.kind === "action"). Fits "Open / Save / About" style items.

Action items (kind: action)

value: mode (actions don't change it)

Shortcuts (shortcut) ​

The shortcut field (e.g. "Ctrl+N"): renders a key hint on the right, and auto-binds a document-level keydown — pressing the combo selects that item (preventDefault blocks the browser default). Binding rules: modifier+key combos bind directly; single keys bind only for function keys (F1–F12) — any other single key (letters/digits/Delete etc.) is display-only and not bound.

Shortcuts

Try Ctrl+N / Ctrl+O / Ctrl+S

Basic usage ​

Basic usage

Nothing selected

Disabled items and groups ​

Submenus support disabled, type: "divider" separators and type: "group" group titles.

Disabled items and groups

Nothing selected

Controlled selection ​

The value attribute is controlled (it is in observedAttributes): an external setAttribute('value', ...) takes effect immediately and syncs the selected item (check/highlight) to the corresponding leaf item; internal clicks also write back to value (uncontrolled channel), and the host can listen to oas-select to take over.

Controlled selection (value attribute)

Select "New"Select "Undo"Clear selectionvalue: -

Checkbox items and keep-open (checkbox + close-on-select) ​

Leaves with kind: "checkbox" toggle a multi-select checked set (value is a JSON array); toggling a checkbox does not close the submenu (continuous toggling). close-on-select="false" keeps the submenu open even after radio/action selections. close-on-select is a boolean attribute: presence means true (including an empty value); only an explicit "false" disables it — when the attribute is absent the default is to close.

Checkbox + close-on-select

Checked: ["grid"]

Controlled open (open) ​

The open attribute holds the value of the currently open top-level menu (open="" closes all). Controlled attribute: an external setAttribute('open', ...) opens/switches/closes immediately; internal changes write open back and dispatch oas-open-change (detail: { value, open }) for the host to take over.

Controlled open (open attribute)

Open "File"Open "Edit"Closeopen: -

Trigger mode and arrow-key wrap (trigger / loop) ​

trigger="click" (default): a top-level menu opens on click first; once one is open, hovering another top-level item switches to it (desktop convention). trigger="hover" opens directly on hover. loop="false" disables wrap-around at the edges.

Hover opens directly (trigger)

hover opens directly (no click-first)

No wrap-around (loop)

← stops at the first, → stops at the last

Disabled bar (disabled) ​

disabled disables the whole bar: top-level/sub-item clicks, keyboard navigation, shortcut hotkeys and Alt access keys are all blocked.

Disabled bar (disabled)

The whole bar is inert

side controls the first-level popup side (default bottom), align its alignment (default start), offset the gap to the trigger (px); show-arrow draws a visual arrow pointing at the trigger.

Popup positioning + arrow (side / align / offset / show-arrow)

side=top / align=end / offset=8 / show-arrow

Vertical orientation (orientation) ​

orientation="vertical": the bar stacks vertically, first-level popups default to the right; ↑/↓ move between top-level items, → opens a submenu.

Vertical (orientation)

Mobile hamburger folding (breakpoint) ​

breakpoint="600": when the viewport is ≤ 600px wide the bar folds into a hamburger button + popup menu (narrow-width folding); widen the window to restore the full bar.

Mobile hamburger (breakpoint)

Shrink the window (≤600px) to try the hamburger

Icons (icon) ​

Both top-level items and sub-items accept an icon field (icon-set name); icons follow the text color.

Icons (icon)

Icon color (iconColor) ​

Both top-level items and sub-items accept an iconColor field (fixes the icon color, overriding the selected/disabled default); it defaults to currentColor following the text color. The declarative child channel uses the icon-color attribute.

Icon color (iconColor / icon-color)

Leaves (both inside submenus and top-level leaves) with href render as real <a> links: right-click new window and middle-click open work natively, good for SEO; clicks still dispatch oas-select and write back value (the link's default navigation is not blocked). target="_blank" automatically adds rel="noopener".

Link navigation (real href links)

Clicking a leaf navigates and fires oas-select

Declarative child channel ​

Besides the items JSON, you can write items declaratively with <oas-menubar-item> / <oas-menubar-group> / <oas-menubar-divider> (the items attribute wins when explicitly set; otherwise child elements are parsed and converge to the same render path). The default slot text is the label; attributes map to the MenubarItem fields: value / disabled / icon / kind / danger / href / target / rel / shortcut / access-key / indeterminate. Nesting child elements directly inside <oas-menubar-item> recursively becomes a submenu; <oas-menubar-group> uses its label attribute as the group title (value can serve as a radio group id) and flattens its children to the same level. Child additions/removals, attribute and text changes re-render automatically (MutationObserver).

Declarative child channel (group / divider / nesting / checkbox / indeterminate / shortcut / danger / href)

File Project AProject BSaveComponent docsView Select allGridlinesDeleteNothing selected (try Alt+F to open "File", Ctrl+S to trigger Save)

Dynamic add/remove (MutationObserver auto-refresh)

Append a top-level itemFile New

Horizontal overflow folding (···) ​

When the container is too narrow, top-level items that don't fit fold into a trailing "···" item; clicking it opens a popup with the folded items (selecting works). When the selected item is folded, "···" highlights. Only in horizontal mode; vertical and the mobile hamburger don't fold. A folded top-level item that has a submenu is treated as a plain selectable item inside the popup (cascading submenus are not expanded there).

Overflow folding (narrow container)

Container fixed at 380px; overflowing items fold into "···"

Indeterminate checkboxes ​

kind: "checkbox" items support indeterminate: true: they render aria-checked="mixed" with a horizontal dash inside the box (distinct from the ✓ of a fully checked item), for parent/child "partially selected" scenarios. The half-selected state is computed by the host and passed via the items JSON; after toggling, the host can update the items to clear the flag.

Indeterminate checkbox

"Select all" is in the mixed state

Leading/trailing slots (start / end) ​

slot="start" (logo position) / slot="end" (avatar position) decorative slots: rendered inside the bar when they have content; keyboard navigation skips them automatically (not part of arrow keys or the focus trap).

start / end slots (logo / avatar)

Put a logo/graphic in `start` and an avatar in `end`; arrow keys move between menu items and never jump into the slots.

API ​

oas-menubar ​

Attributes ​

AttributeDescriptionTypeDefault
alignFirst-level popup alignment: start (default) / center / end; with side top/bottom it aligns on the horizontal axis, with left/right on the vertical axisstring—
breakpointMobile breakpoint (px, e.g. 600): when the viewport width is ≤ the breakpoint the bar collapses into a hamburger button + popup menu (narrow-width folding)string—
close-on-selectWhether selecting a leaf closes the open submenu, default true (desktop menubar convention); close-on-select="false" keeps it open (multi-select scenario); kind:"checkbox" items never close on togglestring—
disabledDisable the whole bar: top-level/sub-item clicks, keyboard navigation, shortcut hotkeys and Alt access keys are all blocked; visually desaturatedboolean—
itemsTop-level menu items JSON (with submenu children)string[]
loopArrow-key wrap-around toggle, default true (loops at edges); explicit loop="false" stops at the edgesstring—
offsetGap between the first-level popup and its trigger (px, default 4)string—
openValue of the currently open top-level menu (open="" closes all). Controlled attribute (in observedAttributes): an external setAttribute('open', ...) opens/switches/closes immediately; internal click/hover/keyboard changes write it back and dispatch oas-open-change, letting the host take overstring—
orientationArrangement direction: horizontal (default) / vertical (bar stacks vertically, first-level popups default to the right, arrow keys move top-level items up/down)——
show-arrowShow a visual arrow on the popup pointing at the trigger——
sideFirst-level popup side: bottom (default horizontal) / top / left / right (default vertical); cascading submenus are unaffectedstring—
triggerTop-level menu trigger: click (default, click to open first, then hover switches — desktop convention) / hover (hover opens directly)——
valueSelected value. As a plain string it is a single global selection (no-group scenarios, backward compatible); as a JSON object string (e.g. {"mode":"preview","theme":"dark"}) selections are recorded per group id — the value of a type:"group" item acts as the group id; as a JSON array string (e.g. ["grid","wrap"]) it is the checkbox checked-set (kind:"checkbox" items, multi-select)string—

Events ​

EventDescription
oas-open-changeThe open top-level menu changed, detail: { value, open } (value = currently open top-level menu value, open = whether anything is open). Fired both on controlled setAttribute('open') and internal click/hover/keyboard changes (not on the first frame)
oas-selectAn item was selected, detail: { value, kind?, checked? }. kind only appears for action items (kind: "action"); checkbox items carry checked (new checked state); radio items omit detail.kind

Slots ​

NameDescription
endTrailing decorative slot (e.g. avatar): <div slot="end"> renders when it has content; keyboard navigation skips it
startLeading decorative slot (e.g. logo): <div slot="start"> renders when it has content; keyboard navigation skips it

oas-menubar-item ​

Attributes ​

AttributeDescriptionTypeDefault
access-keyAlt access key (single character); defaults to the first ASCII letter of the label——
dangerDestructive item: danger color semantics (delete/logout operations)——
disabledDisable this item——
hrefLink URL: with href the item renders as a native <a> (real navigation + still fires oas-select)——
iconLeading icon (@oas-ui/icons registry icon name)——
icon-colorIcon color: fixes the icon to this color (overrides the selected/disabled default); defaults to currentColor following the text color——
indeterminateCheckbox indeterminate state: renders aria-checked="mixed" + a dash inside the box (only for kind="checkbox" items)——
kindLeaf semantics: radio (default, selectable) / action (no checked state, does not write back value) / checkbox (multi-select, value is the checked-set array)——
relLink rel (with href)——
shortcutShortcut hint (e.g. "Ctrl+N"): rendered as a trailing kbd + a document-level keydown binding that triggers select on hit——
targetLink target (with href)——
valueSelection value (data-carrier field of the declarative child channel)——

Slots ​

NameDescription
defaultMenubar item label content (default slot text); direct child <oas-menubar-item>/<oas-menubar-group>/<oas-menubar-divider> elements recursively become the submenu children

oas-menubar-group ​

Attributes ​

AttributeDescriptionTypeDefault
labelGroup title (small secondary text, not clickable)——
valueRadio group id (picking inside the group only updates that group's selected value)——

Slots ​

NameDescription
defaultGroup items: child <oas-menubar-item>/<oas-menubar-group>/<oas-menubar-divider> elements flatten to the same level

oas-menubar-divider ​

Slots ​

NameDescription
defaultDivider data carrier (no attributes; the host parses it as type: "divider")

Event detail note: the detail of oas-select is a component-internal object (value/kind) — not a native Event, so you can't preventDefault() on it or read a native event.target from it. To reach the native event, use the outer parameter of your listener (e.g. in addEventListener('oas-select', (e) => ...), e is a CustomEvent and e.detail is the component data).

MenubarItem fields (inherits MenuItem):

FieldDescriptionType
labelMenu textstring
valueSelection value (declared in the items JSON; after render the host tag carries a data-value lowercased attribute for internal lookup — hosts shouldn't rely on it as public API)string
kindLeaf semantics: radio (default, checkable, participates in value) / action (action item, no checkmark, doesn't write back value on click)string
shortcutShortcut hint (e.g. "Ctrl+N"); renders as a right-side kbd and auto-binds a document-level keydown (selects the item on match, preventDefault)string
accessKeyAlt access key (single character); defaults to the first ASCII letter of labelstring
disabledDisabledboolean
childrenSubmenu items (nested recursively, cascading to the right)MenubarItem[]

Keyboard: at top level ←/→ switch, ↓/Enter opens the submenu, Esc closes; inside a submenu ↑/↓ move, → enters a cascading submenu, ← returns to the parent; Home/End jump. Pressing Alt alone focuses the menu bar, Alt + access key opens the matching top-level menu. While a submenu is open, Tab cycles among its items (focus trap); roving tabindex keeps only the current top-level item tab-reachable.

Released under the MIT OR Apache-2.0 License.