Webパーツ辞典

ドロップダウンメニューDropdown menu

押すと下に選択肢が開くメニュー。入力欄ではなく、操作を選ばせる点がセレクトボックスと違う。

表示例

アイコン: Font Awesome 7 Free(fa-solid / fa-regular)

別名・英語表記

  • プルダウンメニュー
  • 展開メニュー
  • メニュー
  • ドロップダウン
  • メニューリストボックス
  • Dropdown menu

実装例

<div class="x-dropdown">
  <button class="x-dropdown__b" type="button" aria-expanded="true" aria-haspopup="true" aria-controls="x-dropdown-m">
    操作
    <i class="fa-solid fa-chevron-down" aria-hidden="true"></i>
  </button>
  <!-- 見本なので開いた状態で流れの中に置いている。実際は ul に popover 属性、button に popovertarget="x-dropdown-m" を付けて開閉し、
       aria-expanded は false から始めて toggle イベントで書き換える(hidden 属性では閉じない) -->
  <ul class="x-dropdown__m" id="x-dropdown-m" role="menu">
    <li role="menuitem" tabindex="-1">名前を変える</li>
    <li role="menuitem" tabindex="-1">複製する</li>
    <li role="menuitem" tabindex="-1" class="x-dropdown__danger">削除する</li>
  </ul>
</div>

AIへの指示文

「ドロップダウンメニュー(Dropdown menu)」を実装してください。

## これは何か
押すと下に選択肢が開くメニュー。入力欄ではなく、操作を選ばせる点がセレクトボックスと違う。

## 使いどころ
- 「編集」「複製」「削除」のように、その場で実行する操作をまとめるとき
- ヘッダーで、下の階層のページを一段見せたいとき

## 実装の要件
- button[type=button][popovertarget="id"][aria-expanded][aria-controls="id"] + ul[popover][id] の構成にする。項目を選んだら hidePopover() で閉じる(項目の click は外側クリックではないので、書かないと開いたまま残る)
- メニューに popover 属性を付け、button の popovertarget で開閉する(閉じた状態を hidden 属性で持たない)。外側クリックと Esc で閉じる動きはブラウザが持つ(Chrome・Edge は 114 から、Safari は 17 から、Firefox は 125 から。iOS の Safari が外側のタップで閉じるのは 18.3 から)
- aria-expanded はメニューの toggle イベントの newState(open / closed)に合わせて true / false に書き換え、外側クリックや Esc で閉じたときも古い値を残さない
- 開いたら中の項目へフォーカスを移すので、開くボタンの click で先に this.focus() を呼ぶ。Safari と macOS の Firefox はマウスで押したボタンにフォーカスを移さず、そのままだと Esc で閉じたときにボタンへ戻らない
- popover は最前面の層に出て親の position: relative が効かない。位置は見本の margin も含めて @supports (position-area: block-end) の中だけに書き、押したボタンが暗黙のアンカーになるのを使う(Chrome・Edge は 133 から、Safari は 26 から、Firefox は 147 から)
- @supports の中では margin: 6px 0 0; inset: auto; width: max-content; min-width: anchor-size(width); position-area: block-end span-inline-end; を指定し、ボタンの下に左端をそろえて、ボタン以上の幅で開く
- 同じ @supports の中で position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline; を付け、下端では上へ、右端ではボタンの右端にそろえて左へ回す。width: max-content が無いと、右端で回らずに細く縮む
- position-area に対応しないブラウザ向けに、@supports not (position-area: block-end) の中でメニューに margin: auto; を書き、画面の中央に出す(popover の既定の margin: auto に頼ると、margin を 0 にするリセット CSS に消されて画面の端に張りつく)
- 値を選ばせたいなら select を使う。操作を選ばせるのがドロップダウン
- ホバーだけで開くメニューは、タッチ環境で扱えない
- 入れ子のメニューは、マウスを滑らせると閉じてしまう。1段に留める

## アクセシビリティの要件
- 開く要素は button にし、aria-expanded を付ける。中身が role="menu" のときだけ aria-haspopup="true" も付け、リンク集では付けない
- 中身は role="menu" / role="menuitem"(本当にメニューのときだけ。リンク集には使わない)
- 矢印キーで項目を移動でき、Esc で閉じ、開いたボタンへ戻る

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

```html
<div class="x-dropdown">
  <button class="x-dropdown__b" type="button" aria-expanded="true" aria-haspopup="true" aria-controls="x-dropdown-m">
    操作
    <i class="fa-solid fa-chevron-down" aria-hidden="true"></i>
  </button>
  <!-- 見本なので開いた状態で流れの中に置いている。実際は ul に popover 属性、button に popovertarget="x-dropdown-m" を付けて開閉し、
       aria-expanded は false から始めて toggle イベントで書き換える(hidden 属性では閉じない) -->
  <ul class="x-dropdown__m" id="x-dropdown-m" role="menu">
    <li role="menuitem" tabindex="-1">名前を変える</li>
    <li role="menuitem" tabindex="-1">複製する</li>
    <li role="menuitem" tabindex="-1" class="x-dropdown__danger">削除する</li>
  </ul>
</div>
```

```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-dropdown,
.x-dropdown *:not([class*="fa-"]),
.x-dropdown *:not([class*="fa-"])::before,
.x-dropdown *: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-dropdown {
  width: 160px;
}
.x-dropdown__b {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  min-height: 36px;
  padding: 0 12px;
  border: 1px solid #cfd4dc;
  border-radius: 8px;
  background: #fff;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.x-dropdown__m {
  margin: 6px 0 0;
  padding: 5px;
  border: 1px solid #e3e6ea;
  border-radius: 8px;
  background: #fff;
  list-style: none;
}
.x-dropdown__m li {
  padding: 6px 8px;
  border-radius: 6px;
  font-size: 11.5px;
  cursor: pointer;
}
.x-dropdown__m li:hover {
  background: #f1f3f6;
}
.x-dropdown__danger {
  color: #b91c1c;
}
```

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

使いどころ

  • 「編集」「複製」「削除」のように、その場で実行する操作をまとめるとき
  • ヘッダーで、下の階層のページを一段見せたいとき

注意点

  • 値を選ばせたいなら select を使う。操作を選ばせるのがドロップダウン
  • ホバーだけで開くメニューは、タッチ環境で扱えない
  • 入れ子のメニューは、マウスを滑らせると閉じてしまう。1段に留める

アクセシビリティの要点

  • 開く要素は button にし、aria-expanded を付ける。中身が role="menu" のときだけ aria-haspopup="true" も付け、リンク集では付けない
  • 中身は role="menu" / role="menuitem"(本当にメニューのときだけ。リンク集には使わない)
  • 矢印キーで項目を移動でき、Esc で閉じ、開いたボタンへ戻る

作るときに使えるツール

見分け方

関連するパーツ