DatePicker
Input형 트리거 + Calendar 팝업 — 단일/범위 날짜를 비제어로 선택하고 트리거에 YYYY-MM-DD로 표시합니다.
마지막 업데이트 2026-06-11
한눈에#
Input형 트리거 + Calendar 팝업으로 하나의 날짜(또는 범위)를 고릅니다 — 선택을 완료하면 팝업이 닫히고 트리거에 YYYY-MM-DD로 표시됩니다.
아직 선택 없음
아직 선택 없음
- Input형 트리거 + Calendar 팝업
- single·range 모드
- docked·modal·modalInput
- 비제어 + BottomSheet 모바일
트리거 높이는 input.height로 Input과 같은 폼 줄맞춤 — 팝업은 APG grid 달력
사용 시점#
폼에서 하나의 날짜(또는 가끔 범위)를 고르면 DatePicker — 업무 조회 기간이나 시각만 고르면 다른 컴포넌트입니다.
마감일·생일·예약일처럼 하나의 날짜(mode=range로 시작~끝도 가능)를 폼 필드로
플레이그라운드#
컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.
import { DatePicker } from '@wds/ui-web';
<DatePicker />변형#
mode가 변형 축입니다 — 'single'(기본)은 클릭 한 번으로 확정되고,
'range'는 시작 → 끝 두 번 클릭으로 확정됩니다. 끝을 시작보다 앞에 찍으면
자동으로 스왑되고, 확정 뒤 세 번째 클릭은 새 범위를 시작합니다.
범위 표시는 'YYYY-MM-DD ~ YYYY-MM-DD'입니다.
데스크톱 변형 (ADR-012 B-P2)#
variant가 데스크톱(비 compact) 표시 방식을 가릅니다(M3 date picker 3변형) —
docked(기본): 트리거 하단에 앵커되는 팝업. 날짜를 클릭하면 즉시 확정되고 닫힙니다(기존 동작).modal: 화면 중앙의 Modal에 달력을 띄우고 취소/확인으로 마무리합니다. 모바일 시트와 동일한 임시 선택(확인 시에만onChange) 흐름입니다.modalInput:modal에 더해 직접 입력 필드(YYYY-MM-DD)를 둡니다(단일 모드). 타이핑과 달력 선택을 모두 허용하며 — 달력 선택은 입력 필드에 동기화되고, 유효한 날짜를 입력하면 임시 선택이 갱신됩니다. 범위 모드에서는 입력 필드 없이modal처럼 동작합니다.
responsive='auto'인 compact 뷰포트에서는 변형과 무관하게 BottomSheet가 우선합니다
(DK 모바일 표준 — ## 모바일 참조).
크기#
단일 크기입니다 — 트리거 높이는 input.height(= control-height-md)로 Input과
같은 어휘를 공유합니다. 3단 size는 component 토큰 신설 기준(§4) 미충족으로
P5 패리티 요구 시 재평가합니다.
상태#
비제어 컴포넌트입니다 — defaultValue로 시작하고 선택 완료 시 onChange로
알립니다. 열림/닫힘도 내부 소유: 트리거 클릭으로 열고, 선택 완료·Escape·외부
클릭으로 닫힙니다(Escape·선택 완료는 트리거로 포커스 복원). 트리거의
Hover/Focus/Disabled는 CSS 의사클래스로 표현되며, 열림 동안
aria-expanded="true"가 유지됩니다.
모바일#
DK 모바일 재활용 표준입니다 — mobile을 켜면 팝업 대신
BottomSheet 안에 Calendar가 렌더되고,
footer의 취소/확인 버튼으로 마무리합니다. 날짜를 골라도 즉시 확정되지 않는
임시 선택이며, 확인을 눌러야 onChange가 발화하고 시트가 닫힙니다.
취소·Escape·배경 닫기는 임시 선택을 폐기합니다(범위 모드도 동일 — 시작·끝을
고른 뒤 확인으로 확정). 시트 제목은 mobileTitle(기본 placeholder), 버튼
라벨은 confirmLabel/cancelLabel로 바꿉니다. 라이브 데모는 데스크톱
뷰포트라 코드로 안내합니다.
<DatePicker
mobile
mobileTitle="기간 선택"
mode="range"
confirmLabel="적용"
onChange={(range) => applyPeriod(range)}
/>
Props#
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
mode | 'single' | 'range' | 'single' | 선택 단위 — 단일 날짜 또는 시작·끝 범위 |
defaultValue | Date | DateRange | — | 초기 선택값 (비제어) — range면 { start, end } |
onChange | (value: Date | DateRange) => void | — | 선택 완료 콜백 — 단일은 클릭, 범위는 끝 선택 시점 |
placeholder | string | '날짜 선택' | 값이 없을 때 트리거 텍스트 + 팝업 dialog의 aria-label |
variant | 'docked' | 'modal' | 'modalInput' | 'docked' | 데스크톱 표시 — docked: 앵커 팝업(즉시) · modal: 중앙 모달(확인) · modalInput: 모달+직접 입력(단일). compact는 BottomSheet 우선 |
mobile | boolean | false | true면 BottomSheet에서 임시 선택 → 확인으로 확정합니다 (DK 모바일 표준) |
mobileTitle | string | — | 시트 제목 — 기본 placeholder |
confirmLabel | string | '확인' | mobile 확정 버튼 라벨 |
cancelLabel | string | '취소' | mobile 취소 버튼 라벨 |
트리거는 네이티브 button 속성을 확장합니다 — disabled, className 등이
그대로 전달됩니다 (onChange/value/defaultValue는 컴포넌트 시그니처로 대체).
접근성#
| 계약 | 구현 |
|---|---|
| 트리거 | aria-haspopup="dialog" + aria-expanded |
| 팝업 | role="dialog" — placeholder가 접근 가능한 이름 |
| 달력 | role="grid" APG 패턴 — roving tabindex(포커스 셀만 0) |
| 키보드 이동 | 화살표(일) · Home/End(주 시작·끝) · PageUp/Down(월) |
| 선택 | Enter·Space — 비활성(min/max/disabled) 날짜는 선택·포커스 불가 |
| 오늘 | aria-current="date" |
| 닫기 | Escape(트리거 포커스 복원) · 외부 클릭 · 선택 완료 |
| 월 라벨 | Intl ko-KR "2026년 6월" — aria-live="polite" 갱신 안내 |
| 모션 | 팝업 등장은 opacity/transform만 + prefers-reduced-motion 존중 |
토큰#
component 토큰 없이 semantic(+input 어휘)을 직접 소비합니다(신설 기준 §4 미충족).
| 속성 | 토큰 |
|---|---|
| 트리거 | input.height · input.bg · input.border · input.radius · input.fg · input.placeholder |
| 트리거 포커스/열림 | color.focus-ring(링) · input.border-focus |
| 팝업 | color.surface-raised · radius.md · shadow.lg · z.dropdown |
| 달력 선택/범위 | color.primary · color.on-primary · color.primary-subtle · color.primary-text |
| 달력 hover/눌림 | color.surface-hover / color.surface-pressed (선택·범위 면은 안 덮음) |
| 모션 | duration.fast · 팝업 등장 ease.emphasized-decelerate / ease.standard |