On this page Calendar Calendar

The m3e-calendar component provides structured navigation and selection across month, year, and multi-year views. It supports single-date and range selection, applies disabled rules including minimum, maximum, and blackout constraints, and provides styling hooks for special date states.

import "@m3e/web/calendar";
Usage

This section outlines usage examples and configuration guidance for the components in this package.

Date string handling

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-calendar uses a consistent, standards-aligned parsing model instead of relying on the browser's built-in Date.parse() rules.

When m3e-calendar receives a date string, it automatically detects whether the value matches one of the ISO-8601 formats that can be parsed safely and unambiguously:

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.

Date selection

Set the date attribute to define the currently selected date. Listen for the change event to respond when the user selects a new one.

<m3e-calendar id="calendar" date="2026-01-01"></m3e-calendar>
<div id="selected-date"></div>
const calendar = document.querySelector("#calendar");
const selectedDate = document.querySelector("#selected-date");
selectedDate.textContent = "Selected Date:" + toLocaleDateString(calendar.date);
calendar.addEventListener("change", () => {
  selectedDate.textContent = "Selected Date:" + toLocaleDateString(calendar.date);
});
Start date

By default, a calendar 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-calendar should initially display.

<m3e-calendar start-at="2026-01-01"></m3e-calendar>
Start view

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.

<m3e-calendar start-view="multi-year"></m3e-calendar>
Date ranges

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-calendar range-start="2026-01-01" range-end="2026-01-09" start-at="2026-01-01"></m3e-calendar>
Min and max dates

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.

<m3e-calendar start-at="2026-04-01" min-date="2026-01-01" max-date="2026-04-30"></m3e-calendar>
Blackout dates

Use the blackoutDates property to specify a function used to determine whether a date is disabled and cannot be selected by the user.

<m3e-calendar id="blackout-dates"></m3e-calendar>
document.querySelector("#blackout-dates").blackoutDates = (date) => isWeekend(date);
Special dates

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.

<m3e-calendar id="special-dates" start-at="2026-04-01"></m3e-calendar>
document.querySelector("#special-dates").specialDates = (date) => isHoliday(date);
Internationalization

m3e-calendar uses the Gregorian calendar, and all date formatting is handled by the browser's Intl APIs based on the active locale.

Accessibility

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.

Native module support

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/calendar.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/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.

API

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.