TimePicker
Input형 트리거 + 시간 목록/list, 웹 권장 input, 터치 보조 dial, 모바일 wheel — interval·minuteStep·hourCycle·minTime·maxTime으로 'HH:mm' 선택 범위를 만드는 비제어 컴포넌트입니다.
마지막 업데이트 2026-07-02
한눈에#
Input형 트리거 + 시간 목록/입력형/시계판 다이얼/컬럼 wheel로 시각만 고릅니다 — list는
interval, input·dial·wheel은 minuteStep을 쓰고 모든 모드는 minTime/maxTime 범위를 지킵니다.
- Input형 트리거 + 시간 선택 표면
- interval·minuteStep·hourCycle·minTime·maxTime
- list·input·dial·wheel 모드
- 비제어 + BottomSheet 모바일
트리거 높이는 input.height로 폼 줄맞춤 — 웹 권장은 input/list, wheel은 모바일 컬럼 피커, dial은 터치·시각 보조
사용 시점#
회의 시작·마감처럼 시각만 고르면 TimePicker — 날짜나 날짜+시각은 다른 컴포넌트입니다.
권장 — 이렇게 쓰세요
지양 — 이러지 마세요
쓴다 — 회의 시작·마감 시각처럼 시각만(interval·minTime/maxTime으로 업무 시간대 제한)
대신 DatePicker — 날짜를 고르거나 날짜+시각을 함께 잡을 때(나란히 배치)
플레이그라운드#
컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.
TimePicker를 직접 조작해 보세요
속성·테마·토큰을 바꾸고 React·Flutter 코드를 확인하는 풀스크린 빌더로 엽니다.
플레이그라운드는 넓은 작업 영역이 필요해 웹·태블릿에서 편집할 수 있어요.
변형#
옵션은 minTime(기본 ‘00:00’)부터 interval분 간격으로 maxTime(기본
‘23:59’, 포함)까지 생성됩니다. 형식이 어긋난 defaultValue(‘25:99’ 등)는
무시되고 placeholder가 표시됩니다. 팝업은 트리거 아래에 뜨고, 뷰포트 하단
공간이 부족하면 위로 플립됩니다.
표시 모드 (ADR-012 Track C)#
mode가 오버레이 내용을 가릅니다 — 기본 'list'는 인터벌 목록(기존 동작),
'input'은 데스크톱 웹 권장 컴팩트 입력형,
'dial'은 터치·시각 보조용 시계판 2단계 선택(Material time picker 계열),
'wheel'은 모바일 컬럼 wheel입니다. 기존 wheel 데스크톱 사용은 하위호환을 위해 input 표면으로 fallback합니다. 네 모드는 같은 트리거와
HH:mm 커밋 계약을 공유합니다.
input은 웹 폼에서 가장 빠른 경로입니다. 시 · 분은 네이티브 <select>가 아니라
WDS Select라 목록 클릭은 물론 타입어헤드(숫자 타이핑 점프)·화살표 탐색을 그대로 쓸 수 있고,
오전/오후는 WDS SegmentedButton으로 고릅니다. 선택 버튼은 minuteStep과
minTime/maxTime 검증을 통과한 조합에서만 활성화됩니다. input과 wheel은 플랫폼 적응
쌍입니다 — 어느 이름을 쓰든 웹에서는 Select 필드, 모바일 시트에서는 엄지 스크롤 컬럼 wheel로
각 플랫폼 최적 관용구를 렌더합니다.
dial은 시(0–23, 바깥/안쪽 두 링)를 먼저 고른 뒤 분을 고르면 HH:mm으로
확정합니다. 상단 readout이 조합 중인 시각(09:--)을 라이브로 보여주고,
선택된 시/분은 중심에서 숫자로 이어지는 hand로 표시되며, 시계판 표면을 드래그하면
각도에 가장 가까운 시/분으로 이동합니다. 즉시확정 관용구라 확정 버튼은 없지만
데스크톱 팝업에는 취소 버튼이 있습니다. 분 눈금은 minuteStep(기본 5,
5/10/15/20/30처럼 60을 나누는 값)으로 조절합니다. 시계판 위의 HH:mm
직접 입력 필드가 키보드 경로를 제공합니다 —
입력값도 minuteStep과 minTime/maxTime 범위를 모두 통과해야 Enter로 확정됩니다.
폼이 밀집된 웹 화면에서는 input 또는 list를 우선 사용하고, dial은 터치 디바이스나
시각적 탐색이 더 중요한 화면에서 보조 선택지로 사용합니다. compact 뷰포트(mobile)에서는
BottomSheet 안에 같은 시계판이 렌더됩니다.
wheel은 모바일 전용에 가까운 표면입니다. BottomSheet 안에서 오전/오후 → 시 → 분
순서의 컬럼 wheel을 노출합니다. hourCycle={12}면 오전/오후가 추가되고,
hourCycle={24}(기본)이면 시간·분만 보입니다. 데스크톱에서 mode="wheel"을 쓰는
기존 코드는 하위호환으로 input 표면을 보여주지만, 새 웹 화면에서는 mode="input"을
권장합니다.
크기#
단일 크기입니다 — 트리거 높이는 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)가 열리고,
시간 리스트·다이얼·wheel이 시트 안에 렌더됩니다. 단일 선택이므로 list 항목과 dial 분은
즉시 확정되고, wheel은 선택으로 확정합니다. 시트 제목은 mobileTitle, 없으면
placeholder가 채워집니다. 모바일 표면에서는 데스크톱 팝업 테두리와 그림자를 제거하고
본문 폭을 채우며, 리스트는 모바일 터치 행 높이를 사용하고 다이얼은 좁은 화면에 맞춰
유동 폭으로 줄어듭니다. wheel도 내부 카드 테두리 없이 full-width 컬럼을 사용하며,
12시간제에서는 오전/오후 → 시 → 분 순서로 배치합니다.
라이브 데모는 데스크톱 뷰포트라 코드로 안내합니다.
<TimePicker
mobile
mobileTitle="시작 시간 선택"
aria-label="시작 시간"
interval={15}
minTime="09:00"
maxTime="18:00"
onChange={(time) => applyStartTime(time)}
/>
<TimePicker
responsive="mobile"
mode="wheel"
hourCycle={12}
minuteStep={15}
minTime="09:00"
maxTime="18:00"
aria-label="업무 시간"
/>
닫힘 경로(배경 클릭·Escape·닫기 버튼·핸들 드래그 다운)와 포커스 복원은 BottomSheet 계약을 그대로 따릅니다 — 값 변경 없이 닫힙니다.
Props#
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
defaultValue | string | — | 초기 선택 시간 'HH:mm' — 형식이 어긋나면 무시 |
onChange | (value: string) => void | — | 선택 변경 콜백 — 같은 값 재선택은 발화하지 않음 |
interval | number | 30 | 옵션 간격(분) — 기본 30 |
minTime | string | — | 선택 가능한 최소 시간 'HH:mm' — list 옵션과 input/dial/wheel 선택·입력에 적용 |
maxTime | string | — | 선택 가능한 최대 시간 'HH:mm' (포함) — list 옵션과 input/dial/wheel 선택·입력에 적용 |
placeholder | string | 시간 선택 | 선택 전 트리거에 표시되는 문구 |
mode | 'list' | 'input' | 'dial' | 'wheel' | list | 표시 모드 — 'list'(기본): 인터벌 목록. 'input': 웹 권장 입력형. 'dial': 시계판 2단계. 'wheel': 모바일 컬럼 휠. input/wheel은 플랫폼 적응 쌍(웹=Select 필드·모바일=휠 크로스 폴백). 트리거는 공유 |
minuteStep | number | — | input/dial/wheel 모드 분 눈금 — 5/10/15/20/30처럼 60을 나누는 5~30분 단위. 기본 5 |
hourCycle | 12 | 24 | 24 | input/wheel 모드 표시 주기 — 12면 오전/오후를 노출하고 값은 계속 'HH:mm'으로 커밋. 기본 24 |
responsive | 'auto' | 'mobile' | 'desktop' | — | 적응형 표시 (ADR-012) — auto(기본): compact 뷰포트에서 borderless/full-width BottomSheet 목록 전환(탭 즉시 확정). mobile/desktop은 강제 override |
mobile | boolean | false | @deprecated responsive='mobile' 사용. true면 BottomSheet 목록 강제 — 하위호환 alias |
mobileTitle | string | — | 시트 제목 — 기본 placeholder |
ref | Ref<HTMLButtonElement> | — | 트리거 <button>으로 (병합) 전달되는 ref (React 19 ref-as-prop) |
접근성#
list 모드 트리거는 role="combobox" + aria-haspopup="listbox", 팝업은
role="listbox"입니다. 포커스는 트리거에 머물고 aria-activedescendant가 활성
옵션 id를 가리킵니다(데스크톱 팝업 전용 — mobile 시트는 BottomSheet 포커스 트랩
계약을 따름). dial 모드 트리거는 aria-haspopup="dialog"를 노출하고, 데스크톱
팝업은 role="dialog"로 이름 붙은 뒤 열림 직후 직접 입력 필드에 포커스를 보냅니다.
wheel 모드도 aria-haspopup="dialog"를 노출합니다. 데스크톱에서는 오전/오후
세그먼트가 button[aria-pressed]로, input 표면의 시·분 입력 컨트롤이 combobox로
동작하고, 모바일 wheel 컬럼은 spinbutton 의미론으로 현재 시간·분·오전/오후 값을 보고합니다.
Escape는 값 변경 없이 닫고 트리거 포커스를 복원합니다.
키보드 열기는 데스크톱·모바일 두 표면 모두에서 동작합니다(WCAG 2.1.1) —
mobile/compact에서도 트리거에 포커스를 두고 ↓·↑·Enter·Space를 누르면
BottomSheet가 열립니다. 시트가 열린 뒤의 포커스 트랩·Escape 닫기는 BottomSheet
계약이 담당합니다.
| 키 | 동작 |
|---|---|
↓ / ↑ / Enter / Space (닫힘) | 열기 — 선택(없으면 첫) 옵션 활성 |
0–9 (list, 데스크톱) | 타입어헤드 — 닫혀 있으면 열며 해당 시각으로 점프(“9”→09:00, 이어 “3”→09:30) |
↓ / ↑ (열림) | 활성 옵션 이동 — 양 끝에서 클램프 |
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 |
| 눌림(옵션·시계판 숫자·wheel 항목) | color.surface-pressed (선택 배경은 안 덮음) |
| 다이얼 hand | color.primary · 선택 숫자와 중심을 잇는 구조적 geometry |
| 옵션 높이 | control.height-sm |
| 비활성 | input.bg-disabled · color.text-placeholder · 다이얼 범위 밖 눈금 muted |
| 모션 | duration.fast + 팝업 등장 ease.emphasized-decelerate / ease.standard |