Sheet
먼저 보고, 그다음 계약을 읽습니다
화면 가장자리에서 슬라이드인하는 드로어 패널 (Radix Dialog 기반).
Component intent의도와 경계 읽기
Dialog 와 같은 Radix Dialog 프리미티브 위에 .axe-sheet__* 스타일을 입힌 wrapper 지만, 중앙 카드 대신 화면 가장자리에서 밀려 들어오는 drawer 다. size 대신 side 변형(left/right/top/bottom)을 가지며 모바일 네비게이션·상세 패널·필터 서랍에 쓴다. right(28rem)·left(20rem)는 세로 전폭 사이드 패널, top/bottom 은 가로 전폭에 최대 높이 80vh 이고, bottom 은 iOS 세이프에어리어 여백을 갖는다. 포커스 트랩·Esc 닫기·바깥 클릭 닫기·ARIA modal 은 Radix 가, 슬라이드 애니메이션과 표면은 @axe/ui 가 담당한다.
예제
SSOT에 등록된 실제 API 기준 snippet입니다. standalone 배지만 독립 실행 단위이며, fragment는 주변 state·handler 문맥을 생략합니다.
import { Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetClose, Button,} from "@axe/ui";<Sheet> <SheetTrigger asChild> <Button variant="ghost">메뉴</Button> </SheetTrigger> <SheetContent side="left"> <SheetHeader> <SheetTitle>탐색</SheetTitle> <SheetDescription>워크스페이스 이동</SheetDescription> </SheetHeader> {/* … 네비 링크 … */} <SheetClose asChild> <Button variant="ghost">닫기</Button> </SheetClose> </SheetContent></Sheet>import { Sheet, SheetContent, SheetTitle, SheetFooter, Button } from "@axe/ui";<Sheet open={open} onOpenChange={setOpen}> <SheetContent side="bottom"> <SheetTitle>공유 옵션</SheetTitle> <SheetFooter> <Button variant="primary">링크 복사</Button> </SheetFooter> </SheetContent></Sheet><div class="axe-sheet__overlay"></div><div class="axe-sheet__content axe-sheet__content--left" role="dialog" aria-modal="true" aria-labelledby="sh-t"> <div class="axe-sheet__header"> <h2 id="sh-t" class="axe-sheet__title">탐색</h2> <p class="axe-sheet__description">워크스페이스 이동</p> </div> <div class="axe-sheet__footer"> <button class="axe-btn axe-btn--ghost">닫기</button> </div></div>Props
TSX 소스가 진실입니다. 주요 export 컴포넌트의 public API만 노출합니다.
| 이름 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
open | boolean | — | — | 열림 상태 (controlled). Radix Dialog.Root prop. |
onOpenChange | (open: boolean) => void | — | — | 열림/닫힘 전환 콜백 (Root). |
defaultOpen | boolean | — | false | uncontrolled 초기 열림 상태 (Root). |
modal | boolean | — | true | true 면 바깥 상호작용 차단·포커스 트랩 (Root). |
side | "left" | "right" | "top" | "bottom" | — | "right" | 어느 가장자리에서 슬라이드할지. left=20rem·right=28rem 세로 전폭, top/bottom=가로 전폭·max-height 80vh. |
withOverlay | boolean | — | true | SheetContent 가 흐린 backdrop 오버레이를 함께 렌더할지. |
...rest (SheetContent) | React.ComponentPropsWithoutRef<typeof Dialog.Content> | — | — | onEscapeKeyDown·onPointerDownOutside·forceMount 등 Radix Content props 를 전파. |
...rest (Header/Footer) | React.HTMLAttributes<HTMLDivElement> | — | — | SheetHeader·SheetFooter 는 className 병합 후 나머지 속성을 루트 <div> 로 전파. |
.axe-* 클래스 계약
React 밖에서도 같은 표면을 그리는 공개 계약입니다. stable은 minor 버전 안에서 이름을 바꾸지 않습니다.
| 클래스 | 안정성 | 용도 |
|---|---|---|
.axe-sheet__overlay | stable | 화면 전체 흐린 backdrop(fixed inset:0). |
.axe-sheet__content | stable | 가장자리 패널 표면(flex column·overflow auto). |
.axe-sheet__content--right | stable | 우측 세로 패널(min 28rem/92vw) 변형(기본). |
.axe-sheet__content--left | stable | 좌측 세로 패널(min 20rem/92vw) 변형. |
.axe-sheet__content--top | stable | 상단 가로 패널(max-height 80vh) 변형. |
.axe-sheet__content--bottom | stable | 하단 가로 패널(80vh·세이프에어리어 여백) 변형. |
.axe-sheet__header | stable | title+설명 스택(우상단 close X 자리 padding-right). |
.axe-sheet__title | stable | 제목(display 폰트·semibold). |
.axe-sheet__description | stable | 보조 설명 문단(secondary 톤). |
.axe-sheet__footer | stable | 하단 고정 우정렬 액션 라인(margin-top auto·상단 구분선). |
접근성
키보드, ARIA, 구현 노트를 함께 검토합니다.
- Esc 로 닫기(트리거로 포커스 복원)
- Tab/Shift+Tab 은 패널 안에서만 순환(포커스 트랩)
- 열릴 때 첫 포커스 대상으로 자동 이동, 닫힐 때 트리거로 복원
Radix Dialog 기반이라 콘텐츠에 role=dialog·aria-modal=true 가 붙고, SheetTitle 을 aria-labelledby, SheetDescription 을 aria-describedby 로 자동 연결한다. 닫기 X 는 자동 생성되지 않으므로 SheetClose 를 직접 배치한다.
SheetTitle 은 접근성상 필수(생략 시 Radix 개발 경고). modal 이라 바깥 스크롤이 잠긴다. side 별로 다른 슬라이드 방향 애니메이션을 쓰고 data-state=closed 로 퇴장이 갈리며, prefers-reduced-motion 시 모든 슬라이드/트랜지션이 제거된다.