6.8 KiB
Drawer
현재 HM 구현의 기준 문서. 공통 원칙은 docs/guidelines/프로젝트-지침.md(§6, §7), Token 구조 원칙은 테마-토큰-가이드.md를 따른다. 이 문서는 Drawer 고유의 API·Token·상태·결정만 기록한다. Dialog·Sheet와 값을 공유하지 않는다.
1. 상태
| 항목 | 내용 |
|---|---|
| 구현 상태 | 구현 완료 (현재 HM 규격) |
| 기준일 | 2026-10-02 |
| 기반 | shadcn/ui Drawer = vaul (vaul@1.1.2, 이번에 @hm-design/ui dependency로 추가) + Radix Dialog |
| 대표 Story | Components/Overlay/Drawer → All (이름 "Drawer") |
| 보류 | 아래 §11 |
2. 적용 근거
- 참고 근거: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Drawer가 없다. 참고할 수치가 없었다.
- 현재 HM 규격: 아래 §3~§4. Dialog의 접근성 원칙과
asChild처리 규칙은 재사용하고 수치는 현재 HM Semantic으로 새로 정했다. - 구현 방식: shadcn Drawer 구조(vaul)를 유지하고 theme CSS로 HM 스타일 연결. drag·snap·dismiss는 vaul 기본이며 재구현하지 않았다.
- vaul 추가 이유: shadcn 공식 Drawer가 vaul 기반이다(Radix에 Drawer가 없음). 프로젝트에 없던 dependency라서 추가했다(
npm install vaul --workspace @hm-design/ui). - Sheet와의 역할 구분: Drawer = 손가락/포인터로 끌어 닫는 하단 중심 임시 panel(handle 포함), Sheet = 버튼으로 닫는 edge panel.
3. 기본 규격 (현재 HM 규격)
| 항목 | 값 |
|---|---|
| Surface / 전경 | --hm-sem-bg-surface / --hm-sem-fg-primary |
| Border | 1px --hm-sem-stroke-input-default, 화면을 향한 한 변 |
| Radius | 8px, 화면 안쪽 두 모서리(bottom이면 위 두 모서리) |
| Shadow | 0 0 12px rgb(0 0 0 / .2) |
| bottom / top | 폭 100%, 높이 내용 기준, 최대 85dvh |
| left / right | 폭 min(400px, 100vw − 48px), 높이 100% |
| Handle(bottom만) | 48 × 4px 알약, --hm-sem-stroke-default, 위 12px |
| Padding | 24px |
| Header | padding 24 24 16, gap 8px |
| Title | 18 / 28 / 700 |
| Description | 14 / 20 / 400, --hm-sem-fg-secondary |
| Body | 14 / 20 / 400, 좌우 padding 24px, 유일한 scroll 영역 |
| Footer | padding 16 24 24, gap 8px, 세로 쌓임(주 action이 위, shadcn 기본) |
| Close(X) | 없음(handle drag, Overlay 클릭, Escape, DrawerClose) |
| Overlay | shadcn 기본(bg-black/10 + blur) |
4. 상태 규격
| 상태 | 값 |
|---|---|
| Closed / Open | vaul data-state, 열림 시 modal, 배경 aria-hidden, scroll lock |
| Drag | handle 또는 panel을 아래로 끌어 닫음(vaul 기본). 내부 scroll 영역이 있으면 vaul이 scroll과 drag를 구분 |
| Focus | 열릴 때 첫 tabbable로 이동, Tab 순환, 닫힐 때 trigger로 복귀 |
| Long Content | Header·Footer 고정, Body만 scroll |
5. API
vaul Drawer.Root props 그대로(direction, snapPoints, dismissible, shouldScaleBackground, modal, open/onOpenChange …). export: Drawer, DrawerTrigger, DrawerPortal, DrawerOverlay, DrawerClose, DrawerContent, DrawerHeader, DrawerBody, DrawerFooter, DrawerTitle, DrawerDescription.
Drawer:autoFocus기본값을 true로 둔다(vaul 기본은 false). vaul 기본값은 Radix의 초기 focus를 취소해, 열린 뒤에도 focus가 aria-hidden 처리된 trigger에 남고 Tab이 Drawer 밖으로 나간다(실측).autoFocus={false}로 vaul 동작을 되돌릴 수 있다.DrawerContent: 방향은 root의direction(기본 bottom),data-vaul-drawer-direction으로 출력.DrawerBody(신규): scroll owner.DrawerClose:asChild일 때 자식의data-slot을 유지(DialogClose와 같은 규칙).- 새 prop은 만들지 않았다.
6. Token
Component Mapping 위치: packages/ui/src/theme/drawer.css (--hm-drawer-*).
background, text, border-width, border-color, radius, shadow, max-height, width, edge-gap, padding, header-gap, footer-gap, handle-width|height|color|margin-top, title-*, description-*, body-*. Semantic과 Theme palette만 참조하며 --hm-sheet-*·--hm-dialog-*를 참조하지 않는다. 신규 Primitive 없음.
7. shadcn / vaul / Radix에서 유지한 항목
vaul의 drag·snap·dismiss·body 처리와 data-vaul-* 속성, Radix의 Portal·focus trap·Escape·ARIA·focus 복귀, 방향별 position class, data-slot, cn(..., className), handle 요소(bottom 방향에서만 표시), Overlay.
8. HM에서 변경한 항목
- shadcn
bg-background, 방향별rounded-*/border-*/max-h-[80vh], handleh-2 w-[100px] bg-muted, Header/Footerp-4제거 → theme CSS. Drawer의autoFocus기본 true(위 §5).DrawerBody추가,DrawerClose asChilddata-slot 처리.- Handle을
data-slot="drawer-handle"요소로 구분해 token으로 스타일.
9. Storybook
- 대표 Story
All("Drawer"): 트리거 3개(기본, 폼, 긴 내용)가 각각 실제 Drawer를 연다. 핸들을 끌어 닫을 수 있다. - verification:
Open,Form,Long Content(처음부터 열림).
10. 사용자 확정 결정
- 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 개별 수치에 대한 별도 결정은 없다.
11. 미확정 / 보류
- 수치(85dvh, 18px Title, handle 크기, 그림자)는 현재 HM 규격이며 디자인 확정 시 재검토.
- Overlay, drag/transition Motion은 vaul·shadcn 기본 유지.
shouldScaleBackground는 쓰지 않았다. - snap points 사용 예는 만들지 않았다(API는 vaul 그대로 사용 가능).
- left/right/top 방향의 시각은 CSS에 두었으나 Story로는 검증하지 않았다(bottom만 사용 예).
- Drawer와 Sheet의 사용 경계 가이드는 후속.
12. 관련 파일
packages/ui/src/components/ui/drawer.tsx
packages/ui/src/theme/drawer.css
packages/ui/package.json (dependency: vaul)
packages/ui/src/theme.css (import)
packages/ui/src/index.ts (export)
src/components/drawer.stories.tsx
13. 검증 상태
npm run typecheck,npm run build: 통과(2026-10-02).- 실제 interaction(Browser pane): 클릭 열기, focus가 "확인"으로 이동(
autoFocus적용 전에는 trigger에 남음), Tab 3회 후에도 Drawer 안(focus trap), Escape 닫힘과 trigger 복귀, handle을 아래로 drag해 닫힘과 focus 복귀, 바깥(Overlay) 클릭 닫힘. - 390×844 긴 내용: Drawer 높이 717px(85dvh), Body만 scroll, Footer가 viewport 하단, 가로 overflow 없음, Footer 버튼 세로 쌓임.
- computed style: radius
8px 8px 0 0, border-toprgb(222,219,210), handle 48×4rgb(154,146,132). - 미확인: 실제 터치 drag(마우스 drag만 확인), snap points, scroll 영역 안에서 drag와 scroll 구분, 닫기 animation 시각, left/right/top 방향 시각.