import "@m3e/web/datepicker";
This section outlines usage examples and configuration guidance for the components in this package.
Different browsers interpret date strings in inconsistent ways, especially when the string resembles an ISO date
but does not include a time or timezone. To ensure predictable behavior across environments,
m3e-datepicker uses a consistent, standards-aligned parsing model instead of relying on the
browser's built-in Date.parse() rules.
When m3e-datepicker receives a date string, it automatically detects whether the value matches one
of the ISO-8601 formats that can be parsed safely and unambiguously:
yyyy-MM-dd — local date (no time, no timezone)yyyy-MM-ddTHH:mm:ss — local date and timeyyyy-MM-ddTHH:mm:ssZ — UTC date and timeyyyy-MM-ddTHH:mm:ss±HH:mm — date and time with an explicit timezone offset
Strings in these formats are converted into local Date objects without unexpected timezone shifts.
This prevents issues such as date-only values drifting into the previous or next day when interpreted as UTC. If
a string does not follow one of these ISO-compatible patterns, parsing may be ambiguous or unsupported. For the
most reliable results, use one of the formats above.
Use the m3e-datepicker-toggle to open a m3e-datepicker. This component should be
nested inside a clickable element and be associated with a picker by setting the for attribute to the id of the
picker to open.
<m3e-form-field variant="outlined">
<label slot="label" for="field">Date Field</label>
<input autocomplete="off" id="field" />
<m3e-icon-button slot="suffix">
<m3e-icon name="calendar_today"></m3e-icon>
<m3e-datepicker-toggle for="datepicker"></m3e-datepicker-toggle>
</m3e-icon-button>
<span slot="hint">MM/DD/YYYY</span>
</m3e-form-field>
<m3e-datepicker id="datepicker"></m3e-datepicker>
The m3e-datepicker supports three appearance variants: docked (default),
modal, and auto. Use the variant attribute to control how the picker is
presented. When set to auto, the picker automatically selects the appropriate appearance based on
the current screen size.
When using the modal variant, the value of the label attribute is used as the header
text for the modal dialog.
All examples in this page use variant="auto".
<m3e-datepicker variant="auto"></m3e-datepicker>
Use the date attribute to set the currently selected date, and listen for the
change event to react when the user selects a new one. You can also enable the
clearable attribute to display a button that clears the current selection.
<!-- Form field omitted for brevity --> <m3e-datepicker id="datepicker" date="2026-01-01" clearable></m3e-datepicker>
const picker = document.querySelector("#datepicker");
const field = document.querySelector("#field");
field.value = toLocaleDateString(picker.date);
picker.addEventListener("change", () => {
field.value = toLocaleDateString(picker.date);
});
By default, a datepicker opens to the selected date, or to today if no date is set. You can override this by
using the start-at attribute to specify the date that m3e-datepicker should initially
display.
<!-- Form field omitted for brevity --> <m3e-datepicker id="datepicker" start-at="2026-01-01"></m3e-datepicker>
The start-view attribute can be used to set the view that will show up when the calendar first
opens. It can be set to month, year, or multi-year; by default it will
open to month view.
<!-- Form field omitted for brevity --> <m3e-datepicker id="datepicker" start-view="multi-year"></m3e-datepicker>
To select a range of dates instead of a single date, use the
range-start and range-end attributes. Setting range-start puts the
calendar into range-selection mode. When the user selects a date after the current range-start,
both date and range-end are updated to that value. If the user selects a date before
the current range-start, the range is reset and both date and
range-start are set to the newly selected date.
<m3e-form-field variant="outlined">
<label slot="label" for="field">Date Range Field</label>
<input autocomplete="off" id="field" />
<m3e-icon-button slot="suffix">
<m3e-icon name="calendar_today"></m3e-icon>
<m3e-datepicker-toggle for="range-picker"></m3e-datepicker-toggle>
</m3e-icon-button>
<span slot="hint">MM/DD/YYYY - MM/DD/YYYY</span>
</m3e-form-field>
<m3e-datepicker
id="range-picker"
range-start="2026-01-01"
range-end="2026-01-09"
start-at="2026-01-01"
></m3e-datepicker>
const picker = document.querySelector("#range-picker");
const field = document.querySelector("#field");
field.value = toLocaleDateString(picker.rangeStart) + " - " + toLocaleDateString(picker.rangeEnd);
picker.addEventListener("change", () => {
field.value = toLocaleDateString(picker.rangeStart) + " - " + toLocaleDateString(picker.rangeEnd);
});
Use the min-date and max-date to disable all dates on the calendar before or after the
respective values and prevent the user from advancing the calendar past the month or year (depending on current
view) containing the minimum or maximum date.
<!-- Form field omitted for brevity --> <m3e-datepicker id="picker1" start-at="2026-04-01" min-date="2026-01-01" max-date="2026-04-30" ></m3e-datepicker>
Use the blackoutDates property to specify a function used to determine whether a date is disabled
and cannot be selected by the user.
document.querySelector("#blackout-dates").blackoutDates = (date) => isWeekend(date);
Use the specialDates property to specify a function used to determine whether a date has a
highlighted look and feel. You can use the --m3e-calendar-item-special-container-color,
--m3e-calendar-item-special-color, --m3e-calendar-item-special-ripple-color,
--m3e-calendar-item-special-hover-color, and
--m3e-calendar-item-special-focus-color CSS properties to style special dates.
document.querySelector("#special-dates").specialDates = (date) => isHoliday(date);
m3e-datepicker uses the Gregorian calendar, and all date formatting is handled by the browser's
Intl APIs based on the active locale.
Datepickers are given ARIA role="dialog" to identify the picker surface as a discrete interactive
region that opens above the page and requires user attention. When the picker is shown in its modal variant, it
also sets aria-modal="true" to indicate that the rest of the page is inert and that focus is
trapped within the dialog until it is dismissed. In docked mode, the picker omits
aria-modal entirely, since the surrounding page remains interactive and the surface behaves like an
anchored popover rather than a blocking dialog.
Datepicker toggles add aria-haspopup="dialog" to its parenting element indicating to assistive
technologies that activating the control will open a dialog.
The calendar is fully accessible and exposes clear semantics for assistive technologies. The header contains
navigation controls, each with an accessible label that can be customized through attributes. The calendar grid
is implemented using role="grid", with each date cell using role="gridcell" and
containing a focusable element with role="button". Selected dates and range boundaries are
indicated using aria-pressed, while today's date is marked with aria-current="date".
Each cell includes a visually hidden, locale-aware full date string for screen readers, while the visible text
shows only the day number. Day-of-week headers use abbreviated labels but include visually hidden full names
(such as “Monday”) to ensure clear, unambiguous announcements.
previous-month-label — Accessible label for the button that navigates to the previous month.
next-month-label — Accessible label for the button that navigates to the next month.previous-year-label — Accessible label for the button that navigates to the previous year.next-year-label — Accessible label for the button that navigates to the next year.previous-multi-year-label — Accessible label for the button that navigates to the previous
24-year range.
next-multi-year-label — Accessible label for the button that navigates to the next 24-year range.
The @m3e/web package uses
JavaScript Modules. To use it directly in a browser without a bundler, use a module script similar to the following.
<script type="module" src="/node_modules/@m3e/web/dist/datepicker.js"></script>
In addition, you must use an import map to include dependencies.
<script type="importmap">
{
"imports": {
"tslib": "https://cdn.jsdelivr.net/npm/tslib@2.8.1/+esm",
"lit": "https://cdn.jsdelivr.net/npm/lit@3.3.0/+esm",
"lit/": "https://cdn.jsdelivr.net/npm/lit@3.3.0/",
"lit-html": "https://cdn.jsdelivr.net/npm/lit-html@3.3.0/+esm",
"lit-html/directive.js": "https://cdn.jsdelivr.net/npm/lit-html@3.3.0/directive.js",
"lit-html/directives/if-defined.js": "https://cdn.jsdelivr.net/npm/lit-html@3.3.0/directives/if-defined.js",
"lit-html/directives/class-map.js": "https://cdn.jsdelivr.net/npm/lit-html@3.3.0/directives/class-map.js",
"@lit/reactive-element": "https://cdn.jsdelivr.net/npm/@lit/reactive-element@2.0.4/+esm",
"@lit/reactive-element/": "https://cdn.jsdelivr.net/npm/@lit/reactive-element@2.0.4/",
"@m3e/web/core": "/node_modules/@m3e/web/dist/core.js",
"@m3e/web/core/a11y": "/node_modules/@m3e/web/dist/core-a11y.js"
"@m3e/web/core/anchoring": "/node_modules/@m3e/web/dist/core-anchoring.js",
"@m3e/web/core/bidi": "/node_modules/@m3e/web/dist/core-bidi.js",
"@m3e/web/core/platform": "/node_modules/@m3e/web/dist/core-platform.js"
"@m3e/web/calendar": "/node_modules/@m3e/web/dist/calendar.js",
"@m3e/web/button": "/node_modules/@m3e/web/dist/button.js",
"@m3e/web/icon-button": "/node_modules/@m3e/web/dist/icon-button.js",
"@m3e/web/tooltip": "/node_modules/@m3e/web/dist/tooltip.js"
}
}
</script>
For production builds, use the minified files to ensure optimal load performance.
The @m3e/web package includes a
Custom Elements Manifest (custom-elements.json), which documents the properties,
attributes, slots, events and CSS custom properties of each component.
You can explore the API below, or integrate the manifest into your own tooling.