components / rangedatepicker

RangeDatePicker

Two-month date-range field with start/end selection, blocked-date handling, and popover calendar containment.

Overview

What it is

This section explains the intent of the component before the implementation details.

  • RangeDatePicker extends the single-date field into a deliberate start/end selection workflow.
  • It is built for booking windows, reporting ranges, and any workflow where two linked dates are the primary decision.

Aliases

date range picker

Decision guide

How to choose it

Use these notes to decide quickly whether this is the right DK component for the job.

Decision guide

  • Choose RangeDatePicker when the interaction should feel deliberate, documented, and reusable across product surfaces.
  • If the scenario is more specialized than the current RangeDatePicker contract, prefer a simpler component or build a dedicated workflow on top of the lower-level DK primitives.

Do

  • Keep the RangeDatePicker label, value, or status language direct enough to scan quickly.
  • Use the documented size and state recipes instead of inventing one-off variants in product code.

Do not

  • Do not hide the main purpose of RangeDatePicker behind decorative copy or overloaded surface treatments.
  • Do not stretch RangeDatePicker into a workflow it was not designed to handle just because the visuals are close.

Usage

When to use

Prefer these situations when choosing this component.

  • Use RangeDatePicker when the user is choosing a bounded start and end date as one decision.
  • Use min, max, and disabledDates when the valid window is constrained by business logic.

Usage

When not to use

These patterns are better served by a different component or a simpler surface.

  • Do not use RangeDatePicker for one-off date fields; use DatePicker.
  • Do not use it for date-plus-time or preset-heavy reporting workflows in this v1 contract.

Anti-patterns

  • Using RangeDatePicker as a generic layout container instead of a purpose-built interaction or content surface.
  • Forking the documented recipe in product code instead of extending the shared DK contract.

Migration notes

  • Migrate legacy rangedatepicker usage by mapping existing variants and states onto the documented DK props before porting any custom styling.
  • Treat the docs examples as the reference contract for accessibility and event behavior during adoption.

Anatomy

Structural parts

The anatomy explains which pieces matter to the recipe and accessibility model.

field button

trigger

Shows the current range summary and opens the two-month calendar.

calendar popover

surface

Contains the paired month grids and navigation controls.

calendar day

day

Selectable day cell supporting start, end, and in-range styling.

API

Public interface

The docs contract distinguishes props, DOM events, and slots so integration behavior is explicit.

Props

NameTypeDefaultDescription
labelstringVisible label used for the field or control.
descriptionstringSupporting helper copy rendered below or beside the main label.
errorstringValidation or problem copy rendered in the error slot when present.
placeholderstringPlaceholder text shown before a user makes a selection or enters text.
requiredbooleanfalseMarks the field or control as required for form semantics and docs examples.
disabledbooleanfalseDisables interaction and exposes the correct non-interactive semantics for the control.
namestringForm field name used for native form submission.
idstringOptional id used to bind external labels or descriptions to the component.
size'sm' | 'md' | 'lg''md'Chooses the compiled size recipe for the component.
value{ start?: string; end?: string }Controlled ISO date-only range value.
minstringEarliest selectable ISO date.
maxstringLatest selectable ISO date.
disabledDatesstring[]Explicit ISO dates that cannot be selected.
weekStartsOn0 | 10First day of the week for the month grid.
openbooleanfalseControlled popover open state.
onOpenChange(detail: { open: boolean }) => voidCallback fired when the calendar opens or closes.
onChange(detail: { value: { start?: string; end?: string } }) => voidCallback fired when the range changes.
themeThemeContractOverrides the compiled DK theme used to resolve tokens and recipes for this component.

Events

NamePayloadDescription
openchange{ open: boolean }Fires when an overlay-style component opens or closes through user interaction.
change{ value: { start?: string; end?: string } }Fires when the range changes.

Slots

NameDescription
defaultRangeDatePicker is prop-driven in v1 and does not expose custom day or panel slots.

Recipes

Variants, sizes, and states

These notes summarize the intended recipe surface rather than exposing raw implementation detail first.

Variants

  • default: Two linked month grids with start/end range emphasis.

Sizes

  • sm: Compact range field
  • md: Default range field
  • lg: Larger touch-oriented range field

States

  • closed: Calendar hidden
  • open: Calendar visible
  • partial: Start date chosen but end date still pending
  • complete: Start and end dates selected

Accessibility

Accessibility contract

This is the behavior the component promises to assistive tech and keyboard users today.

Semantics

  • Uses standard field labeling through visible labels, descriptions, and error text.
  • Exposes disabled and required semantics through the underlying form control.
  • Uses the DK field shell plus dialog-like calendar semantics for the popover surface.

