Files
HMDesign/docs/components/slider.md

9.8 KiB
Raw Permalink Blame History

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로 재마운트하고, 동적 값 변경에는 Controlled value를 사용한다. 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, thumb size-3 border-ring ring-* class를 제거하고 Token으로 이전. thumb을 12px에서 18px로 키웠다.
  • aria-label을 thumb으로 전달(shadcn은 Root에만 붙어 thumb에 이름이 없었다).

9. Storybook

  • 대표 Story All("Slider"): Single(값 label), Range(두 thumb, minStepsBetweenThumbs 5), 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 값·배열 길이 변경, Uncontrolled defaultValue 배열 길이 변경 시 초기 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 border rgb(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), range rgb(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 시각).