はじめに 🌟
Fluent UI 2 の Menu は、画面上に常時表示しない選択肢を、必要なタイミングで開いて提示するためのコンポーネントです。
単なる「項目の一覧」ではなく、どの操作を今ここで選んでもらうかを設計する部品です。
本記事では、まず「Fluent UI 2 とは」を短く整理し、Fluent UI 2 シリーズ記事をまとめて紹介します。
そのうえで、Fluent UI 2(React)と Fluent UI Blazor 5 の Menu を、特に使い方と機能差に絞って比較します。
参照した公式情報はこちらです。
Fluent UI 2 とは
Fluent UI 2 は、Microsoft Fluent 2 デザインシステムに沿って UI を設計・実装するための考え方とコンポーネント群です。
見た目の統一だけでなく、情報の優先順位、操作導線、アクセシビリティまで一体で扱うことを重視します。
Menu も同じで、「どこに置くか」より先に「何を選ばせるか」「選んだ結果がどうなるか」を決めるのが基本です。
入力値を収集する用途なら Menu ではなく、Select / Dropdown / Combobox を選ぶ、というのが Fluent UI 2 のガイダンスです。
Fluent UI 2 シリーズ紹介 📚
Fluent UI 2 シリーズの既存記事は次のとおりです。
- Fluent UI 2 の Accordion を理解する — 情報設計・アクセシビリティ・React 実装
- Fluent UI 2 の Avatar を整理しつつ Fluent UI Blazor 5 でどう実装するか
- Fluent UI 2 の Badge を理解する — Fluent UI Blazor 5 との比較と実装ポイント
- Fluent UI 2 の Breadcrumb を理解する — Fluent UI Blazor 5 との対応とアクセシビリティ
- Fluent UI 2 の Button を理解する — 種類・レイアウト・アクセシビリティ・Blazor 5 比較
- Fluent UI 2 の Card を理解する — React と Fluent UI Blazor の違い
- Fluent UI 2 の Checkbox を理解する — React と Fluent UI Blazor v5 の違い
- Fluent UI 2 の Combobox を理解する — Fluent UI Blazor 5 との比較と使い分け
- Fluent UI 2 の Dialog を理解する — React と Fluent UI Blazor 5 の違いと使い分け
- Fluent UI 2 の Divider を理解する — Fluent UI Blazor 5 との比較
- Fluent UI 2 の Dropdown を理解する — Fluent UI Blazor 5 の近縁コンポーネント比較とアクセシビリティ
- Fluent UI 2 の Field を理解する — Fluent UI Blazor 5 と比較する入力設計の基礎
- Fluent UI 2 の Icon を理解する — Fluent UI Blazor の FluentIcon 比較とアクセシビリティ
- Fluent UI 2 の Image を理解する — Fluent UI Blazor 5 と比較しながらレイアウトとアクセシビリティを整理する
- Fluent UI 2 の Label を理解する — Fluent UI Blazor 5 と比較するラベル設計の基礎
- Fluent UI 2 の Link を理解する — Fluent UI Blazor 5 と比較するリンク設計とアクセシビリティ
- Fluent UI 2 のアクセシビリティを色から読む ─ WCAG 2.1 と対比しながら整理する
- Fluent UI 2 で始めるアクセシビリティ実装 — キーボード操作・支援技術・WCAG 2.1 の実践ガイド
今回のゴール ✅
- Fluent UI 2 の Menu の基本方針が分かる
- Fluent UI 2 と Fluent UI Blazor 5 の使用方法と機能差が分かる
- Menu のガイダンス、レイアウト、動作の要点を整理できる
- アクセシビリティとコンテンツ設計の実務ポイントを押さえられる
Fluent UI 2 と Fluent UI Blazor 5 の比較(使用方法・機能差)
結論から言うと、Fluent UI 2(React)は「トリガーと項目構造を JSX で組み立てる」設計で、Fluent UI Blazor 5 は「FluentMenu + FluentMenuList + FluentMenuItem を Razor で構成する」設計です。
| 観点 | Fluent UI 2 (React) | Fluent UI Blazor 5 |
|---|---|---|
| 🧭 基本の使い方 | ボタンやアイコンをトリガーにして Menu を開く |
Trigger に対象要素の id を渡して紐づける |
| 🧱 構成要素 | Menu / MenuItem / submenu 構造 |
FluentMenu / FluentMenuList / FluentMenuItem
|
| ✅ 選択タイプ | default / checkbox / radio |
MenuItemRole.MenuItem / Checkbox / Radio
|
| 🪜 サブメニュー | 可能(ただし深いネストは非推奨) |
MenuItems でサブメニューを構築可能 |
| 🖱 開き方 | 通常トリガー中心 | クリックに加え OpenOnHover / OpenOnContext
|
| 🔁 開閉制御 | 状態管理で制御 |
OpenMenuAsync() / CloseMenuAsync() で制御可能 |
| 📏 長い一覧 | 長文ラベルは非推奨、横幅上限の考慮が必要 |
Height 指定でスクロール可能 |
| 🧩 追加表示 | ラベル中心、必要最小限 | indicator/start/end/submenu-glyph スロット対応 |
React 側のイメージ
import { Menu, MenuList, MenuItem, MenuTrigger, Button } from "@fluentui/react-components";
export function MenuExample() {
return (
<Menu>
{/* disableButtonEnhancement: MenuTrigger が Button の挙動を二重適用しないための指定 */}
<MenuTrigger disableButtonEnhancement>
<Button>Open menu</Button>
</MenuTrigger>
<MenuList>
<MenuItem>New</MenuItem>
<MenuItem>Rename</MenuItem>
<MenuItem>Delete</MenuItem>
</MenuList>
</Menu>
);
}
Blazor 側のイメージ
<FluentButton Appearance="ButtonAppearance.Primary" Id="menuTrigger">Open menu</FluentButton>
<FluentMenu Trigger="menuTrigger" OnClick="@OnMenuClick">
<FluentMenuList>
<FluentMenuItem>New</FluentMenuItem>
<FluentMenuItem>Rename</FluentMenuItem>
<FluentMenuItem>Delete</FluentMenuItem>
</FluentMenuList>
</FluentMenu>
@code {
private void OnMenuClick(MenuItemEventArgs args)
{
Console.WriteLine($"Clicked: {args.Text}");
}
}
Menu ガイダンス 🧭
Fluent UI 2 のガイダンスでは、Menu は「即時アクション」または「コンテキスト移動」のために使います。
値入力や詳細選択の収集が目的なら、Select / Dropdown / Combobox を使うのが原則です。
また、項目順は意味のある順序に並べます。
たとえば「よく使う操作を先頭」「危険な操作を最後」に置くと、誤操作を減らせます。
Menu タイプの選び方
| タイプ | いつ使うか | 具体例 |
|---|---|---|
| Default(MenuItem) | 即時アクションを起こすとき | Delete、Rename、Share |
| Checkbox | 複数条件でフィルタリングするとき | カテゴリを複数選んで絞り込む |
| Radio | ひとつの設定を切り替えるとき | 並び替え基準・書式スタイルの選択 |
レイアウト
Menu 項目は、視線のスキャン性を優先して設計します。
- 関連項目はグルーピングする
- グループ間は divider で区切る
- 長文ラベルは避ける(Fluent UI 2 側は横幅上限 300px を前提)
React(Fluent UI v9)では MenuGroup と MenuGroupHeader でグループを表現できます。
Blazor 側では、項目が多いメニューに Height を設定してスクロール化できます。
一方で、スクロール前提の長いメニューは判断負荷が上がるため、分類や分割を先に検討する方が安全です。
動作
動作面では、次の差分を押さえると実装判断がしやすくなります。
-
開き方: Blazor は
OpenOnHoverやOpenOnContextがあり、右クリック起点のコンテキストメニューを直接作れます。 -
閉じ方:
PersistOnItemClickを使うと、項目選択後もメニューを開いたままにできます。 -
制御API:
OpenMenuAsync/CloseMenuAsyncでプログラム的に開閉でき、targetIdで表示位置の基準を切り替えられます。 - ネスト: 両者ともサブメニューは作れますが、深い多段ネストは操作と記憶の負担を上げるため非推奨です。
アクセシビリティ ♿(重要)
Menu は「見えること」より「選べること」が重要です。特に次の 4 点を優先すると崩れにくいです。
-
サブメニュー起点の明示
サブメニューを開くトリガーにはaria-labelを付け、スクリーンリーダー利用者が目的を把握できるようにします。 -
ロール設計の一致
単純アクションは MenuItem、複数選択は Checkbox、単一選択は Radio と、役割を UI 意味と一致させます。 -
フォーカス可能要素の入れ子回避
メニュー項目の中に別のクリック可能要素を入れない方が、キーボード操作と読み上げが安定します。 -
ネストの抑制
多段サブメニューはキーボード移動の負担を増やしやすいため、2 階層程度までに抑える方が安全です。
Menu のアクセシビリティで特に重要なのは、見た目の整列ではなく「役割が正しく伝わる構造」です。
ラベル、ロール、フォーカス順をセットで設計しないと、支援技術では利用しづらくなります。
コンテンツ(ラベルと補助情報)✍️(重要)
Fluent UI 2 の Menu では、文言設計がそのまま使いやすさになります。
- アクション項目は、結果が想像できる動詞ベースで短く書く
- 設定・選択項目は、名詞または短い句で表す
- sentence case で、末尾ピリオドは付けない
二次情報(secondary content)は、原則としてキーボードショートカット表示に限定します。
説明文を長く入れると、一覧性が落ちて選択ミスを招きやすくなります。
| 目的 | よい例 | 避けたい例 |
|---|---|---|
| ⚡ 即時アクション |
Delete / Rename / Share
|
This will delete selected file |
| ⚙ 設定選択 |
Sort by name / Newest first
|
Click here to sort by name |
| ⌨ 補助情報 |
Save + Ctrl+S
|
長い補足説明テキスト |
まとめ 📝
Fluent UI 2 の Menu は、選択肢を出す部品ではなく、選択の体験を設計する部品です。
Fluent UI Blazor 5 では、開き方やプログラム制御など実装面の自由度が高いため、用途に応じて使い分けやすいのが強みです。
実務では、次を先に決めると失敗しづらくなります。
- Menu を使うべきか(入力収集なら別コンポーネント)
- 項目の順序とグルーピング
- ロール(MenuItem / Checkbox / Radio)の意味づけ
- ラベルとショートカットの情報量
- サブメニューの深さと
aria-labelの付与
この 5 点を押さえるだけで、見た目だけでなく操作性とアクセシビリティまで一貫した Menu 設計になります。