Modal
오버레이 다이얼로그 — 포커스 트랩 · Escape/배경 닫기 · 포커스 복원 · 스크롤 잠금을 내장한 제어 컴포넌트입니다.
마지막 업데이트 2026-06-11
한눈에#
진행 중인 작업을 멈추고 한 가지 결정을 받는 오버레이 다이얼로그입니다 — 포커스 트랩·Escape/배경 닫기·포커스 복원·스크롤 잠금을 내장한 제어 컴포넌트입니다.
- role=dialog · aria-modal
- 포커스 트랩·복원
- Escape·배경 닫기
- 배경 스크롤 잠금
트리거를 눌러 포커스 트랩·Escape·배경 닫기를 직접 확인하세요:
사용 시점#
흐름을 멈추고 한 가지 결정을 받는 차단형 다이얼로그면 Modal — 측면·하단·가벼운 정보는 다른 컴포넌트입니다.
권장 — 이렇게 쓰세요
지양 — 이러지 마세요
쓴다 — 삭제 확인·약관 동의처럼 화면 중앙에서 주의를 가두고 한 가지 결정·짧은 폼을 받을 때
대신 Drawer — 측면에서 슬라이드되는 보조 작업 패널(필터·설정 편집)
대신 BottomSheet — 모바일·터치 환경의 하단 표면
대신 Popover — 행동을 강제하지 않는 가벼운 정보·메뉴
해부#
오버레이는 포털로 떠서 인라인 렌더가 안 되므로, 열린 패널을 재현해 토큰 실측을 핀으로 얹습니다(실제 동작은 위 트리거).
- radius
- 20px
radius.xl - shadow
- shadow.xl
- body padding
- 24px
space-6 - header/footer
- 16 · 24px
space-4 · space-6
플레이그라운드#
컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.
Modal를 직접 조작해 보세요
속성·테마·토큰을 바꾸고 React·Flutter 코드를 확인하는 풀스크린 빌더로 엽니다.
플레이그라운드는 넓은 작업 영역이 필요해 웹·태블릿에서 편집할 수 있어요.
변형#
배경 클릭 닫기는 기본 동작이며, 파괴적 확인 등 명시적 선택이 필요한 흐름은
closeOnBackdrop={false}로 끕니다(Escape와 닫기 버튼은 항상 동작).
크기#
상태#
제어 컴포넌트입니다 — 열림/닫힘은 소비자의 open prop이 결정하고,
닫힘 요청(Escape·배경·닫기 버튼)은 전부 onClose 콜백으로 전달됩니다.
내부에 열림 상태를 갖지 않으므로 URL·폼 흐름과 동기화하기 쉽습니다.
Props#
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
open | boolean | — | 표시 여부 — 제어 prop |
onClose | () => void | — | Escape·배경·닫기 버튼의 닫힘 요청 콜백 |
title | ReactNode | — | 다이얼로그 제목 — aria-labelledby 대상이라 필수 |
footer | ReactNode | — | 하단 행동 영역 (우측 정렬) |
size | 'sm' | 'md' | 'lg' | md | 최대 폭 400/560/720px |
closeOnBackdrop | boolean | true | 배경(오버레이) 클릭으로 닫기 허용 (기본 true) |
closeLabel | string | 닫기 | 닫기 버튼 aria-label |
className | string | — | 추가 CSS 클래스 — 루트 요소에 병합됩니다. |
ref | Ref<HTMLDivElement> | — | 패널(role=dialog)로 병합 전달되는 ref — 닫힘(미마운트) 동안 null (React 19 ref-as-prop). 내부 panelRef(포커스 트랩·진입 포커스용)와 useMergedRefs로 합성한다. |
접근성#
| 계약 | 구현 |
|---|---|
| 역할 | role="dialog" + aria-modal="true" |
| 이름 | title이 aria-labelledby로 연결 |
| 포커스 진입 | 열리면 패널로 이동 |
| 포커스 트랩 | Tab/Shift+Tab이 내부에서 순환 |
| 포커스 복원 | 닫히면 이전 포커스 요소로 복귀 |
| 키보드 닫기 | Escape — 포커스 위치 무관(document 캡처) |
| 스크롤 | 열림 동안 배경 body 스크롤 잠금 |
| 모션 | open=false 전환 후 closing 상태로 퇴장 모션을 완료한 뒤 unmount. prefers-reduced-motion 존중 |
토큰#
component 토큰 없이 semantic을 직접 소비합니다(신설 기준 §4 미충족 — 폭 3단은 P5 패리티 시 재평가).
| 속성 | 토큰 |
|---|---|
| 오버레이 | color.overlay · z.modal |
| 패널 배경 | color.surface-raised |
| 라운드/그림자 | radius.xl · shadow.xl |
| 닫기 버튼 | hover color.surface-hover · pressed color.surface-pressed |
| 등장/퇴장 모션 | panel spring.spatial.default · scrim/exit spring.effect.default · close ease.emphasized-accelerate |