Keyboard

  • Supports native focus and text entry behavior.
  • Arrow keys move day focus, Home/End stay inside the week, PageUp/PageDown shift months, and Escape closes the surface.

Screen readers

  • Connects visible label, helper text, and error text to the native control.
  • Start, end, blocked, and in-range states remain understandable without relying on color alone.

Checklist

  • Verify the visible label or title still produces a clear accessible name.
  • Verify focus remains obvious in every supported state and size.
  • Verify disabled, selected, and error states do not rely on color alone.

Implementation

Tokens and slot vars

This section shows the representative compiled slot variables that the runtime consumes for the selected design system.

root

--dk-range-gap
0.5rem
--dk-range-offset
8px

label

--dk-field-label-color
#16181c
--dk-field-label-size
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
--dk-field-label-weight
600

trigger

--dk-range-trigger-bg
#f2f5fb
--dk-range-trigger-fg
#16181c
--dk-range-trigger-border
#95989d
--dk-range-trigger-radius
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)
--dk-range-trigger-block-size
48px
--dk-range-trigger-inline-padding
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
--dk-range-trigger-font-size
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)

description

--dk-field-description-color
#16181c
--dk-field-description-size
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

error

--dk-field-error-color
#cc272e
--dk-field-error-size
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

surface

--dk-range-surface-bg
#f2f5fb
--dk-range-surface-fg
#16181c
--dk-range-surface-border
#95989d
--dk-range-surface-radius
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
--dk-range-surface-shadow
0 10px 15px rgba(0, 0, 0, 0.06), 0 4px 6px rgba(0, 0, 0, 0.04)
--dk-range-surface-width
720px
--dk-range-surface-padding
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)

caption

--dk-range-caption-color
#16181c
--dk-range-caption-size
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
--dk-range-caption-weight
700

navButton

--dk-range-nav-bg
#e5e8ed
--dk-range-nav-fg
#16181c
--dk-range-nav-size
36px

weekday

--dk-range-weekday-color
#16181c
--dk-range-weekday-size
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

day

--dk-range-day-bg
#f2f5fb
--dk-range-day-fg
#16181c
--dk-range-day-selected-bg
#2072e4
--dk-range-day-selected-fg
#ffffff
--dk-range-day-between-bg
#d7e9ff
--dk-range-day-between-fg
#000f4d
--dk-range-day-disabled-fg
#16181c
--dk-range-day-outside-fg
#95989d
--dk-range-day-radius
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)
--dk-range-day-target
44px
--dk-range-day-size
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

Implementation notes

  • RangeDatePicker stays date-only in v1 so range semantics remain predictable across time zones and reporting contexts.

Implementation checklist

  • Use the public component API first and only drop to lower-level primitives when the component contract genuinely does not fit.
  • Keep theme overrides token-driven so proofs and recipes stay meaningful.
  • Verify the shipped examples and proof fixtures still represent the real product scenario you are implementing.

Examples

Copy-paste examples

Each example is intentionally practical, grouped by starter, common pattern, and edge-case coverage.

Examples
3
Depth
expanded

Starter

1 example

starter

Starter: booking window

A simple date-range field with DK labeling and helper copy.

Copy snippet

<RangeDatePicker label="Stay window" description="Choose the booking range." />

Common patterns

1 example

common

Common: constrained range

Use min, max, and blocked dates to keep the calendar honest.

Copy snippet

<RangeDatePicker label="Freeze window" min="2026-04-10" max="2026-04-20" disabledDates={['2026-04-16']} />

Edge cases

1 example

edge

Edge: controlled range + popover state

Control the range and the surface when app workflow owns selection state.

Copy snippet

<RangeDatePicker value={range} open={isOpen} onOpenChange={(detail) => isOpen = detail.open} onChange={(detail) => range = detail.value} />

Verification

Curated proof fixtures

Proofs stay visible in the docs so the system shows what it can guarantee, not just what it can render.

default-md (size=sm)

pass

size=sm

  • Contrast: 98.5 Lc, 76.6 Lc
  • Target: 44px, 44px
  • Layout: 0 / 560, 720
  • Surface: 720x360

default-md (size=md)

pass

size=md

  • Contrast: 98.5 Lc, 76.6 Lc
  • Target: 48px, 44px
  • Layout: 0 / 560, 720
  • Surface: 720x360

lg

pass

size=lg

  • Contrast: 98.5 Lc, 76.6 Lc
  • Target: 52px, 44px
  • Layout: 0 / 560, 720
  • Surface: 720x360