Webパーツ辞典

メニューバーMenubar

「ファイル」「編集」「表示」のように、アプリの操作を種類ごとの見出しで横に並べた帯。見出しを押すと、その下に操作の一覧が垂れ下がる。

表示例

別名・英語表記

  • アプリケーションメニュー
  • ファイルメニュー
  • プルダウンメニューバー
  • メインメニューバー
  • ファイル・編集・表示のメニュー
  • Menubar

実装例

<div class="x-menubar">
  <ul class="x-menubar__bar" role="menubar" aria-label="メモ帳">
    <li role="none"><button class="x-menubar__top" type="button" role="menuitem" aria-haspopup="menu" aria-expanded="false" tabindex="-1">ファイル</button></li>
    <!-- 見本なので「編集」を開いた状態。フォーカスが乗っている項目だけ tabindex="0"(ロービング tabindex) -->
    <li role="none" class="x-menubar__item">
      <button class="x-menubar__top" type="button" role="menuitem" aria-haspopup="menu" aria-expanded="true" aria-controls="x-menubar-edit" tabindex="0">編集</button>
      <ul class="x-menubar__menu" id="x-menubar-edit" role="menu" aria-label="編集">
        <li role="menuitem" tabindex="-1" aria-keyshortcuts="Control+Z">元に戻す<kbd class="x-menubar__k" aria-hidden="true">Ctrl+Z</kbd></li>
        <li role="menuitem" tabindex="-1" aria-disabled="true" aria-keyshortcuts="Control+Y">やり直す<kbd class="x-menubar__k" aria-hidden="true">Ctrl+Y</kbd></li>
        <li role="separator" class="x-menubar__sep"></li>
        <li role="menuitem" tabindex="-1" aria-keyshortcuts="Control+X">切り取り<kbd class="x-menubar__k" aria-hidden="true">Ctrl+X</kbd></li>
        <li role="menuitem" tabindex="-1" aria-keyshortcuts="Control+C">コピー<kbd class="x-menubar__k" aria-hidden="true">Ctrl+C</kbd></li>
        <li role="menuitem" tabindex="-1" aria-keyshortcuts="Control+V">貼り付け<kbd class="x-menubar__k" aria-hidden="true">Ctrl+V</kbd></li>
      </ul>
    </li>
    <li role="none"><button class="x-menubar__top" type="button" role="menuitem" aria-haspopup="menu" aria-expanded="false" tabindex="-1">表示</button></li>
    <li role="none"><button class="x-menubar__top" type="button" role="menuitem" aria-haspopup="menu" aria-expanded="false" tabindex="-1">ヘルプ</button></li>
  </ul>
  <div class="x-menubar__doc" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
</div>

AIへの指示文

「メニューバー(Menubar)」を実装してください。

## これは何か
「ファイル」「編集」「表示」のように、アプリの操作を種類ごとの見出しで横に並べた帯。見出しを押すと、その下に操作の一覧が垂れ下がる。

## 使いどころ
- 文書エディターや表計算、画像編集のように、Web 上で動くアプリの操作が数十個あり、種類ごとにまとめて置きたいとき
- デスクトップのアプリと同じ操作感(矢印キーで見出しを渡り歩く)を求める利用者が多いとき

## 実装の要件
- ul[role="menubar"][aria-label] > li[role="none"] > button[role="menuitem"][aria-haspopup="menu"][aria-expanded] + ul[role="menu"] > li[role="menuitem"] の構成にする
- キー操作はすべて JavaScript で実装する。role を付けただけでは矢印キーは動かず、読み上げ環境の案内と実際の操作が食い違う
- 一覧は見出しを基準に position: absolute; top: 100%; で重ね、同時に開くのは1つだけにする
- 一覧の外をクリックしたときと、フォーカスが外へ出たときにも閉じる
- 普通のサイトの行き先の一覧(グローバルナビ)には使わない。WAI-ARIA APG は、見た目がメニューバーに似たサイトのナビゲーションには menubar の複雑な操作が要らないとし、Disclosure の形(ボタンで開いてリンクを並べる)を勧めている
- 押すと何かを実行する項目と、下の一覧を開く見出しを1つの項目に兼ねさせない(APG も避けるよう勧めている)
- マウスを乗せただけで開く作りにしない。見出しを押して開き、開いている間だけ隣の見出しへマウスを移すと切り替わるようにする
- いま使えない操作も一覧から消さず、押せない見た目で残す。場所が毎回変わると覚えられない
- スマートフォンでは横に並びきらない。狭い画面では1つのメニューボタンにまとめる

