AI下拉菜单生成器。

描述你的菜单项,MeDo即可生成完整菜单 — 触发器、面板、带分隔线的分组区块、子菜单、方向键与首字母跳转、Escape与点击外部关闭,以及关闭时焦点回到触发器。

下拉菜单
下拉菜单

菜单只占一个Tab停靠点,而这正是大家最常做错的地方

直觉会让人把每个菜单项都设为可聚焦,于是Tab会逐项走过去。ARIA menu模式恰恰相反:整个菜单只是一个Tab停靠点,方向键在菜单项之间移动roving tabindex,而Tab会关闭菜单并离开。正因如此,跳过一个12项的账户菜单只需按一次键,而不是十二次。这也解释了菜单为何需要显式的焦点还原:焦点是通过程序移入面板的,如果关闭时不把它送回触发器,键盘用户就会被丢在文档顶部。把触发器和面板作为一个组件生成,能保住这次往返,因为两半是按同一套焦点约定编写的。

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延迟,关闭前有类似的宽限时间。没有它,斜向拖动指针经过父级列表时,在到达目标之前会连续打开又关闭三个子菜单。

明确要求避免超出视口的定位

说明面板在靠近底部边缘时应翻转到触发器上方,并水平偏移以保持可见。菜单锚定在右上角头像上,是固定偏移导致一半菜单项渲染到屏幕外的最常见情形。

把破坏性菜单项单独隔开

把退出登录、删除、撤销访问权限等操作放在分隔线下方的独立分组中,并要求破坏性配色处理。真正防止误点的是与常规项之间的物理距离,而不是红色文字。

谁在使用 下拉菜单 组件

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 —— 同一个 下拉菜单,六种接入方式。

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>

如何把 下拉菜单 加入项目

四个步骤,从挑选 下拉菜单 模板到上线生产环境。

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会把触发器、锚定面板、分组项和键盘处理作为一个组件生成,所以在你第一次打开它之前,方向键、Escape、点击外部和焦点还原就已经接好了。

下拉菜单和<select>有什么区别?

菜单用于触发操作:选中一项会立即执行某件事,且不会存储任何内容。<select>持有表单值:选中一个选项会改变稍后随表单提交的状态。两者使用不同的role和不同的键盘约定,把一个当作另一个使用是自定义下拉组件中最常见的无障碍缺陷。

导航下拉菜单应该使用role="menu"吗?

不应该。role="menu"面向应用式命令菜单,把它套在链接列表上会让每个链接被播报为menuitem,同时把它们压缩成一个Tab停靠点。导航下拉菜单应该是一组普通锚点链接,放在由触发器通过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的按键绑定。尽可能只保留一层嵌套,因为第二层用指针难以命中,也难以向屏幕阅读器用户描述。

下拉菜单需要在点击外部时关闭吗?

需要,而且还应在按Escape、祖先容器滚动以及再次点击触发器时关闭。把外部监听绑定在pointerdown而不是click上,这样从面板外开始的拖动也会关闭它;并且始终把焦点还给触发器,避免键盘用户被丢在页面顶部。