MobTranslate styleguide

Menubar

A persistent application-command surface with real composite keyboard behavior and coordinated menus.

Choose a command bar

Use Menubar for persistent application commands such as Record and View in an editor. Use a navigation bar/rail for destinations and a Toolbar for direct frequent actions. A menubar role alone does not create composite keyboard behavior.

This wrapper now uses the installed Menubar primitive and attached Menu roots/triggers. Arrow movement, menu coordination and disabled behavior come from that installed implementation; ordinary independent buttons are not a complete menubar.

Anatomy and specifications

PartActual contract
MenubarNamed div with installed menubar semantics, token surface/border/radius, 4px gap/padding; actual orientation also exposed through aria-orientation/data-orientation
Menu roots/triggersAttached child menus; default triggers become command-bar menu items, min 44px height with visible focus outline
Popup commandsNamed MenuPopup and real handlers/checkbox settings; same geometry as Menu
Vertical / narrow layoutVertical flex column; horizontal items can wrap. Wrapping preserves logical composite order rather than a spatial grid-navigation claim.
DirectionDirectionProvider plus DOM dir govern installed navigation and reading context

Working coordinated command bar

Local copies: 0.

Local command-bar notes are visible.

No local menubar command run. This bar operates this local example, not the site navigation.

tsx
'use client';
import { useState } from 'react';
import { Button, DirectionProvider, Menubar, Menu, MenuCheckboxItem, MenuCheckboxItemIndicator, MenuItem, MenuPopup, MenuPortal, MenuPositioner, MenuTrigger } from '@mobtranslate/ui';

export function MenubarCommandsExample() {
  const [notes, setNotes] = useState(true);
  const [count, setCount] = useState(0);
  const [vertical, setVertical] = useState(false);
  const [rtl, setRtl] = useState(false);
  const [disabled, setDisabled] = useState(false);
  const [result, setResult] = useState('No local menubar command run.');
  const direction = rtl ? 'rtl' : 'ltr';
  return <div data-menubar-lab className="space-y-4">
    <div className="flex flex-wrap gap-3"><Button type="button" variant="outline" onClick={() => setVertical(current => !current)}>{vertical ? 'Use horizontal command bar' : 'Use vertical command bar'}</Button><Button type="button" variant="outline" onClick={() => setRtl(current => !current)}>{rtl ? 'Use LTR command context' : 'Use RTL command context'}</Button><Button type="button" variant="outline" onClick={() => setDisabled(current => !current)}>{disabled ? 'Enable command bar' : 'Disable command bar'}</Button></div>
    <DirectionProvider direction={direction}><div dir={direction}>
      <Menubar aria-label="Local example commands" orientation={vertical ? 'vertical' : 'horizontal'} disabled={disabled} className={state => state.orientation === 'vertical' ? 'local-menubar-vertical' : undefined}>
        <Menu><MenuTrigger>Record</MenuTrigger><MenuPortal><MenuPositioner sideOffset={8}><MenuPopup aria-label="Local Record commands"><MenuItem onClick={() => { setCount(current => current+1); setResult('Added one local copy.'); }}>Add local copy</MenuItem><MenuItem disabled>Remote save (unavailable)</MenuItem></MenuPopup></MenuPositioner></MenuPortal></Menu>
        <Menu><MenuTrigger>View</MenuTrigger><MenuPortal><MenuPositioner sideOffset={8}><MenuPopup aria-label="Local View commands"><MenuCheckboxItem checked={notes} onCheckedChange={setNotes}><MenuCheckboxItemIndicator keepMounted aria-hidden="true">✓</MenuCheckboxItemIndicator>Show bar notes</MenuCheckboxItem></MenuPopup></MenuPositioner></MenuPortal></Menu>
        <Menu disabled><MenuTrigger>Unavailable</MenuTrigger></Menu>
      </Menubar>
    </div></DirectionProvider>
    <p data-menubar-count>Local copies: {count}.</p>{notes && <p data-menubar-notes>Local command-bar notes are visible.</p>}
    <Button type="button" variant="outline" onClick={() => { setCount(0); setNotes(true); setResult('Restored local command-bar state.'); }}>Restore command-bar state</Button>
    <p role="status">{result} This bar operates this local example, not the site navigation.</p>
  </div>;
}

API reference

PropInstalled contract
orientationhorizontal default; vertical alternative changes composite navigation and actual layout.
loopFocustrue default; controls wrapping at composite ends.
disabledfalse default; disables attached top-level triggers. If availability changes while a popup is already open, close that menu or guard its commands explicitly.
modaltrue default for open submenu interaction boundary; caller may choose false and verify outside behavior.
aria-label / aria-labelledbyRequired meaningful bar name; label identifies the command scope, not the site name alone.
className / style / render / refsInstalled part contracts; styled wrappers retain string-or-state class callbacks. Default item/submenu refs target their actual elements. Custom renders must preserve events, roles, names and refs.

Keyboard, state and local effects

  • Tab enters/leaves the composite rather than visiting each command as an independent tab stop. Arrow navigation follows actual orientation and direction; opening a child menu uses installed menu behavior.
  • The lab adds an actual local copy and toggles rendered notes. It includes an unavailable menu and whole-bar disable, plus independent restoration.
  • Opening another top-level command coordinates the existing menu rather than leaving unrelated popups stacked. Check focus after selection, Escape and sibling movement.
  • Vertical and RTL settings need DOM geometry plus actual key checks. A recorded pnpm patch synchronizes the installed Menu root’s parent context while closed, so a live orientation change is available before the next arrow key; it does not reset menu children or setting values. Pointer hover alone does not prove keyboard coordination.

Failures and recovery

  • The pinned dependency patch lives in patches/@base-ui-components__react@1.0.0-rc.0.patch and is applied by the root pnpm manifest/lockfile. It updates CJS and ESM MenuRoot parent-context synchronization. Re-run the first-key orientation tests before removing it during an upgrade.
  • The previous role-only div lacked installed composite state/navigation. New MenubarProps follow the installed primitive; state callback classes and orientation are now supported. Review event handlers that assumed independent tab stops.
  • Do not use application-command menu semantics for every site link. Keep destinations available through ordinary navigation and reserve commands for actions.
  • Narrow wrapping is not two-dimensional keyboard navigation. Prefer fewer meaningful groups or a task-specific compact command menu when a full bar becomes unwieldy.
  • An unavailable command bar should not remove the task’s recovery path. Local command completion is not a saved record or permission check.

Verification and release scope

  • Verify the actual command effect, requested/retained state, recovery and focus after activation. A closed menu or click callback is not a save receipt.
  • Check trigger naming/expanded state, item roles, group names, checked states, disabled enforcement, keyboard entry/exit, type navigation, submenu return and focus restoration. Inspect the exact installed behavior rather than assuming an upstream convention.
  • Check both themes, readable text/focus cues, long labels, bounded scrolling, 320px reflow, reduced motion, forced colors, zoom and the actual touch device. Context long press is not proven by mouse tests.
  • Use the checked example sources and the menu-family browser suite, then a named screen-reader/browser pair for spoken output. These local English-only examples prove no server write, permission, source authority or qualified review.