MeDo

AIドロップダウンメニュー生成。

項目を説明するだけでMeDoがメニュー全体を生成します — トリガー、パネル、区切り線付きのグループ化セクション、サブメニュー、矢印キーと先頭文字入力によるナビゲーション、Escapeと外側クリックでの閉じる処理、そして閉じたときにトリガーへ戻るフォーカス。

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

メニューはタブストップ1つ分 — ここを取り違えている実装が多い

直感的には各項目をフォーカス可能にして、Tabで1つずつ辿らせたくなります。しかしARIAのmenuパターンは逆です:メニュー全体が1つのタブストップとなり、矢印キーがroving tabindexを項目間で動かし、Tabはメニューを閉じて外へ出ます。だからこそ12項目のアカウントメニューを飛ばすのに12回ではなく1回のキー操作で済むのです。これはメニューに明示的なフォーカス復元が必要な理由も説明します:フォーカスはプログラムでパネル内へ移されたため、トリガーへ戻さずに閉じるとキーボード利用者はドキュメントの先頭に取り残されます。トリガーとパネルを1つのコンポーネントとして生成すれば、両者が同じフォーカスの取り決めに沿って書かれるため、この往復が保たれます。

6 個のテンプレート

生成できる ドロップダウンメニュー テンプレート

どのテンプレートもスクリーンショットではなく実際のプロンプトです。任意の AI エディタに貼り付けるか、MeDo で実行して React + Tailwind のコードを受け取れます。

BasicAccount Menu

Avatar Trigger With Grouped Actions

The top-right menu every product has. Get the focus round trip right here and the rest of your menus inherit it — closing without returning focus drops keyboard users at the top of the document.

Create an account dropdown menu: a 32px avatar trigger with aria-haspopup="menu" and aria-expanded, and a 240px panel with role="menu" anchored 6px below it. Each row is role="menuitem", 32px tall, with a 16px leading icon. Use a roving tabindex so the whole menu is one tab stop — Tab closes it and leaves rather than walking the items. Enter, Space or Arrow Down opens the menu and focuses the first item; Arrow Up opens it focusing the last. Arrow Down and Up cycle with wrapping, Home and End jump to the ends, typing a letter jumps to the next item starting with it, Escape and outside pointerdown close it, and focus returns to the trigger on every close path. Group items with 1px dividers and style Sign out as a destructive red item in its own group.

MeDoで試す
Table rowRow Overflow

Three-Dot Menu on a Table Row

Repeated once per row, which makes the trigger label the real problem: twenty buttons all called "More options" tell a screen reader user nothing about which record they are about to delete.

Create a row-level overflow menu for a data table: a 32px icon-only three-dot trigger with aria-haspopup="menu", aria-expanded, and an accessible name that names its record — "More actions for invoice 1042" — so repeated triggers are distinguishable. The panel is role="menu" with Edit, Duplicate and Export, then a divider and a destructive Delete that opens a confirmation dialog and moves focus into it rather than acting immediately. Roving tabindex, Arrow Up and Down with wrapping, Home and End, single-letter type-ahead, Escape and outside pointerdown to close, focus back to the trigger. Close the menu when its ancestor scroll container scrolls, since an anchored panel detaches from a row that has moved.

MeDoで試す
NestedSubmenu

Parent Items With Nested Panels

One level of nesting, with a hover intent delay. Without the delay, dragging the pointer diagonally to the item you wanted opens and closes three submenus on the way there.

Create a dropdown menu with one level of submenus: the parent item carries aria-haspopup="menu", aria-expanded and a trailing chevron, and its child panel is a separate role="menu" anchored to the parent's right edge, flipping to the left when it would leave the viewport. Open the submenu on Arrow Right or Enter with focus on the first child, and on hover after a 150ms intent delay with a similar grace period before closing so a diagonal pointer path does not toggle three panels. Arrow Left closes the submenu and returns focus to the parent item; Escape closes the whole menu and focuses the trigger. Keep type-ahead scoped to the currently open panel, and do not nest a second level.

MeDoで試す
TogglesCheckable Items

Menu With Checkbox and Radio Rows

View options and sort order inside a menu. The roles change with the semantics — menuitemcheckbox for independent toggles, menuitemradio in a group for one-of-many — and so does whether the panel closes on select.

Create a view-options dropdown mixing checkable rows: a group of role="menuitemcheckbox" rows with aria-checked for column visibility, and below a divider a group of role="menuitemradio" rows inside role="group" with aria-labelledby a Sort by heading, where exactly one is aria-checked. Keep the panel open when a checkbox row is toggled so several can be set in one pass, and close it after a radio selection since the choice is complete. Reserve the check indicator column at a fixed width so labels do not shift when a state changes. Roving tabindex, Arrow Up and Down with wrapping, Home and End, Space toggles without closing, Enter selects, Escape closes and returns focus to the trigger.

