Date Range Picker
Dual-calendar picker for selecting a start/end date range with presets and full keyboard navigation.
<arc-date-range-picker> Overview
>
DateRangePicker extends the single DatePicker model to a start/end range. The read-only input displays the committed range in a compact human-readable form (e.g. "Jan 5 – Feb 2, 2026") while the `start` and `end` properties hold ISO strings (YYYY-MM-DD). The derived `value` getter returns the two dates joined as an ISO 8601 interval ("2026-01-05/2026-02-02") — the same string is submitted with the parent form via ElementInternals, and `form.reset()` restores the initial range.
Selection follows the familiar two-click model: the first click (or Enter on a focused day) sets the start date, and while the end is pending, hovering or keyboard-focusing another day previews the resulting range with a soft highlight. The second click commits the range (automatically swapping if the second date is earlier), fires `arc-change` with `{ start, end }`, and closes the popup. A third click begins a new range. The popup shows a configurable number of month panels (two by default) that sit side by side and wrap into a vertical stack when horizontal space runs out, with shared previous/next month navigation.
An optional `presets` array renders a left rail of one-click quick ranges such as "Last 7 days" — each entry is `{ label, days }` and resolves to a range ending today. The day grids use a roving tabindex with Arrow/Home/End/PageUp/PageDown navigation that pages months automatically at the edges, endpoint days carry ", range start"/", range end" label suffixes, and a polite live region announces selection progress ("Start date set. Choose end date.") for screen reader users. Escape closes the popup and restores focus to the input.Guidelines
When to use
- Provide presets for the ranges users pick most often (7/30/90 days) to reduce interaction cost
- Use the start and end ISO strings (or the interval value) for APIs — never parse the display text
- Set min and max when only a bounded window is meaningful (e.g. report data availability)
- Listen to arc-change to react to committed ranges; it fires only when both dates are set
- Set months={1} in narrow layouts like popover filters where two panels would overflow
When not to use
- Do not use it for a single date — use DatePicker instead
- Do not treat the first click as a committed value; arc-change fires only after the end date is chosen
- Do not hand-build preset buttons outside the component when the presets prop covers the need
- Do not place it inside an overflow: hidden container, as the popup will be clipped
- Do not set min greater than max, which disables every day in the calendar
Features
- Two-click range selection with automatic start/end swap when picked out of order
- Live range preview highlight while hovering or keyboard-focusing the end date
- ISO 8601 interval value ("start/end") submitted through native form association
- Configurable number of side-by-side month panels (default 2) that stack when space is tight
- Optional preset rail for one-click quick ranges like "Last 7 days"
- Min/max bounds that disable out-of-range days
- Roving-tabindex day grid with Arrow, Home/End, and PageUp/PageDown month paging
- Polite live-region announcements of selection progress for assistive technology
- Escape and outside-click dismissal with focus restoration to the input
Preview
Usage
This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.
<arc-date-range-picker
label="Reporting Period"
presets='[{"label":"Last 7 days","days":7},{"label":"Last 30 days","days":30}]'
></arc-date-range-picker> import { DateRangePicker } from '@arclux/arc-ui-react';
export default function Example() {
return (
<DateRangePicker
label="Reporting Period"
presets={[{ label: 'Last 7 days', days: 7 }, { label: 'Last 30 days', days: 30 }]}
onArcChange={(e) => console.log(e.detail.start, e.detail.end)}
></DateRangePicker>
);
} <script setup>
import { DateRangePicker } from '@arclux/arc-ui-vue';
const presets = [{ label: 'Last 7 days', days: 7 }, { label: 'Last 30 days', days: 30 }];
</script>
<template>
<DateRangePicker label="Reporting Period" :presets="presets" />
</template> <script>
import { DateRangePicker } from '@arclux/arc-ui-svelte';
const presets = [{ label: 'Last 7 days', days: 7 }, { label: 'Last 30 days', days: 30 }];
</script>
<DateRangePicker label="Reporting Period" presets={presets} /> import { Component } from '@angular/core';
import { DateRangePicker } from '@arclux/arc-ui-angular';
@Component({
imports: [DateRangePicker],
template: `
<arc-date-range-picker label="Reporting Period" [presets]="presets"></arc-date-range-picker>
`,
})
export class MyComponent {
presets = [{ label: 'Last 7 days', days: 7 }, { label: 'Last 30 days', days: 30 }];
} import { DateRangePicker } from '@arclux/arc-ui-solid';
export default function Example() {
return (
<DateRangePicker
label="Reporting Period"
presets={[{ label: 'Last 7 days', days: 7 }]} />
);
} import { DateRangePicker } from '@arclux/arc-ui-preact';
export default function Example() {
return (
<DateRangePicker
label="Reporting Period"
presets={[{ label: 'Last 7 days', days: 7 }]} />
);
} API
-
autoValidatesbooleanfalse - Runs its own constraint logic — owns the whole validity flag set.
-
valuestring - Read-derived ISO 8601 interval ("start/end") when both dates are set, otherwise an empty string. This is the value submitted with forms. Assigning "start/end" sets both dates.
-
size'sm' | 'md' | 'lg''md' - Control size. `md` is the default; `sm` and `lg` scale the field padding.
-
localestring'' - BCP 47 tag used for month and weekday names. Defaults to the document's `lang`, then the browser's language.
-
first-day-of-weeknumber0 - Which day the week starts on, 1 = Monday … 7 = Sunday. Defaults to the locale's own convention.
-
startstring'' - Range start date as an ISO string (YYYY-MM-DD). Empty when unset. Set both start and end to pre-select a range.
-
endstring'' - Range end date as an ISO string (YYYY-MM-DD). Empty when unset or while an end date is pending.
-
namestring'' - Form field name used when the interval value is submitted with a form.
-
minstring'' - Minimum selectable date as an ISO string. Earlier days are dimmed and non-interactive.
-
maxstring'' - Maximum selectable date as an ISO string. Later days are dimmed and non-interactive.
-
monthsnumber2 - Number of month panels shown in the popup. Panels sit side by side and stack vertically when the popup is too narrow.
-
presetsArray<{label:string,days:number}>[] - Quick ranges rendered as a left rail. Each preset selects the last N days ending today and closes the popup. Hidden when empty.
-
placeholderstring'Select date range' - Placeholder text shown in the input when no range is selected.
-
disabledbooleanfalse - Disables the picker, reducing opacity and preventing the popup from opening.
-
requiredbooleanfalse - Marks the control invalid (valueMissing) until a complete range is selected.
-
labelstring'' - Label text rendered above the input in uppercase accent font styling.
-
openbooleanfalse - Whether the calendar dropdown is visible. Reflected so it can be opened programmatically or styled from CSS.
-
formAssociatedbooleantrue -
propertiesobject{ required: { type: Boolean, reflect: true }, readonly: { type: Boolean, reflect: true }, } - Lit merges static properties up the prototype chain, so every consumer gets these without declaring them. `required` participates in constraint validation below; `readonly` reflects for styling and is enforced by each component's interaction handlers (the mixin can't know which gestures mutate state).
-
form -
validity -
validationMessage -
readonlybooleanfalse
Events
-
arc-changedetail: { start: string, end: string } - Fired when a complete range is committed (second day clicked or preset applied). detail: { start, end }
See Also
- Date Picker Calendar-based date picker with keyboard navigation.
- Calendar Interactive month-view calendar grid for date selection with min/max constraints, keyboard navigation, and today highlighting.
- Input Versatile form control supporting single-line text, email, password, and multiline textarea modes with built-in label, placeholder, and validation states. Pairs with Form for complete data-entry workflows.