실전 가이드
모든 대화상자에 이름 지정
화면에 보이는 제목을 ariaLabelledBy로 연결하고 설명 문구를 ariaDescribedBy로 연결하는 방식을 우선하세요. 보이는 제목이 없다면 ariaLabel을 지정합니다. 즉각적인 주의가 필요한 긴급한 결정에만 role: 'alertdialog'를 사용하세요.
await display({
role: 'alertdialog',
ariaLabelledBy: 'discard-title',
ariaDescribedBy: 'discard-description',
render: (close) => (
<section>
<h2 id="discard-title">변경 사항을 버릴까요?</h2>
<p id="discard-description">저장하지 않은 편집 내용이 사라집니다.</p>
<button onClick={() => close(false)}>계속 편집</button>
<button onClick={() => close(true)}>버리기</button>
</section>
),
});인라인 어댑터는 최상단 모달 안에 포커스를 가두고 첫 번째 컨트롤이나 패널에 포커스를 둡니다. 또한 이전 포커스를 복원하고 본문 스크롤을 잠급니다. 최상위 레이어 모드에서는 네이티브 <dialog>가 포커스 가두기를 처리합니다. 애플리케이션에서 이 동작을 의도적으로 대체할 때만 autoFocus나 restoreFocus를 false로 설정하세요.
두 전송 방식 모두 DOM 순서에서 첫 번째 유효한 컨트롤을 초기 포커스 대상으로 선택합니다. 인라인 Tab 순환도 같은 후보를 사용합니다. 음수 tabIndex, 숨겨진 input, 비활성 컨트롤(비활성 fieldset 내부 포함), 그리고 자신이나 조상의 hidden, inert, aria-hidden="true", display: none, visibility: hidden / collapse 또는 조상의 content-visibility: hidden으로 숨겨진 컨트롤은 제외됩니다. 자식이 visibility: visible을 지정하면 상속된 visibility: hidden을 덮어쓸 수 있으며, 진입 애니메이션 중의 opacity는 후보를 제외하지 않습니다. 후보가 없으면 패널이나 네이티브 대화상자에 포커스를 둡니다.
최상위 레이어의 키보드 포커스 가두기는 계속 네이티브 showModal()이 담당하며 조상의 inert에서는 벗어납니다. 대화상자 자체나 내부 콘텐츠의 inert는 적용됩니다. 후보 필터링이 aria-hidden에 브라우저 키보드 포커스 차단 기능을 추가하지는 않습니다. 키보드 사용자도 콘텐츠에 접근할 수 없어야 한다면 hidden이나 inert를 사용하세요.
전송 방식 선택
패키지가 일관되게 위치, 백드롭, 모션을 제어하도록 하려면 기본값인 transport: 'inline'을 사용하세요. 잘림이나 스태킹 컨텍스트를 벗어나야 한다면 transport: 'top-layer'를 사용합니다.
await display({
transport: 'top-layer',
ariaLabel: '계정 설정',
render: (close) => <SettingsPanel onDone={() => close()} />,
});최상위 레이어 전송 방식은 네이티브 <dialog>와 showModal()을 지원하는 브라우저가 필요합니다. 폴리필은 포함하지 않습니다.
패널과 백드롭 스타일 지정
className과 style은 패널 또는 <dialog>에 적용됩니다. backdropClassName과 backdropStyle은 인라인 백드롭에 적용됩니다. 최상위 레이어 모드에서는 생성된 ::backdrop 규칙을 통해 백드롭 스타일을 적용합니다. 패키지 모션은 200ms의 페이드 및 크기 조절 애니메이션을 사용하며, prefers-reduced-motion이 설정되어 있으면 빠르게 제거합니다.
범위별 명령과 전역 명령을 구분하여 사용
프로바이더 안에서는 어느 프로바이더가 관리하는지 명확한 useModal()을 우선 사용하세요. 모듈 수준의 modal 퍼사드는 React 컴포넌트 밖에서 유용하지만, 기본 <ModalProvider>가 하나 마운트되어 있어야 합니다.
<ModalProvider><AppRoutes /></ModalProvider>;
const id = modal.open({ ariaLabel: '작업 중', render: () => <Progress /> });
modal.close(id);독립된 프로바이더가 여러 개라면 각 프로바이더에 서로 다른 createOverlayStore()를 전달하고 해당 트리 안에서 훅을 사용하세요. 전역 퍼사드는 항상 globalModalStore를 대상으로 합니다.
렌더 콜백을 순수하게 유지
render 안에서 훅을 호출하거나 부수 효과를 실행하지 마세요. 대신 훅을 사용하는 컴포넌트를 반환하세요: render: (close) => <EditorDialog onClose={close} />.