DateRangePicker
ImplementedControlled pair of native date inputs (start → end) for range capture. Live onChange emits { start, end } when either input changes. Container is role='group' labelled by the visible label (htmlFor → start input) or ariaLabel fallback; each input carries its own accessible name from startLabel/endLabel. Error is aria-describedby-wired + aria-invalid on inputs. Not a calendar — for dashboard range filters use StatisticsDateFilter; for visual month-grid use Calendar.
patterns/molecules/daterangepicker.htmlControlled pair of native date inputs (start → end) for range capture; live onChange, per-field accessible names, error wiring. No calendar view or presets — the browser UA paints the date chrome.
DateRangePicker
Input molecule — Form & Choice (Family 3)
Two native `<input type="date">` fields separated by a decorative "→" span. No calendar view, presets or keyboard range navigation ships — any calendar chrome is the browser UA. Both inputs are editable; onChange fires live with { start, end }.
Controlled ISO yyyy-mm-dd values — both inputs are editable and onChange fires with { start, end } on every keystroke-level change.
DateRangePicker — disabled
Input molecule — Form & Choice (Family 3)
Disabled state: both inputs non-focusable, cursor-not-allowed, 50% opacity.
DateRangePicker — error
Input molecule — Form & Choice (Family 3)
Error state: red message below the pair, error border painted on each input (DatePicker family treatment), aria-invalid on both inputs and the message wired via aria-describedby.
End date precedes start
Usage
Controlled from/to pair: pass ISO yyyy-mm-dd values via startValue/endValue and receive every edit through onChange ({ start, end }). The molecule does no cross-field validation — surface it via the error prop. Accessible names for the two inputs come from startLabel/endLabel.
import { DateRangePicker } from '@epasslive/design-system'
// Live range capture with localized accessible names.
<DateRangePicker
label="Sales window"
startValue={fromIso}
endValue={toIso}
startLabel="Date de début"
endLabel="Date de fin"
onChange={setRange}
/>When to use
- Single date in a form → DatePicker (ISO normalization, min/max, hint, error wiring).
- From/To pair in a form → DateRangePicker — live
onChange, per-field accessible names, error wiring; shares the DatePicker SIZE map (md = h-10) so it aligns with Input/Select rows. - Date-range filtering in dashboards → StatisticsDateFilter — the shipped range-filter surface (no docs page yet — known gap, tracked separately).
- Custom inline month grid you compose yourself → Calendar.
- Not a calendar component — no two-month view, presets or range-navigation keyboard shortcuts; the browser paints its own date chrome around the native inputs. Zero current consumers.
Localization
The native inputs render their values and calendars in the viewer’s browser locale — no DS i18n keys. Every authored string is caller-supplied: label (visible caption), ariaLabel (group-name fallback) and startLabel/endLabel (per-input accessible names). Defaults are English (“Start date” / “End date”; group fallback “Select date range”) — override them on /fr routes.
Accessibility
- Container is
role="group"named by the visible label viaaria-labelledby; without a label it falls back toariaLabel(default “Select date range”). No combobox/popup roles are emitted. - The visible label is wired to the start input via
htmlFor/id; each input carries an accessible name fromstartLabel/endLabel(native date inputs have no visible text slot — DatePicker-style naming). - With
errorset: both inputs carryaria-invalidandaria-describedbypointing at the error paragraph; the error border is painted on each input (DatePicker family treatment). - Color contrast passes WCAG AA in both themes (text tokens ≥4.5:1; error color passes on page background — can fail on dark card surfaces, a family-wide caveat shared with DatePicker, MOL57-E6).
- Known family-wide leftover: no
color-schemedeclaration, so the native date-input chrome stays light in dark theme — affects DatePicker too (MOL57-F3).
Props
startValuestringControlled ISO `yyyy-mm-dd` value of the start (from) input — the only format a native date input accepts.
endValuestringControlled ISO `yyyy-mm-dd` value of the end (to) input.
onChange(range: { start?: string; end?: string }) => voidFires with `{ start, end }` when either input changes — the freshly edited value plus the current counterpart. Cross-field validation is the consumer’s job.
disabledbooleanfalseDisables both inputs (cursor-not-allowed, 50% opacity).
size'sm' | 'md' | 'lg'mdHeight preset: sm h-8, md h-10, lg h-11 — identical to the DatePicker SIZE map (Input/Select parity restored).
classNamestringClasses merged onto the root wrapper.
ariaLabelstringAccessible name for the group when no visible `label` is supplied. Default "Select date range".
idstringBase id for the group and its parts (`-start`, `-end`, `-label`, `-error`); defaults to `daterange-<useId>`.
labelstringVisible caption above the pair; wired to the start input via htmlFor/id and names the group via aria-labelledby.
errorstringError text below the pair in error color; error border on both inputs, announced via aria-describedby with aria-invalid on each input. No helperText/hint prop exists — no interplay applies.
startLabelstring"Start date"Accessible name for the start input (native date inputs have no visible text slot — DatePicker-style naming).
endLabelstring"End date"Accessible name for the end input.
Props
DateRangePicker
startValuestring—ISO 8601 yyyy-mm-dd value for the start input.
endValuestring—ISO 8601 yyyy-mm-dd value for the end input.
onChange(range: { start?: string; end?: string }) => void—Fires with { start, end } when either input changes. No cross-field validation — consumer decides.
disabledboolean—Disables both inputs.
sizeDateRangePickerSize'md'Visual size preset. sm h-8 text-ep-xs / md h-10 text-ep-sm / lg h-11 text-ep-base (DatePicker family parity).
classNamestring—Tailwind classes applied to the outer wrapper.
ariaLabelstring—Accessible name for the group when no visible label is rendered. Falls back to 'Select date range'.
idstringauto-generatedBase id for the group; inputs derive `${id}-start` / `${id}-end`, error derives `${id}-error`.
labelstring—Visible label for the group. htmlFor → start input; group aria-labelledby → label id.
errorstring—Error message rendered below the inputs; both inputs get aria-invalid + aria-describedby → error id.
startLabelstring'Start date'Accessible name for the start input. Caller-supplied for i18n.
endLabelstring'End date'Accessible name for the end input. Caller-supplied for i18n.
<DateRangePicker />Accessibility
Type
1 ruleUsage
Import
import { DateRangePicker } from "@epasslive/design-system"Example 1
<DateRangePicker label="Sales window" startValue={fromIso} endValue={toIso} onChange={setRange} />Anti-patterns
— Do not use for dashboard range filters — StatisticsDateFilter owns that niche
— Do not hardcode data in molecules