TimePicker
Input형 트리거 + 시간 목록 드롭다운 — interval·minTime·maxTime으로 'HH:mm' 옵션을 생성하는 비제어 컴포넌트입니다. mobile이면 BottomSheet 목록으로 전환됩니다 (DK 사양 승격).
마지막 업데이트 2026-06-11
한눈에#
Input형 트리거 + 시간 목록 드롭다운으로 시각만 고릅니다 — interval·minTime/maxTime으로 'HH:mm' 옵션을 생성합니다.
- Input형 트리거 + 시간 목록
- interval·minTime·maxTime
- list·dial 모드
- 비제어 + BottomSheet 모바일
트리거 높이는 input.height로 폼 줄맞춤 — list는 인터벌 목록, dial은 시→분 시계판
사용 시점#
회의 시작·마감처럼 시각만 고르면 TimePicker — 날짜나 날짜+시각은 다른 컴포넌트입니다.
회의 시작·마감 시각처럼 시각만(interval·minTime/maxTime으로 업무 시간대 제한)
플레이그라운드#
컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.
import { TimePicker } from '@wds/ui-web';
<TimePicker />변형#
옵션은 minTime(기본 '00:00')부터 interval분 간격으로 maxTime(기본
'23:59', 포함)까지 생성됩니다. 형식이 어긋난 defaultValue('25:99' 등)는
무시되고 placeholder가 표시됩니다. 팝업은 트리거 아래에 뜨고, 뷰포트 하단
공간이 부족하면 위로 플립됩니다.
표시 모드 (ADR-012 Track C)#
mode가 오버레이 내용을 가릅니다 — 기본 'list'은 위의 인터벌 목록(기존 동작),
'dial'은 시계판 2단계 선택(M3 dial)입니다. 두 모드는 같은 트리거를 공유합니다.
dial은 시(0–23, 바깥/안쪽 두 링)를 먼저 고른 뒤 분(5분 단위)을 고르면 HH:mm으로
확정합니다. 시계판 위의 HH:mm 직접 입력 필드가 키보드 경로를 제공합니다 — 유효한
시간을 입력하고 Enter로 확정합니다. compact 뷰포트(mobile)에서는 BottomSheet 안에
같은 시계판이 렌더됩니다. min/max 범위 제한은 list 모드에만 적용됩니다.
크기#
단일 크기입니다 — 트리거 높이는 input.height(44px)로 Input과 같은 폼 컨트롤
줄맞춤이고, 최소 폭은 'HH:mm' + 시계 아이콘 기준 9rem(시간 전용이라
Select 14rem보다 좁음)입니다. 리스트는 최대 16rem 높이에서 스크롤됩니다.
상태#
- Focus — 트리거
:focus-visible에color.primary아웃라인 링 - 열림 —
input.border-focus보더 +aria-expanded="true" - 활성 옵션 —
color.surface-hover배경(마우스 호버도 활성을 따라감) - 선택 옵션 —
aria-selected+color.primary-text잉크 - Disabled —
input.bg-disabled배경 + 커서 차단
비제어 컴포넌트입니다 — defaultValue로 시작하고 선택 시
onChange('HH:mm')로 알립니다. 같은 값 재선택은 발화하지 않습니다.
모바일#
DK 모바일 재활용 표준입니다 — mobile을 켜면 트리거 클릭 시 데스크톱 팝업
대신 BottomSheet(height half)가 열리고,
시간 리스트는 role="listbox" 의미론을 유지한 채 시트 안에 렌더됩니다. 단일
선택이므로 항목을 누르는 즉시 확정되고 시트가 닫힙니다(임시 상태 없음). 시트
제목은 mobileTitle, 없으면 placeholder가 채워집니다. 라이브 데모는
데스크톱 뷰포트라 코드로 안내합니다.
<TimePicker
mobile
mobileTitle="시작 시간 선택"
aria-label="시작 시간"
interval={15}
minTime="09:00"
maxTime="18:00"
onChange={(time) => applyStartTime(time)}
/>
닫힘 경로(배경 클릭·Escape·닫기 버튼·핸들 드래그 다운)와 포커스 복원은 BottomSheet 계약을 그대로 따릅니다 — 값 변경 없이 닫힙니다.
Props#
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
defaultValue | string | — | 초기 선택 시간 'HH:mm' — 형식이 어긋나면 무시 (비제어) |
onChange | (value: string) => void | — | 선택 변경 콜백 — 같은 값 재선택은 발화하지 않음 |
interval | number | 30 | 옵션 간격(분) — 1 미만/비정상 값은 30으로 대체 |
minTime | string | '00:00' | 목록 시작 시간 'HH:mm' |
maxTime | string | '23:59' | 목록 끝 시간 'HH:mm' (포함) |
placeholder | string | '시간 선택' | 선택 전 트리거에 표시되는 문구 |
mode | 'list' | 'dial' | 'list' | list: 인터벌 목록(기본) · dial: 시계판 2단계(시→분) + 직접 입력 |
mobile | boolean | false | true면 팝업 대신 BottomSheet 목록 — 탭 즉시 확정 (DK 모바일 표준) |
mobileTitle | string | — | 시트 제목 — 기본 placeholder |
…rest | Omit<ButtonHTMLAttributes, 'onChange' | 'value' | 'defaultValue'> | — | aria-label · disabled 등은 트리거 button으로 전달 |
접근성#
트리거가 role="combobox" + aria-haspopup="listbox", 팝업이
role="listbox"입니다. 포커스는 항상 트리거에 머물고
aria-activedescendant가 활성 옵션 id를 가리킵니다(데스크톱 팝업 전용 —
mobile 시트는 BottomSheet 포커스 트랩 계약을 따름).
키보드 열기는 데스크톱·모바일 두 표면 모두에서 동작합니다(WCAG 2.1.1) —
mobile/compact에서도 트리거에 포커스를 두고 ↓·↑·Enter·Space를 누르면
BottomSheet가 열립니다. 시트가 열린 뒤의 포커스 트랩·Escape 닫기는 BottomSheet
계약이 담당합니다.
| 키 | 동작 |
|---|---|
↓ / ↑ / Enter / Space (닫힘) | 열기 — 선택(없으면 첫) 옵션 활성 |
↓ / ↑ (열림) | 활성 옵션 이동 — 양 끝에서 클램프 |
Enter / Space | 활성 옵션 선택 + 닫기 + 트리거 포커스 유지 |
Escape | 값 변경 없이 닫기 |
Tab | 닫고 기본 포커스 이동 |
외부 클릭으로도 닫히며, 선택 옵션은 aria-selected로 보고됩니다.
토큰#
component 토큰 없이 Input의 component 토큰(input.*)과 semantic을 직접
소비합니다(신설 기준 §4 미충족 — Select과 같은 dropdown 어휘).
| 속성 | 토큰 |
|---|---|
| 트리거 | input.bg/fg/border/height/radius · placeholder input.placeholder |
| 포커스 | color.focus-ring 아웃라인 · 열림 보더 input.border-focus |
| 시계 아이콘 | icon.size-md · color.text-subtle |
| 팝업 | color.surface-raised · radius.md · shadow.lg · z.dropdown |
| 옵션 활성/선택 | color.surface-hover/selected · 선택 잉크 color.primary-text |
| 눌림(옵션·시계판 숫자) | color.surface-pressed (선택 배경은 안 덮음) |
| 옵션 높이 | control.height-sm |
| 비활성 | input.bg-disabled · color.text-placeholder |
| 모션 | duration.fast + 팝업 등장 ease.emphasized-decelerate / ease.standard |