Skip to content

ExpressiveCSS

Browse documentation

Date Picker

Select a date, a range, or several dates from a calendar.

View as Markdown

The inline and docked calendars support keyboard navigation. Enter on the date input focuses a day. Tab enters or leaves the grid, and the arrow keys move by day or week. Home and End follow firstDay, and Left and Right follow isRTL. PageUp and PageDown change the month. Hold Shift with them to change the year. Enter or Space selects an enabled day.

Disabled dates stay focusable, and screen readers announce them as unavailable. Navigation respects configured bounds and keeps focus after redraws. A modal calendar moves focus to the selected day when it opens and back to the input when it closes.

An input's initial value is the selection, and a form reset reads the selection back from the reset values. If a typed date is outside the bounds, falls on a disabled weekend or is refused by disableDayFn, the input goes back to the selected date.

Add date-picker to a text input. AutoInit() starts every .date-picker except those marked no-autoinit.

Pass openByDefault: true to show the calendar under the field. The calendar is inline, not a modal. With the default options it is hidden (openByDefault: false), and clicking the input does not reveal it.

<div class="field">
  <input type="text" class="date-picker" id="birthdate">
  <label for="birthdate">Birthdate</label>
</div>
Expressive.Datepicker.init(document.querySelectorAll('.date-picker'), {
  openByDefault: true
});

Wrap the input in its own .field (or another small parent). The picker inserts the calendar after that parent, not after the input itself.

Initialization

The IIFE bundle exposes Expressive.Datepicker. Call init yourself when you need options other than the defaults, or let Expressive.AutoInit() start every .date-picker. AutoInit uses the defaults, so the calendar stays hidden until you pass openByDefault (or a working displayPlugin pair, below).

document.addEventListener('DOMContentLoaded', function() {
  const elems = document.querySelectorAll('.date-picker');
  const instances = Expressive.Datepicker.init(elems, {
    openByDefault: true
  });
});

You can also pass per-instance options through AutoInit:

Expressive.AutoInit(document.body, {
  Datepicker: { openByDefault: true }
});

Display

The calendar is a .datepicker-container in the page, unless displayPlugin: 'modal' puts it in a dialog. There is no autoClose option.

Always visible

openByDefault: true leaves the calendar in the layout. That is the reliable way to show it.

Docked popover

displayPlugin: 'docked' wraps the calendar in a .display-docked popover. The popover appears when the input is clicked or focused with Enter, and hides when you click outside. The plugin only animates the wrapper. If openByDefault is still false, the calendar inside stays display: none. Use both:

Expressive.Datepicker.init(elem, {
  displayPlugin: 'docked',
  openByDefault: true
});

The popover stays inside the input's parent and accounts for its positioning and scroll offsets. You can use positioned wrappers such as .field directly.

Optional displayPluginOptions: margin (default 5), transition (10), duration (250), and align ('left').

Modal dialog

displayPlugin: 'modal' opens the calendar in a modal <dialog class="display-modal"> when the input is clicked or focused with Enter. It looks like a basic dialog over a scrim. The dialog takes the input's label as its name, and focus moves to the selected day.

Picking a day writes nothing until Ok. Cancel, Escape or a press on the scrim closes the dialog without changing the input and calls onCancel. Focus then goes back to the input. A modal calendar always shows Cancel and Ok, so it sets openByDefault: true and autoSubmit: false for you.

Expressive.Datepicker.init(elem, {
  displayPlugin: 'modal'
});

A selective Sass build needs "dialog" and "modal-display" in $components.

container renders the calendar into a specific element and does not hide it. Pass a selector or a DOM node.

Options

Name Type Default Description
format String or Function 'mmm dd, yyyy' Output written to the input, or a function that takes a Date and returns a string.
parse Function null Turn the current input string back into a Date. Receives (value, format), where format is the configured string or an empty string for a function formatter. Blank inputs skip parsing.
isDateRange Boolean false Select a start date and an end date.
dateRangeEndEl String null Selector for an existing end-date input. If you omit it, the picker creates a second input.
isMultipleSelection Boolean false Toggle several dates. The picker creates extra inputs as dates are added.
defaultDate Date null Initial date to view. Falls back to the input's current value, then today.
defaultEndDate Date null Initial end date when isDateRange is true.
setDefaultDate Boolean false If true, defaultDate is also the selected value.
setDefaultEndDate Boolean false If true, defaultEndDate is also the selected end value.
disableWeekends Boolean false Prevent selecting Saturday and Sunday.
disableDayFn Function null Return true to disable that day. Receives a Date.
firstDay Number 0, or the language's First day of the week. 0 is Sunday, 1 is Monday. With a lang on or around the input, it defaults to that language's first day where the browser knows it.
minDate Date null Earliest selectable date.
maxDate Date null Latest selectable date.
yearRange Number or Array 10 Years on either side of the viewed year, or [minYear, maxYear].
yearRangeReverse Boolean false Sort the year list in reverse order.
isRTL Boolean false Render the calendar right-to-left.
showMonthAfterYear Boolean false Show the month after the year in the title.
showDaysInNextAndPreviousMonths Boolean false Render days that fall in the adjoining months.
openByDefault Boolean false If true, the calendar is visible. If false, it gets display: none.
container Element or String null Element or selector to render the calendar into. When set, the calendar is not hidden.
showClearBtn Boolean false Show a Clear button in the footer.
autoSubmit Boolean true If true, selecting a day writes the input immediately. If false, Ok and Cancel buttons are added.
i18n Object See below Labels and month and weekday names. Partial objects are merged with the defaults.
events Array [] Strings from Date.toDateString(). Matching days get a has-event class. The default stylesheet does not style that class.
onSelect Function null Called when a date is selected. Receives the Date.
onDraw Function null Called after the calendar HTML is redrawn.
onInputInteraction Function null Called when the input is clicked or confirmed with Enter.
onConfirm Function null Called once when Ok is activated by click, Enter or Space. The picker adds this button when autoSubmit is false.
onCancel Function null Called once when Cancel is activated by click, Enter or Space. The picker adds this button when autoSubmit is false.
displayPlugin String null Set to 'docked' for a click-to-open popover, paired with openByDefault: true, or 'modal' for a dialog with Cancel and Ok.
displayPluginOptions Object null Options for the docked plugin: margin, transition, duration, align. The modal plugin takes none.

