9.8 KiB
Slider
현재 HM 구현의 기준 문서. 공통 원칙은 docs/guidelines/프로젝트-지침.md(§6, §7), Token 구조 원칙은 테마-토큰-가이드.md를 따른다. 이 문서는 Slider 고유의 API·Token·상태·결정만 기록한다.
1. 상태
| 항목 | 내용 |
|---|---|
| 구현 상태 | 구현 완료 (현재 HM 규격) |
| 기준일 | 2026-10-06 |
| 기반 | shadcn/ui Slider (Radix Slider) |
| 대표 Story | Components/Form/Slider → All (이름 "Slider") |
| 보류 | 아래 §11 |
2. 적용 근거
- 참고 근거: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Slider가 없다. 참고할 수치가 없었다.
- 현재 HM 규격: 아래 §3~§4. 현재 HM Semantic/Primitive로 새로 정했다.
- 구현 방식: shadcn / Radix Slider 구조(Root, Track, Range, Thumb)를 유지하고 theme CSS로 연결. Slider는 값 입력 컨트롤이며 Toggle/Button 계열과 역할이 다르다.
- 보류: §11.
3. 기본 규격 (현재 HM 규격)
| 항목 | 값 |
|---|---|
| Track | 두께 6px, radius 9999px, 배경 --hm-sem-palette-warm-neutral-light |
| Range | --hm-sem-state-active |
| Thumb | 18px 원, 배경 --hm-sem-bg-surface, 2px --hm-sem-state-active border |
| Thumb halo | Hover 4px / Drag(:active) 6px Primary 20% 후광. 키보드 Focus는 같은 크기의 Focus Semantic 20% 후광, 커서 grab/grabbing 유지 |
| 폭/높이 | Root에 className. 기본은 부모 폭 100%, vertical은 높이 100%·최소 176px(shadcn 기본) |
4. 상태 규격
| 상태 | 값 |
|---|---|
| Default | 후광 없음, 커서 grab |
| Hover | 4px --hm-sem-action-primary 20% 후광 |
Focus (:focus-visible) |
4px --hm-sem-stroke-input-focus 20% 후광 |
Drag (:active) |
6px Primary 20% 후광, 커서 grabbing |
| Focus + Hover | Focus 색 우선, 4px Focus 20% 후광 |
| Focus + Drag | Focus 색 우선, Drag의 6px 크기와 grabbing 커서 유지 |
Disabled (data-disabled) |
Root opacity: 0.6(이 컴포넌트 값, 전역 Disabled 규칙으로 승격 안 함), 후광 없음, 커서 not-allowed, thumb이 focus를 받지 않음 |
| Orientation | data-orientation horizontal / vertical |
우선순위: Disabled > Focus 색 > Hover/Drag interaction 색. Drag는 halo 크기만 6px로 확대한다. Radix의 pointer focus 정책은 유지한다(pointer로 thumb을 잡으면 focusVisible: false로 focus하므로 일반 pointer drag는 Primary halo). Focus + Drag 규칙은 실제로 :focus-visible과 :active가 겹칠 때 적용하며, interaction 규칙이 Focus 표시를 덮지 않는다.
5. API
Radix Slider props 그대로. export: Slider.
value/defaultValue/onValueChange/onValueCommit,min/max/step,minStepsBetweenThumbs,orientation,dir,inverted,disabled,name.- 값이 배열이면 항목 수만큼 thumb을 렌더한다(두 개면 range). Range용 별도 컴포넌트는 없다.
value와defaultValue를 모두 생략하면 초기값은[min]이고 thumb은 하나다(min기본값 0). 숨겨진 추가 thumb이나 빈 FormData 항목을 만들지 않는다.- Uncontrolled의
defaultValue는 마운트 시 초기값이다. 이후defaultValue배열이나 항목 수를 바꿔도 내부 값과 thumb 수는 초기 배열을 유지한다. 초기화가 필요하면 새key로 재마운트하고, 동적 값 변경에는 Controlledvalue를 사용한다. Controlled/Uncontrolled 모드는 컴포넌트 수명 동안 유지한다. - Form 안에서
name을 지정하면 Enabled Slider는 현재 값을 제출한다(단일값은name, 범위값은name[]으로 여러 항목).form="id"로 외부 Form을 연결할 수도 있다. - Disabled Slider는 FormData와 native Form Submit에서 제외한다. 설치된 Radix Slider 1.4.7의 내부 input에
disabled가 전달되지 않아 HM은 disabled일 때 Radix에 넘기는name을 생략한다. 다시 Enabled로 바꾸면 기존name으로 현재 값 제출을 재개한다. Controlled/Uncontrolled에 동일하게 적용하며 값·callback·키보드·ARIA 계약은 유지한다. aria-label:role="slider"는 Root가 아니라 Thumb이므로 Slider가aria-label을 모든 thumb에 전달한다(thumb이 둘 이상이면"label 1/2","label 2/2").aria-label이 없으면 Radix 기본 thumb 이름(Minimum/Maximum)이 남는다.variant,status,appearance같은 prop은 만들지 않았다.
6. Token
Component Mapping 위치: packages/ui/src/theme/slider.css (--hm-slider-*).
track-size, track-background, range-background, thumb-size, thumb-background, thumb-border-width|color, thumb-halo-color|size|size-active, thumb-focus-halo-color, disabled-opacity. Interaction halo는 Primary, Focus halo는 기존 Focus Semantic을 참조한다. 다른 컴포넌트 Token을 쓰지 않는다. 신규 Primitive 없음. Track/Range/Thumb은 내부 부품이라 theme CSS(레이어 밖)로 정하고, Slider Root의 폭·높이는 className 영역이다.
7. shadcn / Radix에서 유지한 항목
Radix 구조·동작(pointer drag, track 클릭 이동, 키보드, thumb 정렬/교차 방지), ARIA(role=slider, aria-valuemin|max|now, aria-orientation), 배열 value에서 thumb 수를 계산하는 shadcn 로직, touch-none select-none, data-slot, cn(..., className).
8. HM에서 변경한 항목
- shadcn
bg-muted,h-1,bg-primary, thumbsize-3 border-ring ring-*class를 제거하고 Token으로 이전. thumb을 12px에서 18px로 키웠다. aria-label을 thumb으로 전달(shadcn은 Root에만 붙어 thumb에 이름이 없었다).
9. Storybook
- 대표 Story
All("Slider"): Single(값 label), Range(두 thumb,minStepsBetweenThumbs5), Step(25), Disabled, Vertical을 한 페이지에 둔다. 값 label은 Story 조합(local state)이다. - verification:
Single,Range,Disabled,Focus(실제 thumb focus). 기본 Sidebar에서 숨긴다.
10. 사용자 확정 결정
- 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 개별 수치에 대한 별도 결정은 없다.
11. 미확정 / 보류
- 수치(6px track, 18px thumb, halo 4/6px)는 현재 HM 규격이며 디자인 확정 시 재검토.
- 값 tooltip/label 표시, tick mark, 범위 밖 구간 표현은 정의하지 않았다.
- 상태색 range(예: 경고)는 HM 상태색 확정 후.
- hit area: thumb 18px이 터치 타깃(44px 권장)보다 작다. 터치 기기 정책은 미정.
- 대표 Story의 form 예시는 만들지 않았다. 기본값 및 Enabled/Disabled 제출 계약은 §5를 따른다.
12. 관련 파일
packages/ui/src/components/ui/slider.tsx
packages/ui/src/theme/slider.css
packages/ui/src/theme.css (import)
packages/ui/src/index.ts (export)
src/components/slider.stories.tsx
13. 검증 상태
- 2026-10-08 기능 개선: 프로젝트 외부 UI 복사본에서 타입 검사와 패키지 빌드 통과. 실제 프로젝트 Slider 소스와 복사본의 SHA-256이 일치하며, 브라우저는 해당 패키지 빌드 결과를 사용했다.
- 브라우저 Form 검증: 기본값 생략/
min=10은 thumb 하나·정상aria-valuenow·빈 제출 항목 없음. 명시적 단일/범위, Controlled 값·배열 길이 변경, UncontrolleddefaultValue배열 길이 변경 시 초기 thumb 수 유지 확인. Controlled/Uncontrolled 단일·범위의 Disabled → Enabled → Disabled 전환에서 FormData와 실제 native POST 제출 결과 정상. 키보드·callback·ARIA와 외부form연결의 FormData 확인. - 디자인 보존: Slider CSS·Theme JSON·대표 Story 변경 없음. 수정 전후 computed style에서 thumb 18px/2px border, track 6px, Disabled opacity 0.6, Hover/Focus 4px 후광 일치. 검증 예제와 POST 수집 서버는 외부 임시 환경에서만 실행했다.
- 2026-10-06 Focus 정합:
typecheck,build,build-storybook,git diff --check통과. 기존 문서 예시 CSS(bg-[var(...)])와 Storybook chunk 크기 경고 유지. - Theme Designer에서 Primary
#6837a8, Active#007f73, Focus#c75b00, Accent#ffd36d를 분리 적용한 뒤 일반 verification Story의 computed style로 확인: Range·Thumb borderrgb(0,127,115), Hover/Drag는 Primary 20% / 4·6px, Focus/Focus + Hover는 Focus 20% / 4px, Focus + Drag는 Focus 20% / 6px, Default/Disabled는 shadow 없음. Hover/Drag 중첩은 기존 Storybook pseudo-state mirror로 고정하고 Focus는 실제 DOM focus다. - 각 역할을 단독 override해도 Primary는 interaction halo, Active는 Range·Thumb border, Focus는 Focus halo만 변경하며 Accent는 Slider에 영향 없음. 실제 ArrowRight로 값·ARIA·label 40 → 41 확인. Focus Semantic 이름·값·Runtime/JSON 계약 변경 없음.
npm run typecheck,npm run build: 통과(2026-10-02).- 실제 DOM(Browser pane): thumb
role="slider",aria-valuemin=0,aria-valuemax=100,aria-valuenow가 실제 값과 일치, vertical은aria-orientation=vertical, disabled thumb은 focus 불가.aria-label이 단일 thumb에볼륨, range에가격 범위 1/2·2/2로 반영됨. - keyboard: ArrowRight 40 → 41, ArrowLeft → 40, End 100, Home 0, step 25에서 ArrowRight 50 → 75, vertical ArrowUp 30 → 31. 값 label과 ARIA가 함께 변함.
- pointer: thumb을 오른쪽으로 drag해 40 → 79(range 폭 382/484), track 클릭으로 23, 왼쪽 경계 밖으로 drag해 0에 고정. Range에서 첫 thumb을 두 번째 thumb 너머로 drag해도 값이 정렬 상태(75, 100)로 유지됨.
- computed style: track 6px
rgb(224,218,206), rangergb(18,51,40), thumb 18px·2px border·흰 면, disabled Root opacity 0.6. - Responsive(390×844): Slider 폭 358px(좌우 16px), thumb이 잘리지 않고 페이지 가로 overflow 없음.
- 미확인: PageUp/PageDown, 터치 drag,
inverted/RTL, thumb halo의 시각(hover/active 상태 pointer 시각).