Skip to main content

DateRangePicker

Implemented

Controlled 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.

Categorymolecules
Sourcepatterns/molecules/daterangepicker.html
AI HintAdoptable for form from/to pairs (e.g. EventForm multi-day branch). For dashboard range filters prefer StatisticsDateFilter (6 call sites, presets). For visual month-grid use Calendar. Not a calendar component — native UA widget owns the popup.

Controlled 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 via aria-labelledby; without a label it falls back to ariaLabel (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 from startLabel/endLabel (native date inputs have no visible text slot — DatePicker-style naming).
  • With error set: both inputs carry aria-invalid and aria-describedby pointing 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-scheme declaration, so the native date-input chrome stays light in dark theme — affects DatePicker too (MOL57-F3).

Props

Prop
Type
Default
Description
startValue
string
—

Controlled ISO `yyyy-mm-dd` value of the start (from) input — the only format a native date input accepts.

endValue
string
—

Controlled ISO `yyyy-mm-dd` value of the end (to) input.

onChange
(range: { start?: string; end?: string }) => void
—

Fires with `{ start, end }` when either input changes — the freshly edited value plus the current counterpart. Cross-field validation is the consumer’s job.

disabled
boolean
false

Disables both inputs (cursor-not-allowed, 50% opacity).

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

Height preset: sm h-8, md h-10, lg h-11 — identical to the DatePicker SIZE map (Input/Select parity restored).

className
string
—

Classes merged onto the root wrapper.

ariaLabel
string
—

Accessible name for the group when no visible `label` is supplied. Default "Select date range".

id
string
—

Base id for the group and its parts (`-start`, `-end`, `-label`, `-error`); defaults to `daterange-<useId>`.

label
string
—

Visible caption above the pair; wired to the start input via htmlFor/id and names the group via aria-labelledby.

error
string
—

Error 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.

startLabel
string
"Start date"

Accessible name for the start input (native date inputs have no visible text slot — DatePicker-style naming).

endLabel
string
"End date"

Accessible name for the end input.

Props

Preview

DateRangePicker

Prop
Type
Required
Default
Description
startValue
string
Optional
—

ISO 8601 yyyy-mm-dd value for the start input.

endValue
string
Optional
—

ISO 8601 yyyy-mm-dd value for the end input.

onChange
(range: { start?: string; end?: string }) => void
Optional
—

Fires with { start, end } when either input changes. No cross-field validation — consumer decides.

disabled
boolean
Optional
—

Disables both inputs.

size
DateRangePickerSize
Optional
'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).

className
string
Optional
—

Tailwind classes applied to the outer wrapper.

ariaLabel
string
Optional
—

Accessible name for the group when no visible label is rendered. Falls back to 'Select date range'.

id
string
Optional
auto-generated

Base id for the group; inputs derive `${id}-start` / `${id}-end`, error derives `${id}-error`.

label
string
Optional
—

Visible label for the group. htmlFor → start input; group aria-labelledby → label id.

error
string
Optional
—

Error message rendered below the inputs; both inputs get aria-invalid + aria-describedby → error id.

startLabel
string
Optional
'Start date'

Accessible name for the start input. Caller-supplied for i18n.

endLabel
string
Optional
'End date'

Accessible name for the end input. Caller-supplied for i18n.

Quick use:<DateRangePicker />

Accessibility

ARIA rolegroup

Type

1 rule

NASA Rule 7

Range values are ISO 8601 yyyy-mm-dd strings (NASA Rule 7)

Usage

Import

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

Example 1

tsxSnippet
<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

Design System v4.1.0 | ePassLive