Modal
Modal은 메인 콘텐츠 위에 오버레이로 표시되는 다이얼로그 컴포넌트입니다. 일반 Modal과 사용자의 명시적 확인이 필요한 AlertModal 두 가지 타입을 제공합니다.
2
Types
6
Sizes
2
Scroll Mode
Radix
Base
Playground
Preview
SMModal
Modal
Type
Size
Scroll
Content
Options
Button Style
Cancel
Confirm
const [open, setOpen] = useState(false)
<Modal open={open} onOpenChange={setOpen}>
<Modal.Trigger asChild>
<Button variant="outline">Open Modal</Button>
</Modal.Trigger>
<Modal.Content>
<Modal.Header>
<Modal.Title>Modal Title</Modal.Title>
<Modal.Description>This is a description of the modal.</Modal.Description>
</Modal.Header>
<Modal.Body>
<p>This is a modal dialog. You can place any content here, including forms, confirmations, or informational messages.</p>
</Modal.Body>
<Modal.Footer>
<Modal.Close asChild>
<Button variant="ghost" size="md" pressEffect={false}>Cancel</Button>
</Modal.Close>
<Button size="md" pressEffect={false}>Confirm</Button>
</Modal.Footer>
</Modal.Content>
</Modal>Sizes
xs간단한 확인 또는 알림max-width360px
sm (default)표준 (기본값)max-width480px
md표준 폼max-width600px
lg복잡한 폼max-width760px
xl테이블 또는 대시보드max-width960px
full전체 너비max-widthFull
패딩 (전 사이즈 공통)
Header24px · bottom 0
Body24px
Footer24px · top 0
Closeright 16px, top 16px
| Size | max-width | 타이틀 | 설명 | 용도 |
|---|---|---|---|---|
xs | 360px | 18px semibold | 14px normal | 간단한 확인 |
sm | 480px | 18px semibold | 14px normal | 확인 또는 알림 (기본값) |
md | 600px | 18px semibold | 14px normal | 표준 폼 |
lg | 760px | 18px semibold | 14px normal | 복잡한 폼 |
xl | 960px | 18px semibold | 14px normal | 테이블 또는 대시보드 |
full | Full | 18px semibold | 14px normal | 전체 너비 |
패딩 (전 사이즈 공통)
Header
p-6 pb-0
24px · bottom 0
Body
p-6
24px
Footer
p-6 pt-0
24px · top 0
Close
right-4 top-4
16px
Features
Scroll Behavior
"outside": 모달 전체가 스크롤됩니다. "inside": Body만 스크롤되고 Header와 Footer는 고정됩니다.
{/* Outside scroll — entire modal scrolls within viewport */}
<Modal.Content scrollBehavior="outside">
<Modal.Header>
<Modal.Title>Outside Scroll</Modal.Title>
<Modal.Description>The entire modal scrolls within the viewport.</Modal.Description>
</Modal.Header>
<Modal.Body>
<p>Long content here...</p>
</Modal.Body>
</Modal.Content>
{/* Inside scroll — only body scrolls; header stays fixed */}
<Modal.Content scrollBehavior="inside">
<Modal.Header>
<Modal.Title>Inside Scroll</Modal.Title>
<Modal.Description>Only the body scrolls; header and footer stay fixed.</Modal.Description>
</Modal.Header>
<Modal.Body>
<p>Long content here...</p>
</Modal.Body>
</Modal.Content>Sidebar Layout
lg / xl / full 사이즈 전용. Modal.Body를 p-0 flex로 분할해 좌측 내비게이션과 우측 콘텐츠 레이아웃을 구성할 수 있습니다.
const [active, setActive] = useState(0)
const navItems = ['General', 'Security', 'Notifications']
<Modal.Content size="lg" scrollBehavior="inside" showCloseButton={false}>
<Modal.Body className="p-0 flex min-h-0">
{/* Left sidebar */}
<div className="w-48 shrink-0 flex flex-col">
<div className="p-3">
<Modal.Close asChild>
<Button variant="ghost" size="md" pressEffect={false} className="w-9 h-9 p-0">
<X className="icon-sm" />
<span className="sr-only">Close</span>
</Button>
</Modal.Close>
</div>
<nav className="flex-1 px-2 pb-4 space-y-1">
{navItems.map((item, i) => (
<button key={item} onClick={() => setActive(i)}
className={cn("w-full text-left px-3 py-2 text-md rounded-lg transition-colors",
active === i
? "bg-background-muted text-foreground font-semibold"
: "text-text-muted hover:text-foreground hover:bg-background-muted"
)}>
{item}
</button>
))}
</nav>
</div>
<Divider orientation="vertical" color="muted" className="mx-0 h-auto" />
{/* Right content */}
<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
<div className="px-6 pt-6 pb-4 shrink-0">
<h2 className="text-lg font-semibold">{navItems[active]}</h2>
</div>
<div className="flex-1 px-6 pb-6 overflow-y-auto">
<p className="text-md text-text-muted">Content area...</p>
</div>
</div>
</Modal.Body>
</Modal.Content>Close Button
false로 설정하면 닫기 버튼이 숨겨집니다. closeIcon prop으로 아이콘을 교체할 수 있습니다.
{/* With close button (default) — X only */}
<Modal.Content showCloseButton>
<Modal.Header>...</Modal.Header>
<Modal.Body>...</Modal.Body>
</Modal.Content>
{/* Without close button — footer actions provide exit */}
<Modal.Content showCloseButton={false}>
<Modal.Header>...</Modal.Header>
<Modal.Body>...</Modal.Body>
<Modal.Footer>
<Modal.Close asChild>
<Button variant="ghost" size="md" pressEffect={false}>Cancel</Button>
</Modal.Close>
<Button size="md" pressEffect={false}>Confirm</Button>
</Modal.Footer>
</Modal.Content>API
Component Structure
ModalRadix Dialog.Trigger.ContentProps.Header.Title.Description.Body.Footer.CloseProps.Overlay.Portal
.Trigger트리거 요소.ContentProps패널 (size · scroll · close).Header헤더 영역.Title타이틀 (aria-labelledby).Description보조 텍스트 (aria-describedby).Body콘텐츠 영역.Footer액션 영역.CloseProps닫기 버튼.Overlay배경 오버레이.Portal포털AlertModalRadix AlertDialog.Trigger.ContentProps.Header.Title.Description.Body.Footer.Action.Cancel.Overlay.Portal
.Trigger트리거 요소.ContentProps패널 (size: xs / sm).Header헤더 영역.Title타이틀 (aria-labelledby).Description보조 텍스트 (aria-describedby).Body콘텐츠 영역.Footer액션 영역.Action확인 액션.Cancel취소.Overlay배경 오버레이.Portal포털Props
Modal.Content
size"sm""xs" | "sm" | "md" | "lg" | "xl" | "full"콘텐츠 최대 너비 (360/480/600/760/960/full)
scrollBehavior"outside""inside" | "outside"콘텐츠 오버플로 시 스크롤 위치
showCloseButtontrueboolean내장 닫기 버튼 표시
closeIconBuilt-in SVG iconReactNode커스텀 닫기 아이콘
| Name | Type | Default | Description |
|---|---|---|---|
size | "xs" | "sm" | "md" | "lg" | "xl" | "full" | "sm" | 콘텐츠 최대 너비 (360/480/600/760/960/full) |
scrollBehavior | "inside" | "outside" | "outside" | 콘텐츠 오버플로 시 스크롤 위치 |
showCloseButton | boolean | true | 내장 닫기 버튼 표시 |
closeIcon | ReactNode | Built-in SVG icon | 커스텀 닫기 아이콘 |
AlertModal.Content
size"sm""xs" | "sm"콘텐츠 최대 너비 (360/480)
| Name | Type | Default | Description |
|---|---|---|---|
size | "xs" | "sm" | "sm" | 콘텐츠 최대 너비 (360/480) |
Modal.Close
asChildfalseboolean자식 요소를 트리거로 사용
| Name | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | 자식 요소를 트리거로 사용 |
Anatomy
1
2
3
4
Modal title
5
Modal description
6
7
8
1
Overlay
배경 오버레이
2
Content
다이얼로그 패널
3
Header
타이틀 영역
4
Title
제목 텍스트
5
Description
보조 설명 텍스트
6
Close
닫기 버튼
7
Body
메인 콘텐츠
8
Footer
액션 버튼
Best Practices
✓
권장
- ✓파괴적 작업에는 AlertModal을 사용하세요
- ✓모달에 항상 타이틀과 설명을 설정하세요
- ✓긴 콘텐츠에는 scrollBehavior="inside"를 사용하세요
- ✓콘텐츠에 적합한 사이즈를 선택하세요
✕
지양
- ✗모달 안에 모달을 중첩하지 마세요
- ✗간단한 알림에는 모달 대신 Toast를 사용하세요
- ✗닫을 수 없는 모달을 만들지 마세요
- ✗확인이 필요한 경우에만 AlertModal을 사용하세요
Accessibility
키보드 조작
Escape 키로 모달을 닫을 수 있습니다. AlertModal은 오버레이 클릭으로 닫히지 않습니다. Tab 키는 모달 내부에 포커스를 가두어 외부 요소로 이동하지 않습니다.
ARIA / WCAG
role="dialog"Radix Dialog가 자동 설정role="alertdialog"AlertModal이 자동 설정aria-labelledbyModal.Title에 자동 연결aria-describedbyModal.Description에 자동 연결- 포커스 트랩: 모달 내부에 포커스를 제한
- 닫기 버튼에 sr-only 레이블 설정