Passer au contenu principal

DatePicker

Implemented

Native <input type="date"> wrapper with ISO value normalization — accepts string | Date, emits yyyy-mm-dd strings. The browser owns the calendar popup, keyboard navigation and locale formatting.

Categorymolecules
Sourcepatterns/molecules/datepicker.html
AI HintStandalone labelled date field — always pass label (the aria-label fallback is English). Dense form rows already using toLocalInput/fromLocalInput helpers may use Input type='date'; never ship a bare styled <input type='date'>. There is no custom calendar and no time part.

Native `<input type="date">` wrapper for standalone labelled date fields. The browser owns the calendar popup, keyboard navigation and locale rendering; the molecule adds ISO value normalization (`string | Date` in, `yyyy-mm-dd` out), label/error/hint wiring, size presets and combobox semantics (`role="combobox"` + `aria-haspopup="dialog"`).

DatePicker

Input molecule — Form & Choice (Family 3)

Accepts an ISO `yyyy-mm-dd` string or a Date, normalizes to `yyyy-mm-dd` using local parts (no UTC day-shift), and emits ISO strings from `onChange`. The visible calendar popup is the browser UA widget — the DS never renders a custom grid.

Labelled field with value + min/max

min/max are ISO yyyy-mm-dd; the browser renders the value and its calendar in the viewer’s locale.

Sizes — sm (h-8) / md (h-10, default) / lg (h-11)

md is h-10 to match the Input and Select atoms — the DS standard form-control height.

Error state

On-sale date must be before the event date.

Sets aria-invalid and announces the message viaaria-describedby.

Hint state

Times are shown in the venue's local timezone.

Error + hint — error wins (mutual exclusion)

Refund deadline cannot be after the event date.

With both set, only the error renders — the hint paragraph is suppressed, the same error-wins rule FormField documents. Known wiring defect (fix tracked in the component hardening pass): aria-describedby still references the suppressed hint id, and the visible label is not yet programmatically associated (htmlFor). Pass an explicit id per instance until that pass lands.

Usage

Standalone labelled date field — e.g. a sales-filter From/To pair. The value contract is ISO yyyy-mm-dd in and out; pass max (or min) to guard a range against another field.

import { DatePicker } from '@epasslive/design-system'

export function SalesFilter({ fromIso, toIso, setFromIso }: {
  fromIso: string
  toIso: string
  setFromIso: (iso: string) => void
}) {
  return (
    <DatePicker
      id="sales-from"
      label="From"
      value={fromIso}
      onChange={setFromIso}
      max={toIso}
    />
  )
}

DatePicker vs Input type="date"

  • Standalone labelled date field (filters, settings, simple forms) → DatePicker: label/error/hint wiring, ISO normalization (accepts string | Date, emits yyyy-mm-dd), size presets, combobox semantics.
  • Dense composite form row already converting values via toLocalInput/fromLocalInput helpers → Input type="date" is acceptable (the helpers already speak yyyy-mm-dd).
  • A bare styled <input type="date"> is always a violation — never introduce a third pattern.

Accessibility

  • The input carries role="combobox" + aria-haspopup="dialog", mirroring the browser’s native date-input exposure; the popup itself is the browser UA widget — keyboard navigation and popup semantics are delegated to it.
  • aria-invalid when error is set; error and hint messages are wired through aria-describedby.
  • Known wiring defects (documented; fix tracked in the component hardening pass): the visible label lacks htmlFor; the id fallback is the literal "datepicker" so multiple unlabeled pickers collide; when error suppresses hint, aria-describedby still references the missing hint id. Until fixed, pass an explicit unique id and always pass label (or ariaLabel).

Props

Prop
Type
Default
Description
value
string | Date
—

Controlled value. ISO `yyyy-mm-dd` string or Date — both normalized to `yyyy-mm-dd` using local parts. Invalid dates render empty.

onChange
(value: string) => void
—

Change handler. Always receives the ISO `yyyy-mm-dd` string — never a Date.

min
string
—

Minimum selectable date (ISO `yyyy-mm-dd`). Passed to the native input; the browser blocks earlier dates.

max
string
—

Maximum selectable date (ISO `yyyy-mm-dd`).

size
'sm' | 'md' | 'lg'
md

Height preset: sm h-8, md h-10 (matches Input/Select atoms), lg h-11.

label
string
—

Visible label above the field. Required-in-practice — without label or ariaLabel the announced name falls back to hardcoded English "Select date".

ariaLabel
string
—

Accessible name when no visible label fits. Takes precedence over label.

error
string
—

Validation error message. Sets aria-invalid, renders the message in error color, and suppresses the hint.

hint
string
—

Helper text below the field. Suppressed when error is set — error wins (same rule as FormField).

disabled
boolean
false

Disables the field (cursor-not-allowed, 50% opacity).

id
string
—

Explicit input id. Pass a unique one per instance — the error/hint ids derive from it (collision fix pending).

className
string
—

Classes appended to the root wrapper (wrapper is w-full).

placeholder
string
—

No-op on `<input type="date">` in modern browsers — the UA paints its own locale placeholder.

locale
string
—

DEPRECATED — ignored. The native input localizes from its ISO value in the viewer’s browser locale. Kept for API compatibility.

Props

Preview

DatePicker

Prop
Type
Required
Default
Description
value
string | Date
Optional
—

Selected date; normalized to a yyyy-mm-dd string on change.

onChange
(value: string) => void
Optional
—

Emits the ISO yyyy-mm-dd string (uncontrolled when omitted).

disabled
boolean
Optional
false

Disabled state

label
string
Optional
—

Field label. Required in practice — the accessible-name fallback is hardcoded English 'Select date'.

error
string
Optional
—

Validation error message; suppresses the hint when set.

min
string
Optional
—

Minimum allowed value (ISO 8601 date).

max
string
Optional
—

Maximum allowed value (ISO 8601 date).

size
DatePickerSize
Optional
'md'

Visual size preset.

className
string
Optional
—

Additional CSS classes on the wrapper.

ariaLabel
string
Optional
label, then 'Select date'

Accessible name — defaults to label, then English 'Select date'.

id
string
Optional
'datepicker'

Stable input id; also keys the hint/error description ids.

placeholder
string
Optional
—

No-op in modern browsers — native date inputs ignore placeholder text.

hint
string
Optional
—

Helper text below the field; hidden when error is set.

locale
string
Optional
—

Deprecated — ignored. The native input localizes in the browser locale.

Quick use:<DatePicker />

Accessibility

ARIA rolecombobox

ARIA

1 rule

WAI-ARIA APG

Input carries aria-haspopup='dialog' (the UA calendar popup) and the DS-rendered element is role='combobox'

Keyboard

1 rule

WCAG 2.1.1

Keyboard navigation and popup semantics are delegated to the browser UA date widget

validation

1 rule

ISO 8601

min/max must be valid ISO 8601 dates

Usage

Import

tsxSnippet
import { DatePicker } from "@epasslive/design-system"

Example 1

tsxSnippet
<DatePicker label="Event date" value={isoDate} min={todayIso} />

Example 2

tsxSnippet
<DatePicker label="Doors open" size="sm" hint="Local venue time" />

Anti-patterns

— Do not pass Date objects to min/max — they are ISO strings here

— Do not expect a custom calendar or time picker — the browser UA widget owns the popup

Design System v4.1.0 | ePassLive