MeDoで試す
NavigationNav Disclosure

Header Dropdown of Plain Links

Deliberately not a menu. A products dropdown holds links, so role="menu" would collapse them into one tab stop and stop cmd+click from opening a new tab.

Create a navigation dropdown for a marketing header that is a disclosure, not an ARIA menu: a trigger button with aria-expanded and aria-controls, and a panel containing a plain ul of anchor elements — no role="menu" or role="menuitem", so each link stays individually focusable and openable in a new tab with cmd+click or middle click. Tab moves through the links in order rather than a roving tabindex. Open on click and on Arrow Down, close on Escape with focus returning to the trigger, close on outside pointerdown, and close on focusout when focus leaves the panel entirely. Do not open on hover alone on touch devices. Mark the link matching the current route with aria-current="page".

MeDoで試す
FilterableSearch Menu

Long Menu With a Filter Field

Past roughly fifteen items, single-letter type-ahead stops being enough. Adding a text field changes the contract: the field keeps focus and the highlight moves by aria-activedescendant.

Create a filterable dropdown for a long list: a trigger with aria-haspopup and aria-expanded, and a panel whose first element is a search input that receives focus on open. Because a text field now holds focus, switch from a roving tabindex to the combobox approach — role="listbox" on the list, role="option" on rows, and aria-activedescendant on the input so Arrow Up and Down move the highlight while typing keeps working. Filter as the user types with a 150ms debounce, bold the matched substring, cap the list at 280px with internal scroll, and scroll the active option into view with block nearest. Show a no-matches row rather than an empty panel. Escape clears a non-empty filter first, then closes on a second press with focus returning to the trigger.

MeDoで試す

ドロップダウンメニュー をカスタマイズする方法

<select>ではなく操作のメニューだと明示する

role="menu"は、各項目がコマンドを実行することを支援技術に伝えます。項目がフォームの値を設定するのであれば、listboxまたはネイティブの<select>を指定してください。フォームフィールドにメニューを使うと、ユーザーが選択肢を期待する場所でスクリーンリーダーがコマンドを読み上げてしまいます。

サブメニューのホバーインテント遅延を指定する

サブメニューが開くまで約150msの遅延と、閉じるまでの同程度の猶予時間を指定しましょう。これがないと、親リストを斜めにポインタで横切るだけで、目的のものに到達する間に3つのサブメニューが開いたり閉じたりします。

画面端を考慮した配置を明示的に依頼する

パネルは下端付近ではトリガーの上に反転し、視界に収まるよう水平方向にずれるべきだと伝えましょう。右上のアバターにアンカーされたメニューは、固定オフセットのせいで項目の半分が画面外に描画される最も一般的なケースです。

破壊的な項目を分離する

サインアウト、削除、アクセス権の取り消しなどの操作は区切り線の下の独立したグループに置き、破壊的なカラー処理を依頼しましょう。誤クリックを防ぐのは赤い文字ではなく、日常的な項目からの物理的な距離です。

ドロップダウンメニュー コンポーネントを使うのは誰か

SaaSプロダクト

右上のアバターメニュー。アカウントのメールアドレスを静的なヘッダー行として表示し、続いてプロフィールと請求のリンク、チームのサブメニュー、テーマ切り替え、そして区切り線の下に隔離されたサインアウトを配置します。

ダッシュボードや管理ツール

三点アイコンボタンの背後にある行単位のオーバーフローメニュー。編集、複製、エクスポート、そして破壊的な削除を含み、トリガーは所属する行によってラベル付けされ、スクリーンリーダーがどのレコードを操作しているか読み上げます。

コンテンツエディター

グループ化された項目タイプ、埋め込みプロバイダーのサブメニュー、そして先頭文字入力によるジャンプを備えたブロック挿入メニュー。リストを把握している人は矢印キーで辿らずに目的の項目へ到達できます。

マーケティングサイトのナビゲーション

role="menu"ではなくプレーンなリンクのリストで構築されたナビバーの製品ドロップダウン。パネルは本来のナビゲーションとして振る舞い、各リンクは個別にフォーカス可能で新しいタブでも開けます。

サイト種別ごとの ドロップダウンメニュー パターン

同じ ドロップダウンメニュー コンポーネントを、掲載するサイトの種類に合わせて調整します。

01 / 06

SaaS Account Menu

Avatar trigger, the account email as a static non-focusable header row, then profile and billing links, a team submenu and Sign out below a divider. Keep the email row out of the roving tabindex — a label that takes an arrow keypress to skip reads as a broken menu item.

ドロップダウンメニュー のスタイルとバリアント

