---
fonte: 01-canonicos/seed-componentes.md
versao_da_fonte: v1.43
secao: 35
titulo: "Menu / Dropdown — `estável` (v0.38) · MN1–MN8 · Bloco 5/5B · suite `suite-navegacao.mjs` 58/58 ✓ · `contraste-navegacao.py` ✅ · regressão 349 verdes · gate visual aprovado 2026-08-06"
sequencia: 37 de 98
bytes_do_corpo: 6051
md5_do_corpo: 86a1b7750bc6003e68744e471ae45e43
gerado_por: 06-validacao/geradores/gen-camada-ia.py
nota: fatia GERADA — o corpo abaixo é byte a byte o trecho do canônico; edite o canônico, nunca esta fatia. Canônico inteiro em https://ds.seed.eng.br/01-canonicos/seed-componentes.md
---
## 35. Menu / Dropdown — `estável` (v0.38) · MN1–MN8 · Bloco 5/5B · suite `suite-navegacao.mjs` 58/58 ✓ · `contraste-navegacao.py` ✅ · regressão 349 verdes · gate visual aprovado 2026-08-06

> **Base (3 rodadas encadeadas, 2026-08-06, ~24 fontes):** R1 — **NN/g Menu-Design Checklist** (hover-open frustra; Fitts; 1 nível; **"gray out em vez de remover + explique o porquê"**), **NN/g Dropdowns** (comando ≠ seleção de atributo; Mac HIG proíbe dropdown-box p/ comandos), NN/g listbox×dropdown, NN/g expandable menus, **Carbon Menu** (menu = ações; form/filtro = dropdown; destrutivo separado no FIM; atalhos exibidos), Carbon Overflow Menu usage+a11y, **Polaris ActionList** (dentro de popover; **verbo+substantivo**; destructive; seções; help text). R2 — Radix DropdownMenu (menu button APG 1:1: roving, typeahead, checkable, Esc — shadcn herda), **Wootonn** (setas do menu × cursor do input = conflito estrutural de ARIA; saída é dialog/popover), Ariakit. R3 — **corpus Adrian Roselli 2017–2026** ("Don't Use ARIA Menu Roles for Site Nav" / "Be Careful Using 'Menu'" / "Link + Popover Navigation") **+ APG Disclosure Navigation Menu (Higley)**: navegação usa link+disclosure, nunca roles de menu — posição hoje do próprio APG; Terrill Thompson (1ª regra do ARIA); Piccalilli; **Fiori Menu Button + Action Sheet** (menu button aposentou o action sheet; 1 ação = botão; nunca ícone-só; importância top-down). Consolidado aprovado pelo Rafael em 2026-08-06.

### 35.1 Decisões MN1–MN8

