Date range picker
A date range picker is a form field for a period, such as a stay or a report window. People type the start and end or pick them in a two-month calendar.
<tk-date-range-picker label="Stay dates" helper="For example, 27 3 2026" start-name="check-in" end-name="check-out" min="2026-01-01" max="2026-12-31"></tk-date-range-picker>
The range picker brings its own label, helper, and error; don't put it in a
Anatomy
| Part | What it is |
|---|---|
tk-date-range-picker |
the element |
| label | names the pair, such as "Stay dates"; an asterisk when required |
| helper | optional hint under the label |
field part |
the bordered box with both inputs |
input part |
the start and end inputs, named "Start date" and "End date"; always typable |
| separator | a "–" between the inputs |
| clear button | "Clear dates"; shows when there is a value |
| calendar button | opens the calendar |
| error | the message under the box |
popover part |
the panel with the presets and a two-month |
Each date has its own input and its own name, so screen readers can tell start from end. Don't replace it with one "start – end" text field.
Sizes
<tk-date-range-picker label="Report period" size="large" start="2026-03-01" end="2026-03-31"></tk-date-range-picker>
size |
Height | Padding-inline | Value text |
|---|---|---|---|
medium (default) |
32px | space-8 / space-4 |
Body Small (14/20) |
large |
36px | space-10 / space-4 |
Body Medium (16/24) |
The calendar is the same size at every field size.
States
<tk-date-range-picker label="Billing period" disabled start="2026-02-01" end="2026-02-28"></tk-date-range-picker> <tk-date-range-picker label="Trip dates" required error="Enter a start and end date"></tk-date-range-picker>
The box doesn't change on hover. Focus in either input colors the box's border and thickens it inward. An error keeps the border red, even with focus.
readonly: people can focus and copy the dates; the buttons don't work.disabled: nothing works, and the dates are left out of the form.error: your message, shown under the box. It replaces any message the picker writes itself.
Picking a range
- The calendar button, or
Downin either input, opens the calendar on the start date, or today. - The first pick sets the start and keeps the calendar open. Hovering shows the span.
- The second pick sets the end, fires
change, and closes the calendar. - A pick before the start moves the start.
Esc, a click outside, orTabcloses the calendar and drops a half-picked range. The previous range stays.- Each input can be typed in at any time, with the same formats as the
Date picker . The text is read on Enter or when focus leaves the input.
Presets
Put buttons in the presets slot; they show to the left of the calendar. A button with
data-start and data-end (ISO dates) selects that range and closes the calendar, with
no script.
<tk-date-range-picker label="Report period" start-name="from" end-name="to">
<tk-button slot="presets" variant="ghost" size="small"
data-start="2026-03-01" data-end="2026-03-31">March</tk-button>
<tk-button slot="presets" variant="ghost" size="small"
data-start="2026-01-01" data-end="2026-12-31">2026</tk-button>
</tk-date-range-picker>Presets such as "Last 7 days" depend on today's date, so you write them: compute the
dates in your code, or call selectRange().
When to use
- Use a date range picker for a period: a stay, a booking, a report window.
- Use a
Date picker for one date. Don't pair two date pickers as a range. - Use a
Calendar withmode="range"when the grid should stay visible on the page. - Use a
Time picker for times.
Content
- Name the period in the label: "Stay dates", "Report period".
- Rename the inputs when it helps:
start-label="Check-in",end-label="Check-out". - Put the format in the helper with an example: "For example, 27 3 2026".
The picker writes some errors itself. Write the others with error, in the same style:
| Problem | Error message |
|---|---|
| empty and required | "Enter a start and end date" |
| a date that isn't real | "Start date must be a real date" (written for you) |
| end before start | "End date must be the same as or after the start date" (written for you) |
outside min / max |
"Enter dates on or after 1 Jan 2026" |
More in
Do / Don't
| ✅ Do | ❌ Don't |
|---|---|
| Use one range picker for a period | Wire two date pickers together |
| Name the inputs for the task: "Check-in", "Check-out" | Use one text field for "start – end" |
Put the format in helper |
Leave people to guess the format |
Set min and max |
Let people pick any date in any year |
| Let people type either date | Make the calendar the only way in |
Related
Calendar : the range grid inside.Date picker : one date.Time picker : times.Input : the box these inputs share.
API
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
label |
string | '' |
the visible name of the pair; required |
label-hidden |
boolean | false |
hides the label but keeps it as the name |
start-label / end-label |
string | Start date / End date |
the names of the two inputs |
helper |
string | — | hint under the label |
error |
string | — | error message under the box |
start / end |
ISO date (yyyy-mm-dd) |
'' |
the range |
start-name / end-name |
string | — | the form field names; each date submits on its own |
min / max |
ISO date | — | the allowed dates; dates outside block submission |
visible-months |
1–3 |
2 |
months shown in the calendar |
week-start |
1–7 |
1 |
first day of the week, Monday to Sunday |
required |
boolean | false |
both dates needed to submit; adds the asterisk |
readonly |
boolean | false |
focusable and copyable; the buttons don't work |
disabled |
boolean | false |
not focusable, left out of the form |
size |
medium | large |
medium |
height 32 / 36px |
Slots
| Slot | Content |
|---|---|
presets |
buttons with data-start and data-end, shown beside the calendar |
Events
change fires when the range changes: a finished range, a preset, a typed date, or
Clear. It doesn't fire on the first of two picks, or when you set start or end in
code. Resetting the form restores the first range.
Methods
selectRange(start, end): sets both dates and closes the calendar.clear(): empties both dates.
Both fire change when the range changes.
CSS
::part(field): the bordered box.::part(input): both inputs.::part(popover): the calendar panel.
Tokens
The box uses the
| Part | Tokens |
|---|---|
| label | text/default, Label Medium; asterisk text/secondary |
| helper | text/secondary, Body Small |
| label and helper, disabled | text/disabled |
| presets column | space-8 padding, space-4 gap, border/subtle divider |
| popover | surface/raised + shadow-2, radius-16 |
Values per theme:
Accessibility
Built in:
- Two named inputs in a group named by
label. - The calendar button says "Choose date range", or "Change dates, 5 Mar 2026 to 20 Mar 2026, 16 days" once set.
- Opening moves focus into the calendar; the inputs stay reachable while it is open.
- The calendar announces the range ends and the finished span with its number of days.
- An empty required date, a date that can't be read, an end before the start, or a date out of bounds blocks submission and moves focus to the field.
Your part:
- Set
label, and rename the inputs when "Start date" and "End date" don't fit. - Put the format example in
helper. - Set
minandmaxto the dates that make sense.
| Key | Action |
|---|---|
Tab |
start input, end input, clear button, calendar button |
Down / Alt + Down |
opens the calendar and moves focus into it |
Enter |
reads the typed date; with both dates set and the calendar closed, submits the form |
Esc |
closes the calendar and drops a half-picked range |
Keys inside the calendar:
Contrast limits
- Field border: 1.33:1 in light and 2.00:1 in dark, under the 3:1 minimum; 4.22:1 in high contrast. Always show the label.
- Range band: the band between the ends is faint in every theme. The filled ends and the announcement carry the selection.