Files
HMDesign/docs/components/pagination.md
T

103 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Pagination
현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Pagination 고유의 API·Token·상태·결정만 기록한다.
## 1. 상태
| 항목 | 내용 |
|---|---|
| 구현 상태 | 구현 완료 (현재 HM 규격) |
| 기준일 | 2026-10-02 |
| 기반 | shadcn/ui Pagination (`nav > ul > li > a`, Radix 없음) |
| 대표 Story | `Components/Navigation/Pagination` → `All` (이름 "Pagination") |
| 보류 | 아래 §11 |
## 2. 적용 근거
- **참고 근거**: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Pagination이 컴포넌트 이름으로만 있고 규격은 없다. 참고할 수치가 없었다.
- **현재 HM 규격**: 아래 §3~§4. 현재 HM Semantic 체계(fg-primary, action-primary, accent)로 새로 정했다.
- **구현 방식**: shadcn Pagination 구조 유지. Presentational / navigation primitive로, page state와 href는 소비자가 관리한다. 링크는 `a`로 유지하고 Button으로 바꾸지 않는다.
- **Button과의 관계**: shadcn은 `buttonVariants`를 입혀 Button처럼 그리지만, HM Button은 `data-slot="button"` 기반이라 이를 그대로 쓰면 HM 스타일이 적용되지 않고 Button token도 재사용하면 안 된다. 그래서 `PaginationLink`는 독립 anchor 스타일(`--hm-pagination-*`)로 구현했다.
- **보류**: §11.
## 3. 기본 규격 (현재 HM 규격)
| 항목 | 값 |
|---|---|
| Page link | 34 × 34px(min-width 34), radius 4px(`--hm-radius-control-md`), 14 / 20 / 500, `--hm-sem-fg-primary` |
| Previous / Next | 높이 34px, padding `0 12px`, 아이콘 16px, 라벨 `hidden sm:block`("Previous"/"Next", 모바일은 아이콘만) |
| 간격 | 목록 gap 4px |
| Current Page | 배경 `--hm-sem-action-primary`(Primary Green `#123328`), 글자 `--hm-sem-on-action-primary`(흰색), weight 700 |
| Ellipsis | 34 × 34 영역, 아이콘 16px, `--hm-sem-fg-secondary` |
## 4. 상태 규격
| 상태 | 값 |
|---|---|
| Hover | 배경 `--accent` 45%, 글자 `--accent-foreground` |
| Focus (`:focus-visible`) | ring 모양 `0 0 0 3px ring 50%` |
| Current (`isActive`) | `aria-current="page"`, 위 Current 스타일(hover에도 `Current`로 보이도록 hover 배경 대신 유지) |
| Disabled (`aria-disabled="true"`) | `opacity: 0.6`(이 컴포넌트 값, 전역 Disabled 규칙으로 승격 안 함), `cursor: not-allowed`, hover 면 없음, `tabIndex -1`, 클릭 취소(`preventDefault`) |
## 5. API
export: `Pagination`, `PaginationContent`, `PaginationItem`, `PaginationLink`, `PaginationPrevious`, `PaginationNext`, `PaginationEllipsis`.
- `PaginationLink`: `a` props + `isActive`(현재 페이지), `size` `"default" | "icon"`(기본 `icon`; shadcn은 Button size, HM은 `default`/`icon` 둘만). 출력 `data-slot="pagination-link"`, `data-active`, `data-size`.
- disabled: 별도 prop 없이 `aria-disabled`를 쓴다. `aria-disabled="true"`이면 컴포넌트가 `tabIndex=-1`로 두고 클릭(onClick 호출 포함)을 취소한다. anchor에는 native disabled가 없어 class만으로는 접근성이 보장되지 않기 때문에 보완했다.
- `PaginationPrevious`/`PaginationNext`: 기본 `aria-label`("Go to previous/next page")과 라벨, `children`으로 교체 가능.
- 새 상태 관리, `page`/`total` 같은 prop은 만들지 않았다.
## 6. Token
Component Mapping 위치: `packages/ui/src/theme/pagination.css` (`--hm-pagination-*`).
`gap`, `height`, `min-width`, `padding-x`, `radius`, `font-size|line-height|font-weight`, `icon-size`, `text`, `hover-background`, `hover-text`, `active-background`, `active-text`, `active-font-weight`, `ellipsis-color`, `disabled-opacity`. Semantic만 참조하고 `--hm-button-*` 등 다른 컴포넌트 Token을 쓰지 않는다. 신규 Primitive 없음.
## 7. shadcn에서 유지한 항목
`nav role="navigation" aria-label="pagination"`, `ul`/`li` 구조, `a` 링크, `isActive`→`aria-current="page"`, Previous/Next 아이콘과 `aria-label`, Ellipsis의 `aria-hidden` + sr-only, `data-slot`, `cn(..., className)`.
## 8. HM에서 변경한 항목
- `buttonVariants` 합성을 제거하고 독립 anchor 스타일로 대체(위 §2).
- `size`를 shadcn의 Button size 전체에서 `default`/`icon`으로 좁혔다.
- `aria-disabled` 처리(tabIndex, 클릭 취소)를 추가했다.
- 현재 페이지를 outline 대신 채움(primary)으로 표시.
## 9. Storybook
- 대표 Story `All`("Pagination"): First page, Middle page, Last page, Long range with ellipsis(1 … 9 10 11 … 20)를 한 페이지에 둔다. 각 Specimen이 local state를 가지고 클릭 시 현재 페이지가 실제로 바뀐다(`#page-N` href + `preventDefault`).
- verification: `First Page`, `Last Page`, `Long Range`.
## 10. 사용자 확정 결정
- 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 개별 수치에 대한 별도 결정은 없다.
- 2026-10-06 (Pagination Current 원복): 직전 Active Semantic 작업에서 Current를 `--hm-sem-state-active`(Light 2)로 바꾼 변경을 취소하고 이전 값 `--hm-sem-action-primary` / `--hm-sem-on-action-primary`(Primary Green `#123328`, 흰색)로 복원했다. Current + Hover도 Current 색을 유지한다. Hover의 `--accent` 45%는 유지한다. Focus·Disabled·API·구조 변경 없음.
## 11. 미확정 / 보류
- 수치(34px, 4px radius, 12px padding, 채움 current, Disabled opacity 0.6)는 현재 HM 규격이며 디자인 확정 시 재검토.
- page range(ellipsis 계산)는 소비자 책임이다(Story의 `pageRange`는 예시일 뿐 컴포넌트가 아님).
- 언어: Previous/Next 기본 라벨은 shadcn 영어 그대로(`children`으로 교체).
- 좁은 화면 compact 표현은 shadcn의 라벨 숨김(`sm` 미만)만 유지. 별도 compact variant 없음.
- 페이지 크기 선택, 결과 수 표시는 범위 밖.
## 12. 관련 파일
```
packages/ui/src/components/ui/pagination.tsx
packages/ui/src/theme/pagination.css
packages/ui/src/theme.css (import)
packages/ui/src/index.ts (export)
src/components/pagination.stories.tsx
```
## 13. 검증 상태
- `npm run typecheck`, `npm run build`: 통과(2026-10-02).
- 실제 interaction(Browser pane, JS click): page 4 클릭으로 current가 4로 변경, Next로 5, 첫 페이지의 Previous(`aria-disabled`)와 마지막 페이지의 Next 클릭은 상태를 바꾸지 않음. `aria-disabled` 링크는 `tabindex=-1`, opacity 0.6, cursor not-allowed. Current는 `aria-current="page"`, 34×34, 배경 `rgb(18,51,40)`, 글자 white, weight 700.
- 구조: `a` 요소, ellipsis 위치(1 … 9 10 11 … 20)와 First/Last 상태 확인.
- Responsive: 390×844에서 Previous/Next 라벨이 숨겨져 아이콘만 남고, 20페이지 범위(`1 … 9 10 11 … 20`)가 350px 한 줄에 들어가며 가로 overflow 없음.
- 미확인: 실제 키보드 Tab/Enter 순서, pointer hover·focus ring 시각.