calendar-wc

Standalone calendar display with events, multi-select, range picking, date constraints, form integration, multi-month view, and expressive theming.

Overview

The <calendar-wc> component displays a month grid for viewing events, availability, or annotations on dates. It supports single, range, and multi-date selection, date constraints, form integration via ElementInternals, multi-month display, and locale-aware rendering.

Attributes

Attribute Type Default Description
data-month number (1-12) current Initial month to display.
data-year number current Initial year to display.
data-events JSON Event data keyed by ISO date. Values: string, object { label, color, icon, time }, or array.
data-selection string "none" Selection mode: "none", "single", "range", or "multi".
data-disabled-dates string Comma-separated ISO dates to disable.
data-highlight-dates string Comma-separated ISO dates to highlight, optionally with :category.
data-size string "default" Size variant: "compact", "default", or "large".
data-min-date ISO date Earliest selectable/navigable date.
data-max-date ISO date Latest selectable/navigable date.
data-months number (1-12) 1 Number of months to display side by side.
name string Form field name. When set with a selection mode, the calendar participates in native form submission.

Selection Modes

Single selection

Click a date to select it. Fires calendar:select with the selected ISO date.

Range selection

Click two dates to define a range. A hover preview shows between start and cursor. The value uses ISO 8601 interval format: YYYY-MM-DD/YYYY-MM-DD. During selection, the host element gets a data-tentative attribute with the start date.

Multi-select

Click dates to toggle them on or off. Selected dates are returned as space-separated ISO strings.

Date Constraints

Use data-min-date and data-max-date to constrain navigation and disable out-of-range dates. Navigation buttons and the year dropdown respect these boundaries. Constraints work alongside data-disabled-dates and the isDateDisallowed callback — all three are additive.

Multi-Month Display

Set data-months (1–12) to show multiple months in a responsive grid. Navigation shifts by one month at a time. All features (selection, events, highlights) work across all visible months.

Grid Layouts

The grid adapts its column count based on the number of months. On narrow containers, columns reduce responsively.

data-monthsLayoutUse case
22 columnsRange picker
33 columnsQuarter preview
42×2 gridQuarter view
63×2 gridHalf-year
124×3 gridYearly overview

Form Integration

When a name attribute is set and a selection mode is active, the calendar participates in native form submission via ElementInternals. No hidden input needed.

JavaScript Properties

PropertyTypeDescription
.value string | null Selected value. ISO date (single), ISO interval (range), space-separated ISOs (multi), or null.
.isDateDisallowed (date: Date) => boolean Callback to dynamically disable dates. Called per cell during render. Additive with attribute-based constraints.
.getDayParts (date: Date) => string | string[] | null Inject custom data-day-part attributes per cell for CSS targeting.

isDateDisallowed

getDayParts

Return a string or array of strings to add as data-day-part on the cell's <td>. Style with attribute selectors in CSS.

Events

Event Detail Description
calendar:select { value, date } (single), { value, start, end } (range), { values, dates } (multi) Fired when selection changes.
calendar:navigate { year, month } Fired on month navigation.
calendar:day-open { date, events } Fired when day detail overlay opens (display-only mode).

Size Variants

Three sizes adapt the calendar to different contexts.

SizeMax WidthEventsUse Case
compact16remDot indicatorsSidebars, cards
default22remDot indicatorsStandard display
large56remLabels with iconsSee Large View below

Large View

Large mode (data-size="large") transforms the calendar into a full-page schedule view with rich event display. Instead of dot indicators, events render as labeled chips with icons and times. Container queries drive responsive cell sizing with near-square proportions.

Event Labels

In large mode, each cell shows up to 2 event chips inline. Events beyond 2 collapse into a "+N more" overflow indicator. Each chip displays the label, optional time, and optional icon.

Day Detail Overlay

In display-only mode (data-selection="none"), clicking a date with events opens a positioned dialog showing the full event list. If any events have a time property, an hour-grid timeline renders alongside. Close with Escape or by clicking outside.

Watermark Day Numbers

Large cells display a subtle background day number (watermark) via ::before. Control its appearance with these tokens:

TokenDefaultPurpose
--cal-watermark-opacity0.05Background number opacity
--cal-watermark-colorvar(--color-text)Background number color
--cal-watermark-size3remBackground number font size
--cal-watermark-weight800Background number font weight

Event Dots

Pass event data as JSON to show dot indicators on dates. Each date can have a string, object with label/color/icon/time, or array of those.

Localization

Weekday headers, month names, and hour labels are rendered via Intl.DateTimeFormat, automatically matching the user's locale. Override by setting lang on the element or an ancestor.

Keyboard

KeyAction
Arrow Left / Arrow RightMove focus one day.
Arrow Up / Arrow DownMove focus one week.
Enter / SpaceSelect the focused date.
EscapeClose day detail overlay.

Calendar vs Date Picker

ComponentPurposeForm input?
<date-picker> Pick a single date via popover input Yes (ElementInternals)
<calendar-wc> Display events, availability, ranges, multi-select Yes (when name attribute is set)

Theming

The calendar exposes 27 CSS custom properties for expressive theming — from paper wall calendars to bold geometric styles. Every token defaults to transparent or the base appearance, so they are fully opt-in. Themes set them via [data-theme~="name"] calendar-wc { ... } selectors.

Banner

TokenDefaultPurpose
--cal-banner-height0pxBanner strip height (0 hides it)
--cal-banner-bgtransparentBackground color or gradient
--cal-banner-imagenoneBackground image
--cal-banner-sizecoverBackground-size
--cal-banner-positioncenterBackground-position

Header and Week Rows

TokenDefaultPurpose
--cal-header-bgvar(--color-surface-raised)Navigation header background
--cal-header-colorinheritNavigation header text
--cal-week-bgtransparentWeek row background
--cal-week-bg-alttransparentAlternating even-row tint

Day Cells

TokenDefaultPurpose
--cal-day-bgtransparentDay cell background
--cal-day-imagenoneDecoration layer image (stickers)
--cal-day-image-sizecontainDecoration background-size
--cal-day-image-opacity1Decoration opacity

Today, Disabled, Outside

TokenDefaultPurpose
--cal-today-bgvar(--color-surface-raised)Today cell background
--cal-today-bordervar(--color-border-strong)Today cell border
--cal-today-shadownoneToday cell glow or shadow
--cal-disabled-opacity0.5Disabled day opacity
--cal-outside-opacity0.35Outside-month day opacity
--cal-outside-colorvar(--color-text-muted)Outside-month text color

Highlight

TokenDefaultPurpose
--cal-highlight-bginteractive tintHighlighted date background
--cal-highlight-borderinteractive tintHighlighted date border

Accessibility

The grid uses role="grid" with individual day buttons. Today is marked with aria-current="date", selected dates with aria-selected="true", and disabled dates with aria-disabled="true". The month header uses aria-live="polite" for navigation announcements. Arrow keys navigate the grid; Enter/Space selects.

Related