## アクセシビリティの要件
- WAI-ARIA APG の Menubar の形にする。帯に role="menubar"、開く一覧に role="menu"、各項目に role="menuitem"、区切りに role="separator" を付ける
- Tab で止まるのは帯の中の1項目だけにする(ロービング tabindex: いまの項目が 0、他は -1)。帯の中は左右の矢印キーで移り、下矢印・Enter・Space で一覧を開いて先頭へ移る
- 一覧の中は上下の矢印キーで移り、Esc で閉じて元の見出しへフォーカスを戻す。Home / End と、文字を打つとその文字で始まる項目へ移る操作も APG に載っている
- 一覧を持つ見出しに aria-haspopup="menu" と aria-expanded を付ける
- 使えない項目は aria-disabled="true" にし、フォーカスは止まれるままにする(どこに何があるかを読み上げで辿れるようにする)
- ショートカットキーを添えるときは aria-keyshortcuts に書く

## 参考にする実装
この形を出発点にして、プロジェクトの命名規則・色・余白に合わせて調整してください。

```html
<div class="x-menubar">
  <ul class="x-menubar__bar" role="menubar" aria-label="メモ帳">
    <li role="none"><button class="x-menubar__top" type="button" role="menuitem" aria-haspopup="menu" aria-expanded="false" tabindex="-1">ファイル</button></li>
    <!-- 見本なので「編集」を開いた状態。フォーカスが乗っている項目だけ tabindex="0"(ロービング tabindex) -->
    <li role="none" class="x-menubar__item">
      <button class="x-menubar__top" type="button" role="menuitem" aria-haspopup="menu" aria-expanded="true" aria-controls="x-menubar-edit" tabindex="0">編集</button>
      <ul class="x-menubar__menu" id="x-menubar-edit" role="menu" aria-label="編集">
        <li role="menuitem" tabindex="-1" aria-keyshortcuts="Control+Z">元に戻す<kbd class="x-menubar__k" aria-hidden="true">Ctrl+Z</kbd></li>
        <li role="menuitem" tabindex="-1" aria-disabled="true" aria-keyshortcuts="Control+Y">やり直す<kbd class="x-menubar__k" aria-hidden="true">Ctrl+Y</kbd></li>
        <li role="separator" class="x-menubar__sep"></li>
        <li role="menuitem" tabindex="-1" aria-keyshortcuts="Control+X">切り取り<kbd class="x-menubar__k" aria-hidden="true">Ctrl+X</kbd></li>
        <li role="menuitem" tabindex="-1" aria-keyshortcuts="Control+C">コピー<kbd class="x-menubar__k" aria-hidden="true">Ctrl+C</kbd></li>
        <li role="menuitem" tabindex="-1" aria-keyshortcuts="Control+V">貼り付け<kbd class="x-menubar__k" aria-hidden="true">Ctrl+V</kbd></li>
      </ul>
    </li>
    <li role="none"><button class="x-menubar__top" type="button" role="menuitem" aria-haspopup="menu" aria-expanded="false" tabindex="-1">表示</button></li>
    <li role="none"><button class="x-menubar__top" type="button" role="menuitem" aria-haspopup="menu" aria-expanded="false" tabindex="-1">ヘルプ</button></li>
  </ul>
  <div class="x-menubar__doc" aria-hidden="true"><span></span><span></span><span></span><span></span></div>
</div>
```