ページ全体のトーンに合うバリアントを選び、そのまま生成します。

01 / 06

Grouped Action Menu

Rows split into two or three sections by 1px dividers, with the destructive action alone at the bottom. The physical gap is what prevents the misclick on Sign out or Delete — red text alone does not, because the pointer is already moving before the color registers.

React・Next.js・Vue・Svelte での ドロップダウンメニュー

React、Next.js、Vue 3、SvelteKit、Astro、素の HTML — 同じ ドロップダウンメニュー を 6 通りの方法で組み込めます。

01 / 06

React Dropdown Menu

Hold the active index in state and derive tabIndex from it — that is the roving tabindex. Keep a ref to the trigger and focus it in the close handler, since the browser will not restore focus for you.

src/components/Menu.tsx

const [open, setOpen] = useState(false)
const [active, setActive] = useState(0)
const triggerRef = useRef(null)

function close() {
  setOpen(false)
  triggerRef.current?.focus()
}

<button ref={triggerRef} aria-haspopup="menu" aria-expanded={open}>
  Account
</button>

ドロップダウンメニュー をプロジェクトに追加する方法

ドロップダウンメニュー テンプレートを選んで本番に載せるまでの 4 ステップ。

01~30s

Pick a Menu Template

Answer one question first: do the items run commands or navigate? Actions get the menu templates and their roving tabindex; links get the navigation disclosure, where every anchor stays its own tab stop.

02~10s

Copy the Prompt

Take the prompt into MeDo, Lovable, Bolt, v0 or Cursor and list your real items with the dividers where you want them. Say which item is destructive and which opens a submenu, since both change the generated markup.

03~1min

Generate and Refine

You get React + Tailwind back with a live preview. Follow up in plain English — "add a filter field", "flip above the trigger near the bottom edge", "turn Sort into radio items" — instead of touching the keydown handler.

04~2min

Wire the Actions and Ship

Connect each item to its handler, then verify the focus round trip with the keyboard alone: open, arrow down, Escape, and confirm focus lands back on the trigger rather than the top of the page.

ドロップダウンメニューに関するよくある質問

AIでドロップダウンメニューを生成するには?

必要な項目を列挙し、どれがサブメニューを開くかを示し、区切り線の位置を伝えてください。MeDoはトリガー、アンカーされたパネル、グループ化された項目、キーボード処理を1つのコンポーネントとして生成するため、開く前から矢印キー、Escape、外側クリック、フォーカス復元が組み込まれています。

ドロップダウンメニューと<select>の違いは?

メニューはアクションを実行するもので、項目を選ぶと即座に何かが起こり、何も保存されません。<select>はフォームの値を保持するもので、選択肢を選ぶと後で送信される状態が変わります。両者は異なるroleと異なるキーボードの取り決めを使い、一方を他方の代わりに使うことがカスタムドロップダウンで最も多いアクセシビリティ上の欠陥です。

ナビゲーションのドロップダウンにrole="menu"を使うべき?

いいえ。role="menu"はアプリケーション的なコマンドメニュー向けであり、リンクのリストに適用すると各リンクがmenuitemとして読み上げられ、全体が1つのタブストップにまとめられてしまいます。ナビゲーションのドロップダウンは、トリガーがaria-expandedで開閉するディスクロージャー内のプレーンなアンカーのリストにすべきです。パネルがナビゲーションであるとMeDoに伝えれば、メニュー項目ではなくリンクを生成します。

ドロップダウンメニューのキーボード操作はどうあるべき?

Enter、Space、またはArrow Downでメニューが開き、最初の項目にフォーカスが当たります。Arrow UpとDownは項目間を移動し端で循環し、HomeとEndは最初と最後へジャンプ、文字を入力するとその文字で始まる次の項目へ移動します。Arrow Rightはサブメニューを開き、Arrow Leftは閉じ、EscapeまたはTabはメニュー全体を閉じてフォーカスをトリガーへ戻します。

AIはサブメニュー付きのドロップダウンメニューを生成できますか?

はい。入れ子構造を説明すれば、MeDoはaria-haspopupを持つ親項目、子パネル、ホバーインテント遅延、Arrow RightとArrow Leftのバインディングを生成します。可能な限り入れ子は1階層に留めてください。2階層目はポインタで狙いにくく、スクリーンリーダー利用者に説明するのも困難です。

ドロップダウンメニューは外側クリックで閉じる必要がありますか?

はい。加えてEscape、祖先コンテナのスクロール、トリガーの再クリックでも閉じるべきです。外側のリスナーはclickではなくpointerdownに登録し、パネルの外で始まったドラッグでも閉じるようにしてください。また常にフォーカスをトリガーへ戻し、キーボード利用者がページ先頭に取り残されないようにしてください。