ナビゲーション

メニュー / メニューバー

解説あり

Menu and Menubar

アプリ的なメニュー。矢印キー操作と role="menu" 系のロールで構成します。

メニューバー(Menubar)とは?

メニューバーは、アプリ画面の上部に「ファイル/編集/表示…」と並ぶ、 あの水平のメニューです。各項目を開くとサブメニュー(コマンドの一覧)が出てきます。 デスクトップアプリのような機能豊富なコマンド体系を提供する場面で使います。

重要:サイトのナビゲーションに menu role を使わない

一番やりがちな誤りが、サイト内リンクのナビゲーションrole="menubar" / role="menu" / role="menuitem" にすることです。 これらの role はアプリのコマンド(実行すると何かが起きる操作)のためのものです。

  • ページへ移動する「リンク集」は <nav> + <ul> + <a> で作る。
  • role="menu" にすると、スクリーンリーダーは「メニュー=矢印で操作するコマンド群」と案内するのに、 中身はただのリンクで挙動が食い違い、利用者が混乱します。
  • サブメニュー付きのサイトナビは、<button aria-expanded> で開閉する 「ディスクロージャー・ナビゲーション」にするのが定石です。

なぜアクセシビリティが大事なの?

ライブデモ(推奨実装)

下はアプリ風のメニューバー(テキストエディタを想定)です。コマンドを実行する例なのでmenu role が適切です。キーボードで操作してみてください。

アクセシブルなメニューバー

試してみよう:Tab でバーに入る → ← → でメニュー移動 → ↓ / Enter でサブメニューを開く → ↑ ↓ で項目移動、Home / End で端へ → サブメニュー内で ← → を押すと隣のメニューへ → Enter で実行 → Esc で閉じて親に戻る。

キーボード操作

キー動作必須/任意
Tabメニューバーに入る/出る(バー全体で1タブストップ)必須
/ (バー上)前 / 次のメニューへ移動必須
/ Enter / Space(バー上)サブメニューを開き、最初の項目へ必須
/ (サブメニュー内)前 / 次の項目へ移動必須
/ (サブメニュー内)現在のメニューを閉じ、隣のメニューを開く任意(推奨)
Home / End最初 / 最後の項目へ移動任意
Enter / Space(項目上)コマンドを実行して閉じる必須
Escサブメニューを閉じ、親のメニュー項目へ戻る必須

必要な WAI-ARIA / ロール

付ける場所属性 / ロール意味
バーの箱role="menubar" + aria-label水平のメニューバーであることと名前を伝える。
バーの各項目role="menuitem" + aria-haspopup="menu"サブメニューを開くメニュー項目であることを伝える。
バーの各項目aria-expanded + tabindex="0 | -1"開閉状態と roving tabindex(1つだけ 0)。
サブメニューrole="menu" + aria-labelサブメニューであることと名前を伝える。
サブメニューの項目role="menuitem" + tabindex="-1"コマンド項目。フォーカスは JS で管理(roving)。
閉じたサブメニューhidden閉じている間は読み上げ/操作の対象外にする。

実装:推奨パターン(Good)

良い例 / 推奨

水平の role="menubar"role="menuitem" を並べ、roving tabindex で1タブストップに。 サブメニューは role="menu" で、開閉と Esc 復帰、 での メニュー移動を JS で実装します。

マークアップ:

<div role="menubar" aria-label="テキストエディタ">
  <button type="button" role="menuitem"
          aria-haspopup="menu" aria-expanded="false" tabindex="0">
    ファイル
  </button>
  <ul role="menu" aria-label="ファイル" hidden>
    <li role="menuitem" tabindex="-1">新規作成</li>
    <li role="menuitem" tabindex="-1">開く</li>
    <li role="menuitem" tabindex="-1">保存</li>
  </ul>

  <button type="button" role="menuitem"
          aria-haspopup="menu" aria-expanded="false" tabindex="-1">
    編集
  </button>
  <ul role="menu" aria-label="編集" hidden>
    <li role="menuitem" tabindex="-1">元に戻す</li>
    <li role="menuitem" tabindex="-1">やり直す</li>
  </ul>
</div>

キー操作の骨子:

// 上位(バー)の menuitem は roving tabindex で1タブストップ。
//  ← →  でバー内を移動、↓/Enter でサブメニューを開いて先頭へ。
// サブメニュー内は ↑↓ で移動、Esc で閉じて親へ戻す、← → で隣のメニューへ。
top.addEventListener('keydown', (e) => {
  if (e.key === 'ArrowRight') moveTop(+1);
  else if (e.key === 'ArrowLeft') moveTop(-1);
  else if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ')
    openSub(top, 0);   // 開いて先頭の項目へ
  else if (e.key === 'ArrowUp') openSub(top, 'last');
});

subItem.addEventListener('keydown', (e) => {
  if (e.key === 'ArrowDown') focusNextItem();
  else if (e.key === 'ArrowUp') focusPrevItem();
  else if (e.key === 'Escape') { closeSub(); top.focus(); } // 親へ戻す
  else if (e.key === 'ArrowRight' || e.key === 'ArrowLeft')
    moveToAdjacentMenu(e.key);  // 隣のメニューへ
  else if (e.key === 'Enter' || e.key === ' ') run(subItem);
});

アンチパターン(Bad)

下はサイトのナビゲーションrole="menubar" を付け、サブメニューを:hover で開く例です。役割の誤用と hover 依存が重なっています。

サイトナビを menu role 化+hover サブメニュー

試してみよう:「製品」にマウスを乗せると開きますが、キーボードではサブメニューを開けません。スクリーンリーダーは「メニュー(矢印で操作)」と案内するのに中身はただのリンクで、挙動がかみ合いません。

<!-- ❌ アンチパターン:サイトナビに menu role を付け、hover で開く -->
<nav>
  <ul role="menubar">
    <li role="menuitem">製品
      <!-- hover でしか開かない & リンクなのに menu 化で読み上げが壊れる -->
      <ul role="menu" class="sub">
        <li role="menuitem"><a href="/a">機能A</a></li>
        <li role="menuitem"><a href="/b">機能B</a></li>
      </ul>
    </li>
    <li role="menuitem"><a href="/price">料金</a></li>
  </ul>
</nav>

<style>
  .sub { display: none; }
  li:hover .sub { display: block; } /* hover 依存 */
</style>

悪い例 / 避ける

この実装の問題点:

  • role の誤用 — ページ移動のリンク集に menubar/menu を付けると、 支援技術の案内(矢印で操作するコマンド)と実体(リンク)が食い違う。
  • hover 依存 — サブメニューがマウスでしか開かず、キーボード/タッチで破綻。
  • キーボード操作の実装なし//Esc も roving tabindex もない。
  • 正しくは <nav> + <ul> + <a>、 サブメニューは <button aria-expanded> で開閉する。

実装チェックリスト


原文(英語):Menu and Menubar Pattern — W3C APG(新しいタブで開きます)