DropdownMenu
트리거 버튼 + 액션 메뉴(WAI-ARIA menu button 패턴) — 열리면 첫 항목으로 포커스 이동, 화살표 순환 탐색, Escape는 닫고 트리거로 복귀. 비제어(defaultOpen).
마지막 업데이트 2026-06-11
한눈에#
트리거 버튼 + 실행 가능한 액션 메뉴(WAI-ARIA menu button)입니다 — 열리면 첫 항목으로 포커스, 화살표 순환 탐색, 선택 시 실행 후 닫힙니다.
- menu button 패턴
- 첫 항목 자동 포커스
- 화살표 순환·섹션 헤더
- 선택 즉시 실행·닫힘
트리거를 눌러 화살표 탐색·선택 실행을 직접 확인하세요:
사용 시점#
트리거로 실행 가능한 액션 목록을 펼치면 DropdownMenu — 폼 값 선택·임의 패널·명령 팔레트는 다른 컴포넌트입니다.
이름 바꾸기·복제·삭제처럼 트리거로 펼쳐 고르면 즉시 실행되는 액션 메뉴(menu button)
플레이그라운드#
컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.
import { DropdownMenu } from '@wds/ui-web';
<DropdownMenu
trigger='메뉴'
items={[
{ value: 'rename', label: '이름 변경' },
{ value: 'duplicate', label: '복제' },
{ value: 'delete', label: '삭제', danger: true },
]}
/>변형#
- danger — 파괴적 액션 변형,
color.error-text잉크 - disabled — 네이티브
disabled— 포커스 탐색이 건너뜀
- group — 같은
group을 공유하는 연속 항목 앞에 비대화형 섹션 헤더(role="presentation")를 렌더 — 헤더는menuitem이 아니라 키보드 탐색에서 건너뜁니다 - leadingIcon — 라벨 앞 장식 아이콘(
aria-hidden) - trailingText — 라벨 우측 보조 텍스트(키보드 숏컷 등), muted·우측 정렬
크기#
단일 크기입니다 — 트리거 높이 control.height-md(44px), 메뉴 최소 폭 11rem.
상태#
- 열림 — 트리거
aria-expanded="true"+ 메뉴 렌더, 첫 활성 항목 포커스 - 항목 hover/focus —
color.surface-hover배경 (트리거 열림 상태만surface-selected) - Focus — 트리거
:focus-visible에 2pxcolor.focus-ring아웃라인
선택하면 onSelect?.(value) 호출 후 메뉴가 닫히고 트리거로 포커스가
복귀합니다.
Props#
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
trigger | ReactNode | — | 트리거 콘텐츠 — 내부에서 button으로 감쌈(자체 button 전달 금지) |
items | readonly DropdownMenuItem[] | — | { value, label, disabled?, danger?, group?, leadingIcon?, trailingText? } 배열 — 데이터 prop 방식 |
onSelect | (value: string) => void | — | 항목 선택 콜백 — 호출 후 메뉴는 닫힘 |
open | boolean | — | 제어 열림 상태 — 제공하면 제어 모드(내부 상태 무시, 모든 전환이 onOpenChange로 통지) |
onOpenChange | (open: boolean) => void | — | 열림/닫힘 전환 통지 — 제어/비제어 모두에서 호출 |
defaultOpen | boolean | false | 초기 열림 여부 — 이후 상태는 내부 소유(비제어, open 미제공 시) |
className | string | — | 루트(div)에 적용 |
DropdownMenuItem#
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
value | string | — | 선택 시 onSelect로 전달되는 식별자 |
label | ReactNode | — | 항목 라벨 — menuitem 접근명 |
disabled | boolean | — | 네이티브 disabled — 선택·포커스 탐색에서 제외 |
danger | boolean | — | 파괴적 액션 변형 — error 잉크 |
group | string | — | 섹션 그룹 — 같은 group을 공유하는 연속 항목 앞에 비대화형 헤더(role="presentation") 렌더. 헤더는 menuitem이 아니며 키보드 탐색에서 건너뜀 |
leadingIcon | ReactNode | — | 라벨 앞 장식 아이콘(aria-hidden) — 접근명에 포함되지 않음 |
trailingText | string | — | 라벨 우측 보조 텍스트(키보드 숏컷 등) — muted·우측 정렬·aria-hidden |
접근성#
트리거가 aria-haspopup="menu" + aria-expanded + aria-controls, 팝업이
role="menu"(트리거 aria-labelledby 연결) + role="menuitem" 항목입니다.
group 섹션 헤더는 role="presentation"이라 menuitem이 아니며, 화살표·Home/End
탐색에서 자연히 건너뜁니다. leadingIcon·trailingText는 aria-hidden이라 항목
접근명은 label만 사용됩니다.
| 키 | 동작 |
|---|---|
↓ (트리거) | 열고 첫 활성 항목으로 포커스 |
↑ (트리거) | 열고 마지막 활성 항목으로 포커스 |
↓ / ↑ (메뉴) | 항목 포커스 순환 이동 — 비활성 건너뜀 |
Home / End | 첫/마지막 활성 항목으로 점프 |
Enter / Space | 항목 선택 + 닫기 + 트리거 포커스 복귀 |
Escape | 선택 없이 닫기 + 트리거 포커스 복귀 |
Tab | 메뉴는 탭 순서에 끼지 않음 — 닫고 기본 이동 |
외부 클릭(mousedown)으로도 닫히며, 항목은 tabIndex={-1}로 화살표
탐색만 허용합니다(menu button 패턴).
토큰#
component 토큰 없이 semantic을 직접 소비합니다(신설 기준 §4 미충족).
| 속성 | 토큰 |
|---|---|
| 트리거 | color.surface + color.border (hover surface-hover/border-strong · pressed surface-pressed) |
| 트리거 높이/라운드 | control.height-md · radius.control |
| 메뉴 | color.surface-raised · radius.md · shadow.lg · z.dropdown |
| 항목 | font.size-body-2 · hover/focus color.surface-hover · pressed color.surface-pressed |
| danger | color.error-text (hover 배경 color.error 톤) |
| 비활성 | color.disabled-fg |
| 모션 | duration.fast + ease.emphasized-decelerate(등장)/ease.standard(전환) |