| # | Decisão | Porquê | Descartado |
|---|---|---|---|
| MN1 | **Fronteira semântica de 4 saídas:** itens executam AÇÃO = **menu §35** (`role="menu"`, APG menu button) · itens NAVEGAM = **disclosure de links** (botão `aria-expanded` + popover §24 com `<a>`, zero roles de menu — o shell F6 consome isto; Tab navega E ↓↑ opcionais do APG sem wrap) · itens selecionam VALOR = select/combobox (B2) · conteúdo rico/inputs = popover §24. **PROIBIDO input dentro de `role="menu"`** | Roselli→APG disclosure nav; NN/g/Mac HIG; Carbon; Wootonn (conflito de setas) | role=menu p/ navegação (anti-padrão testado); componente "faz-tudo" |
| MN2 | **Trigger:** botão §1 + `aria-haspopup="menu"` + `aria-expanded` + caret; caso nomeado **overflow "⋯"** (icon-button nomeado). **1 ação = botão simples.** Split button fora da v1 (fronteira) | Carbon/Fiori; sem demanda p/ split | Split na v1; abrir por hover (NN/g) |
| MN3 | **Teclado APG integral:** Enter/Espaço/↓ abrem focando o 1º; ↑ abre no último; setas com wrap; Home/End; **typeahead**; Esc fecha→trigger; **ativar AÇÃO executa, fecha e devolve o foco ao trigger** | APG; Radix 1:1 | Subconjunto sem typeahead |
| MN4 | **Itens:** `menuitem` verbo+substantivo; ícone sempre com rótulo; atalho no trailing (JetBrains Mono + `aria-keyshortcuts`); **`menuitemcheckbox`/`menuitemradio`** p/ opções de exibição — **alternáveis PERMANECEM abertos ao alternar** (opção ≠ ação; ajustar 2+ opções sem reabrir) | Polaris; Fiori; Carbon; Radix (padrão preventDefault em checkable é prática da stack) | Checkable fora da v1; fechar ao alternar (pune o segundo ajuste — Fitts) |
| MN5 | **Organização:** grupos `role="group"` nomeados; separadores (divider §27); **destrutivo SEMPRE no fim, após separador, vermelho interface**; importância top-down; **máx ~7 itens, 1 nível — SUBMENU PROIBIDO na v1** (fronteira: demanda real → pesquisa própria) | Carbon/Polaris; NN/g (2 níveis frustram; Fitts); vermelho interface-only | Submenu; destrutivo no topo |
| MN6 | **Item indisponível — EXCEÇÃO DOCUMENTADA da linha "sem disabled":** visível, `aria-disabled="true"`, **navegável pelas setas** (descobrível por AT), texto `text-muted` deliberadamente legível (4.74 — não usamos a isenção da WCAG), **motivo em help text**. Critério da exceção: menu é INVENTÁRIO de comandos — remover muda o mapa mental; a aba (TB8) não é inventário | NN/g literal; APG aria-disabled focável | Remover o item (NN/g); `disabled` nativo fora das setas (invisível p/ AT — divergência declarada com Carbon) |
| MN7 | **Superfície:** popover §24 (z 75, light-dismiss, flip); largura mín. 200px; itens ≥44px; **<640px tray por herança Y4, sem regra nova**; ativou ação → fecha; feedback (toast) é do consumidor | Carbon; §24 resolveu; Fiori (superfície plena no phone) | Action sheet como componente (Fiori o aposentou) |
| MN8 | **Sem live region própria** — estado vive nos roles; anúncio do resultado pertence ao consumidor | Regra do um no canal sonoro (linha CN10/CN12) | Live region de abre/fecha (dupla fala) |

### 35.2 Anatomia, tokens e fronteiras

Trigger: botão §1 (qualquer variante) + caret rotativo. Painel: overlay branco/#273137, radius 10, sombra overlay, padding 6. Item: 44px, radius 6, ícone 18px decorativo, rótulo 600, atalho mono ci-600/ci-300, check `action-primary` (alternáveis). Destrutivo: `--seed-danger-item` **ve-700 light / ve-200 dark**. **Pares novos medidos (`contraste-navegacao.py` ✅): 7.18 · 6.61 · 8.67 · 4.74 · 6.94.** Fora do DS: split button (fronteira MN2) · submenu (MN5) · context menu por clique-direito (sem demanda; se surgir, herda esta spec com trigger próprio) · navegação do shell (F6, consome a saída disclosure do MN1).

### 35.3 Os 7 testes

1. Pares novos medidos ✅. 2. Destrutivo = posição+separador+ícone+cor (nunca só cor); indisponível = aria-disabled+motivo (nunca só cinza). 3. Grayscale: posição/separador/check carregam. 4. Menu de ações × disclosure de links a 1s (roles×links). 5. Regra do um: um nível, uma live-region nenhuma, um canal de feedback. 6. Estado alternável visível (check). 7. 360px: tray herdado, itens 44px, overflow "⋯" 44px. *(Suite 5B em `suite-navegacao.mjs` — total 57/57 ✓ após erratas do gate visual: Tab/Esc/focusout no menu + setas opcionais APG no disclosure; tray renderizado, flip e sombra = gate visual.)*

---

