Skip to content

Navigation

Menubar

A horizontal application menu bar (WAI-ARIA menubar) whose menus reuse the DropdownMenu items.

<script>
  import { Menubar, MenubarMenu, DropdownMenuItem } from "@codeatlasdev/ui";
</script>

<Menubar aria-label="Application">
  <MenubarMenu label="File">
    <DropdownMenuItem onclick={newFile}>New file</DropdownMenuItem>
  </MenubarMenu>
  <MenubarMenu label="Edit">
    <DropdownMenuItem onclick={undo}>Undo</DropdownMenuItem>
  </MenubarMenu>
</Menubar>

Menubar is the WAI-ARIA menubar pattern for an application or editor: a row of top-level menus, each a DropdownMenu whose content is the same items (DropdownMenuItem, checkbox and radio items, DropdownMenuSub, labels, separators). The bar is one tab stop. Left and Right move between the bar items (Home and End jump, disabled menus are skipped, the ends wrap); Enter, Space or Down open a menu on its first item and Up on its last. Inside an open menu, Right and Left move to the adjacent menu and open it on its first item, except on a submenu trigger, where Right opens the submenu and Left closes it. Escape closes the menu and returns focus to its bar item; Tab closes it and leaves the bar.

With the pointer, a click opens a menu and a second click closes it. Once one menu is open, resting on another bar item switches to it; hover alone never opens a menu. Give the bar an aria-label.

A menubar is for actions in a desktop-style application. For a menu on one button use DropdownMenu; for site navigation with links use NavigationMenu, which is not role="menu".

#944

Editor menu bar

File (with an Export submenu), Edit, View (a checkbox and a radio group) and a disabled Help. Try Tab, arrows, Down, then Left and Right.

Word wrap: on. Zoom: 100%

Reference #944 · Pick in the gallery

Code
svelte
<script lang="ts">
  import {
    announce,
    DropdownMenuCheckboxItem,
    DropdownMenuItem,
    DropdownMenuLabel,
    DropdownMenuRadioGroup,
    DropdownMenuRadioItem,
    DropdownMenuSeparator,
    DropdownMenuSub,
    DropdownMenuSubTrigger,
    Menubar,
    MenubarMenu,
  } from '@codeatlasdev/ui';

  let last = $state('');
  let wrap = $state(true);
  let zoom = $state('100');

  function pick(action: string) {
    last = action;
    announce(`Last action: ${action}`);
  }
</script>

<Menubar aria-label="Editor">
  <MenubarMenu label="File">
    <DropdownMenuItem onclick={() => pick('New file')}>New file</DropdownMenuItem>
    <DropdownMenuItem onclick={() => pick('Open')}>Open</DropdownMenuItem>
    <DropdownMenuSub>
      {#snippet trigger()}<DropdownMenuSubTrigger>Export</DropdownMenuSubTrigger>{/snippet}
      <DropdownMenuItem onclick={() => pick('Export PDF')}>PDF</DropdownMenuItem>
      <DropdownMenuItem onclick={() => pick('Export HTML')}>HTML</DropdownMenuItem>
    </DropdownMenuSub>
    <DropdownMenuSeparator />
    <DropdownMenuItem variant="destructive" onclick={() => pick('Close')}>Close</DropdownMenuItem>
  </MenubarMenu>
  <MenubarMenu label="Edit">
    <DropdownMenuItem onclick={() => pick('Undo')}>Undo</DropdownMenuItem>
    <DropdownMenuItem onclick={() => pick('Redo')}>Redo</DropdownMenuItem>
  </MenubarMenu>
  <MenubarMenu label="View">
    <DropdownMenuCheckboxItem bind:checked={wrap}>Word wrap</DropdownMenuCheckboxItem>
    <DropdownMenuSeparator />
    <DropdownMenuLabel>Zoom</DropdownMenuLabel>
    <DropdownMenuRadioGroup bind:value={zoom} aria-label="Zoom">
      <DropdownMenuRadioItem value="100">100%</DropdownMenuRadioItem>
      <DropdownMenuRadioItem value="150">150%</DropdownMenuRadioItem>
    </DropdownMenuRadioGroup>
  </MenubarMenu>
  <MenubarMenu label="Help" disabled>
    <DropdownMenuItem>About</DropdownMenuItem>
  </MenubarMenu>
</Menubar>

<span class="note">Word wrap: {wrap ? 'on' : 'off'}. Zoom: {zoom}%{last ? `. Last action: ${last}` : ''}</span>

<style>
  .note { display: block; margin-block-start: var(--ca-space-3); font-size: var(--ca-font-size-body2); color: var(--ca-text-secondary); }
</style>
#945

Bar open state

onOpenChange fires when the bar goes from none open to one open and back, not for a switch between menus.

A menu is closed.

Reference #945 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { DropdownMenuItem, Menubar, MenubarMenu } from '@codeatlasdev/ui';

  let open = $state(false);
</script>

<Menubar aria-label="Document" onOpenChange={(o) => (open = o)}>
  <MenubarMenu label="Share">
    <DropdownMenuItem>Copy link</DropdownMenuItem>
    <DropdownMenuItem>Invite people</DropdownMenuItem>
  </MenubarMenu>
  <MenubarMenu label="Insert">
    <DropdownMenuItem>Table</DropdownMenuItem>
    <DropdownMenuItem>Image</DropdownMenuItem>
  </MenubarMenu>
</Menubar>
<span class="note">A menu is {open ? 'open' : 'closed'}.</span>

<style>
  .note { display: block; margin-block-start: var(--ca-space-3); font-size: var(--ca-font-size-body2); color: var(--ca-text-secondary); }
</style>

Props

PropTypeDefaultDescription
childrenSnippet–The `MenubarMenu`s.
onOpenChange(open: boolean) => void–Called when the bar goes from no menu open to one open, and back. Not called for a switch between menus.
aria-labelstring–Accessible name of the bar (also `aria-labelledby`).
classstring''Extra CSS classes on the bar.

Props

PropTypeDefaultDescription
labelstring–Text of the bar item.
childrenSnippet–The menu content: DropdownMenu items, checkbox and radio items, submenus, labels and separators.
disabledbooleanfalseThe bar item is announced as disabled, skipped by the arrow keys and never opens.
menuClassstring''Extra CSS classes on the menu surface.
classstring''Extra CSS classes on the bar item.