Date format options

Without a custom parse callback, the picker reads YYYY-MM-DD input as a local calendar date. Impossible dates such as 2023-02-29 do not change the selection. The picker reads text in its own string format with its i18n names. So 05/10/2026 in dd/mm/yyyy is 5 October, and a German Mär 15, 2026 reads back. Other strings go to the browser's date parser. Supply parse for other input formats.

The custom parser also runs when the picker initializes, and when it reopens by click or Enter. Clearing a single-date input clears its selection, so confirming does not restore the deleted date.

Use these tokens in the format string.

Key Description Output
d Date of the month. 1 to 31
dd Date of the month, two digits. 01 to 31
ddd Weekday short name from i18n. Sun to Sat
dddd Weekday full name from i18n. Sunday to Saturday
m Month of the year. 1 to 12
mm Month of the year, two digits. 01 to 12
mmm Month short name from i18n. Jan to Dec
mmmm Month full name from i18n. January to December
yy Two-digit year. 26
yyyy Four-digit year. 2026

Internationalization

Put a lang attribute on the input or around it, such as <html lang="de">, to take the month and weekday names from that language, as Intl writes them. The first day of the week follows that language too, where the browser knows it. Without a lang, the English defaults stay. The button words are not dates, so pass them yourself.

Pass a partial i18n object. Keys you pass win over the language. Missing keys keep the language's names, or the English defaults.

Key Default
cancel 'Cancel'
clear 'Clear'
done 'Ok'
previousMonth 'Previous month'
nextMonth 'Next month'
months ['January', …, 'December']
monthsShort ['Jan', …, 'Dec']
weekdays ['Sunday', …, 'Saturday']
weekdaysShort ['Sun', …, 'Sat']
weekdaysAbbrev ['S', 'M', 'T', 'W', 'T', 'F', 'S']

Date range

Set isDateRange: true. Click a start day, then an end day that is on or after it. Point dateRangeEndEl at a second input, or omit it and the picker creates a second input next to the first.

An authored end input supports typing and Enter-to-calendar navigation. A range with only an end date can be reopened safely. Clearing either input keeps the other date and removes the range shading until both dates are set. An edit that would reverse the range restores that field's previous value.

Each input fires its own bubbling change event when the picker writes its value. Destroying the picker keeps authored inputs and removes generated ones. Generated inputs opt out of AutoInit, so repeated initialization does not create another picker for them.

Expressive.Datepicker.init(document.getElementById('datepicker-range'), {
  openByDefault: true,
  isDateRange: true,
  dateRangeEndEl: '#datepicker-range-end'
});

Multiple dates

Set isMultipleSelection: true. Click a day to add it, and click it again to remove it. Each selected date gets its own input. Removing the last date or using Clear keeps the original input empty. The picker removes extra generated inputs when their dates are deselected or the picker is destroyed.

Expressive.Datepicker.init(document.getElementById('datepicker-multi'), {
  openByDefault: true,
  isMultipleSelection: true
});

Methods

All methods are called on the plugin instance. You can get the instance like this:
const instance = Expressive.Datepicker.getInstance(elem);

The low-level renderRow, renderBody, and renderTable helpers accept only calendar rows, cells, and day buttons. Use renderDay to create cells. Unsupported elements or attributes, including event handlers and styles, throw TypeError. Day buttons require type="button". Arbitrary custom HTML is no longer accepted.

.toString();

String form of the selected date, using format. You can pass another date and format.

instance.toString();
instance.toString(someDate, 'yyyy-mm-dd');
.setDate();

Select a date and move the calendar to it.

Date (optional): Date to select.

instance.setDate(new Date());
.gotoDate();

Change the visible month without changing the selection.

Date: Date whose month should be shown.

instance.gotoDate(new Date());
.destroy();

Destroy the plugin instance, remove the calendar, and tear down its event handlers.

instance.destroy();

Properties

Name Type Description
el Element The input the plugin was initialized with.
options Object The options the instance was initialized with.
date Date The selected date, or the range start.
endDate Date The range end, when isDateRange is true.
dates Array The selected dates when isMultipleSelection is true.

No matching pages

  • Source color

    The seed every generated ramp derives from. Pick one and browse the docs — the whole theme follows. Error does not: it is a fixed hue.

    Default source: Mist Iris.

    ExpressiveCSS presets: Lagoon, Harbor, Breeze, Cobalt, Iris.

    Bold

    Soft

    Mist

    Glow