Pular para o conteúdo
Menus & seleção

Componentes

Menus & seleção

Uma pergunta decide tudo aqui: escolher ou agir? Escolher 1 entre N pinta o escolhido de verde e mantém a lista viva; agir só destaca no hover e fecha o menu. Mesma peça técnica, duas gramáticas — trocá-las faz a pessoa achar que desfez uma escolha quando na verdade executou uma ação.
src/components/ui/dropdown-menu.tsxradix-uianimate-popup-in

Menu de ação

O kebab de uma linha, o “mais” de uma toolbar. Ninguém fica selecionado depois — o menu executa e fecha.
← kebab de linha · menu da toolbar
DropdownMenu real · item destaca no hover · destrutivo em vermelho, sempre por último e depois de um separador
o uso
<DropdownMenu>
  <DropdownMenuTrigger asChild>
    <Button variant="outline" size="icon-sm" aria-label="Ações de Marina Alvez">
      <EllipsisVertical />
    </Button>
  </DropdownMenuTrigger>
  <DropdownMenuContent align="end">
    <DropdownMenuItem onSelect={editar}><Pencil /> Editar cadastro</DropdownMenuItem>
    <DropdownMenuSeparator />
    <DropdownMenuItem variant="destructive" onSelect={excluir}>
      <Trash2 /> Excluir
    </DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>
PropTipoPadrãoO que faz
DropdownMenuContent sideOffsetnumber6A distância do painel até o gatilho. 6px é o respiro do canon; não mexer sem motivo.
DropdownMenuContent align"start" | "center" | "end""center"Menu de linha alinha por start; menu de toolbar à direita alinha por end, pra não vazar da tela.
DropdownMenuItem variant"default" | "destructive""default"O destrutivo pinta texto e ícone de vermelho e usa focus:bg-destructive/10.
DropdownMenuItem onSelect(e) => voidPreferir a onClick: cobre teclado e Enter, e o Radix fecha o menu sozinho.
PeçaReceita
Painelrounded-xl border bg-surface p-1 shadow-md ring-1 ring-foreground/5
Entradaanimate-popup-in (100ms) com origin vindo do Radix — o zoom nasce do canto por onde abriu
Saídaseca, sem animação
Itemrounded-md px-2 py-1.5 text-sm · ícone size-4 em fg-subtle
Item em focofocus:bg-ink-50 (é o hover E o teclado — o Radix usa focus)
Separadormy-1 h-px bg-border
Largura mínimamin-w-[8rem]

Armadilha já pagaFechar por código não avisa

Fechar um Radix Dialog ou menu programaticamente NÃO dispara onOpenChange — o estado da última abertura vaza pra próxima. Remonte com uma key por abertura quando o conteúdo depender do que estava aberto.

Escolher 1 entre N

Filtro, ordenação, troca de visão. O escolhido fica verde e a contagem mora à direita — a lista continua fazendo sentido depois de fechada.
← o item escolhido pinta de verde
mesma peça, outra gramática de item: check verde + bg-brand-muted no escolhido

Sim

✓ Odontologia 34

Dermatologia 27

Escolha: o item ativo é verde e permanece. A contagem alinha à direita, tabular.

Não

Excluir paciente

Ver histórico

Ação pintada de verde parece escolha feita: quem lê acha que “Excluir paciente” é o estado atual, não o que vai acontecer no clique.

Quando NÃO usar menu pra escolher

Nem toda escolha merece painel flutuante.
SituaçãoUse
Escolha dentro de um formulárioSelectField (select nativo) — no celular ele abre a roleta do sistema
Duas ou três opções, sempre visíveisGrupo de pílulas com aria-pressed, ou abas se forem LUGARES
Muitas opções com buscaMenu com um SearchInput no topo (o padrão “SearchSelect”)
Ligar/desligarCheckbox — não é escolha entre opções
dentro de formulário: select nativo, sem painel custom
lista longa: busca ANTES do menu, filtrando a lista da tela

NotaAberto: o “SearchSelect” ainda não existe

O “escolher 1 entre N com busca dentro do menu” está desenhado aqui, mas não tem instância no app nem componente em ui/: as listas longas de hoje filtram pela busca da TELA, não por um campo dentro do painel. Se a sua precisa do campo dentro, componha (DropdownMenu + SearchInput + a gramática de item acima) e avise — a segunda ocorrência é o gatilho pra virar primitivo.

Teclado e acessibilidade

O Radix entrega quase tudo; o que sobra é não estragar.
TeclaO que faz
Enter / Espaço no gatilhoAbre e põe o foco no primeiro item
↑ ↓Anda pelos itens (o item em foco usa focus:bg-ink-50)
Digitar uma letraPula pro item que começa com ela
EscFecha e devolve o foco pro gatilho
TabFecha o menu e segue o fluxo da página

O gatilho usa asChild pra virar o Button do canon — assim ele herda a pílula, o anel de foco e o crescimento no dedo, sem aninhar botão dentro de botão. Gatilho só de ícone precisa de aria-label com o objeto: “Ações de Marina Alvez”, não “Ações”.