# Popover
현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Popover 고유의 API·Token·상태·결정만 기록한다.
## 1. 상태
| 항목 | 내용 |
|---|---|
| 구현 상태 | 구현 완료 (현재 HM 규격) |
| 기준일 | 2026-10-02 |
| 기반 | shadcn/ui Popover (Radix `Popover`) |
| 대표 Story | `Components/Overlay/Popover` → `All` (이름 "Popover") |
| 보류 | 아래 §11 |
## 2. 적용 근거
- **참고 근거**: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Popover 규격이 없다(인벤토리 "상세 디자인·API 보류"). 같은 floating surface인 Dialog의 윤곽 의도만 참고했다.
- **현재 HM 규격**: 아래 §3~§4. 현재 HM Semantic 체계로 새로 정했다.
- **구현 방식**: shadcn / Radix Popover 구조를 유지하고 theme CSS로 HM 스타일을 연결. Popover는 일반 floating panel이며 modal이 아니다(overlay·focus trap 없음).
- **보류**: §11.
## 3. 기본 규격 (현재 HM 규격)
| 항목 | 값 |
|---|---|
| Surface | `--hm-sem-bg-surface` |
| 전경 | `--hm-sem-fg-primary` |
| Border | 1px `--hm-sem-floating-border-color` |
| Radius | `--hm-radius-container` (4px) |
| Shadow | `0 0 12px rgb(0 0 0 / .2)` |
| Padding | 16px |
| Width | 288px (viewport − 16px 이하로 제한) |
| Typography | 14 / 20 / 400 |
| Position | `sideOffset` 4, `align` center (기본) |
## 4. 상태 규격
| 상태 | 값 |
|---|---|
| Open / Closed | Radix `data-state`, shadcn open/close animation |
| Focus | 열릴 때 Radix가 content 안 첫 focus 가능 요소로 이동, 닫힐 때 trigger로 복귀(Escape/Close) |
| 바깥 클릭 | 닫힘. focus는 클릭한 곳에 둔다 |
## 5. API
Radix Popover props 그대로. export: `Popover`, `PopoverTrigger`, `PopoverContent`, `PopoverAnchor`, `PopoverClose`.
- `PopoverContent`: `align` 기본 `center`, `sideOffset` 기본 4.
- `PopoverClose`: `asChild`일 때 자식의 `data-slot`을 유지(DialogClose와 같은 규칙, Button HM 스타일 보존).
- 출력 속성: `data-slot="popover-*"`, Radix `data-state`·`data-side`·`data-align`.
### 접근성 이름
`PopoverContent`는 `role="dialog"`로 출력된다. 소비자는 패널의 목적을 나타내는 접근성 이름을 `aria-label` 또는 `aria-labelledby`로 지정한다. Trigger의 텍스트는 패널 이름으로 자동 연결되지 않는다.
```tsx
{/* 입력 폼 */}
```
보이는 제목이 있다면 해당 제목의 고유 ID를 연결한다. 같은 화면에서 반복되는 패널은 `React.useId()` 등으로 서로 다른 ID를 생성한다.
```tsx
const titleId = React.useId()
프로젝트 안내
일정과 담당자 정보를 확인합니다.
```
대표 Story는 정보·폼·정렬 예제 각각에 `aria-label`을 제공한다. 이름 지정은 기존 레이아웃과 Radix의 focus 이동·Escape·닫힘 동작을 바꾸지 않는다.
## 6. Token
Component Mapping 위치: `packages/ui/src/theme/popover.css` (`--hm-popover-*`).
`background`, `text`, `border-width`, `border-color`, `radius`, `shadow`, `padding`, `width`, `font-size`, `line-height`, `font-weight`. 다른 컴포넌트의 Component Token을 참조하지 않는다. 신규 Primitive 없음.
## 7. shadcn / Radix에서 유지한 항목
Radix 구조·ARIA(`role=dialog`, `aria-expanded`, `aria-controls`)·Escape·바깥 클릭 닫힘·focus 이동/복귀, Portal·positioning·collision, `data-slot`, `cn(..., className)`, animation class, `z-50`.
## 8. HM에서 변경한 항목
- shadcn `w-72 rounded-md border p-4 shadow-md bg-popover text-popover-foreground` 등 시각 class 제거 → theme CSS.
- `PopoverClose` 추가 export (shadcn 기본 export에 없는 Radix `Close`).
## 9. Storybook
- 대표 Story `All`("Popover"): 정보 패널, 폼 패널(Input + 취소/저장 `PopoverClose`), `align` start/center/end 비교.
- verification: `Open`(열린 상태).
## 10. 사용자 확정 결정
- 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 개별 수치에 대한 별도 결정은 없다.
## 11. 미확정 / 보류
- 수치(288px, 16px padding, 그림자)는 현재 HM 규격이며 디자인 확정 시 재검토.
- arrow 없음(shadcn 기본에 없음).
- 크기 variant 없음. 폭은 `className`으로 확장.
- Motion 규칙은 정의되지 않아 shadcn 기본 animation 유지.
## 12. 관련 파일
```
packages/ui/src/components/ui/popover.tsx
packages/ui/src/theme/popover.css
packages/ui/src/theme.css (import)
packages/ui/src/index.ts (export)
src/components/popover.stories.tsx
```
## 13. 검증 상태
- 2026-10-08: 대표 Story의 정보·폼·정렬 3종, 총 5개 `role="dialog"` 접근성 이름을 확인했다. 키보드 Enter·Tab·Escape, 폼 자동 focus, 취소/저장 `PopoverClose`의 닫힘·trigger focus 복귀, 바깥 클릭 닫힘을 Browser에서 확인했다. 공개 API 타입 검사 통과, 최종 검증 Console 오류 없음.
- `npm run typecheck`, `npm run build`: 통과(2026-10-02).
- 실제 interaction(Browser pane): 클릭 열기(focus가 content 안 Input으로 이동), Escape 닫힘과 trigger focus 복귀, 바깥 클릭 닫힘.
- computed style: width 288px, white, border `rgb(222,219,210)` 1px, radius 4px, padding 16px, shadow `0 0 12px .2`.
- 미확인: 좁은 viewport collision 시각.
## 14. Floating 계열 공통 (Semantic 승격 완료)
Dropdown Menu·Popover·Hover Card는 surface(White, 1px pale border, 4px radius, `0 0 12px .2` 그림자)가 같다. 의미(떠 있는 패널의 면·윤곽·그림자)도 같아 공통 Semantic 후보는 아래이며, `default.css`의 `--hm-sem-floating-*`로 승격했고 각 컴포넌트의 `--hm--*`가 이를 참조한다(Component Token은 유지). Dialog(Dialog 전용 값), Tooltip(dark inverse face)은 같은 면이 아니다.
- Floating background / border / shadow / radius
Popover만 다르게 하려면 `--hm-popover-*`만 override한다. Date Picker·Combobox popup은 Popover를 그대로 쓰므로 이 값을 따른다.