```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-menubar,
.x-menubar *:not([class*="fa-"]),
.x-menubar *:not([class*="fa-"])::before,
.x-menubar *:not([class*="fa-"])::after {
  box-sizing: border-box;
  /* 貼った先の既定の書体に引きずられないようにする。書体が変わると
     文字の幅と高さが変わり、枠のサイズまでずれる */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}

/* 外枠はアプリの画面の上部を切り取った見本 */
.x-menubar {
  width: 300px;
  height: 196px;
  border: 1px solid #e3e6ea;
  border-radius: 8px;
  background: #fff;
  color: #1b2028;
  overflow: hidden;
}
.x-menubar__bar {
  display: flex;
  margin: 0;
  padding: 0 4px;
  border-bottom: 1px solid #e3e6ea;
  background: #f3f4f6;
  list-style: none;
}
.x-menubar__item {
  position: relative;
}
.x-menubar__top {
  min-height: 30px;
  padding: 0 10px;
  border: 0;
  border-radius: 4px;
  background: none;
  color: #1b2028;
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
}
.x-menubar__top:hover,
.x-menubar__top[aria-expanded="true"] {
  background: #dbe3f7;
}
.x-menubar__menu {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 10;
  width: 176px;
  margin: 2px 0 0;
  padding: 4px;
  border: 1px solid #7f8a99;
  border-radius: 6px;
  background: #fff;
  box-shadow: 0 6px 14px rgba(27, 32, 40, 0.14);
  list-style: none;
}
.x-menubar__menu[hidden] {
  display: none;
}
.x-menubar__menu [role="menuitem"] {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 26px;
  padding: 0 8px;
  border-radius: 4px;
  font-size: 12px;
  cursor: pointer;
}
.x-menubar__menu [role="menuitem"]:hover {
  background: #f1f3f6;
}
/* 使えない項目も消さずに残す(どこにあるかを覚えてもらうため)。押せないことは aria-disabled で伝える */
.x-menubar__menu [aria-disabled="true"] {
  color: #6b7280;
  cursor: default;
}
.x-menubar__menu [aria-disabled="true"]:hover {
  background: none;
}
.x-menubar__k {
  color: #5b6472;
  font-family: inherit;
  font-size: 11px;
}
.x-menubar__sep {
  height: 1px;
  margin: 4px 6px;
  background: #e3e6ea;
}
.x-menubar__top:focus-visible,
.x-menubar__menu [role="menuitem"]:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: -2px;
}
/* 本文の場所。メニューが本文の上に重なって開くことを見せるための飾り */
.x-menubar__doc {
  display: grid;
  gap: 10px;
  padding: 14px 14px 0;
}
.x-menubar__doc span {
  height: 6px;
  border-radius: 3px;
  background: #e3e6ea;
}
```

## 出力の約束
- HTML と CSS を分けて出す。JavaScript が要るときは素の JavaScript で書き、UIフレームワークは使わない。
- 参考実装がアイコンに Font Awesome のクラスを使っている場合は、プロジェクトで使っているアイコンかインライン SVG に置き換えてよい。
- 上のアクセシビリティ要件を満たす。満たせない箇所があるときは、理由を添えて先に知らせる。
- 色・角丸・余白は既存のデザイントークンがあればそれを使う。無ければ CSS 変数として切り出す。

使いどころ

  • 文書エディターや表計算、画像編集のように、Web 上で動くアプリの操作が数十個あり、種類ごとにまとめて置きたいとき
  • デスクトップのアプリと同じ操作感(矢印キーで見出しを渡り歩く)を求める利用者が多いとき

注意点

  • 普通のサイトの行き先の一覧(グローバルナビ)には使わない。WAI-ARIA APG は、見た目がメニューバーに似たサイトのナビゲーションには menubar の複雑な操作が要らないとし、Disclosure の形(ボタンで開いてリンクを並べる)を勧めている
  • 押すと何かを実行する項目と、下の一覧を開く見出しを1つの項目に兼ねさせない(APG も避けるよう勧めている)
  • マウスを乗せただけで開く作りにしない。見出しを押して開き、開いている間だけ隣の見出しへマウスを移すと切り替わるようにする
  • いま使えない操作も一覧から消さず、押せない見た目で残す。場所が毎回変わると覚えられない
  • スマートフォンでは横に並びきらない。狭い画面では1つのメニューボタンにまとめる

アクセシビリティの要点

  • WAI-ARIA APG の Menubar の形にする。帯に role="menubar"、開く一覧に role="menu"、各項目に role="menuitem"、区切りに role="separator" を付ける
  • Tab で止まるのは帯の中の1項目だけにする(ロービング tabindex: いまの項目が 0、他は -1)。帯の中は左右の矢印キーで移り、下矢印・Enter・Space で一覧を開いて先頭へ移る
  • 一覧の中は上下の矢印キーで移り、Esc で閉じて元の見出しへフォーカスを戻す。Home / End と、文字を打つとその文字で始まる項目へ移る操作も APG に載っている
  • 一覧を持つ見出しに aria-haspopup="menu" と aria-expanded を付ける
  • 使えない項目は aria-disabled="true" にし、フォーカスは止まれるままにする(どこに何があるかを読み上げで辿れるようにする)
  • ショートカットキーを添えるときは aria-keyshortcuts に書く

作るときに使えるツール

見分け方

関連するパーツ