HyCalendar
Usage examples on this page are written for Lit / plain HTML (
<hy-calendar>). The same component ships asHyCalendarin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A presentational, keyboard-accessible month grid built on the Temporal API. Pure date engine — no input, no popover. Embeddable standalone (booking surfaces, inline filters) and composed by hy-date-picker.
Selection is single by default; set range for two-endpoint selection. The value is an ISO YYYY-MM-DD (single) or an ISO 8601 interval start/end (range).
Temporal-free first paint
The grid is computed from Temporal, which is awaited via ensureTemporal() on first connect. Before it resolves (and during SSR) the component renders a lightweight loading shell — it never reads Temporal in the constructor or first render, so it passes the @lit-labs/ssr smoke test and behaves on the Safari/no-Temporal path.
Accessibility: built in
role="grid" with a roving tabindex over day buttons; Arrow keys move by day, PageUp/PageDown by month (Shift by year), Home/End to week edges. Today carries aria-current="date"; selected days carry aria-selected. Month changes are announced via a polite live region.
Accessibility: your part
- The calendar has no
label. Put it under a heading or in a labeled region that says what the date is for. - When
minormaxis set, tell people the allowed dates.
When to use
- A month that stays on screen: a booking surface, a filter by date, an availability view.
- Picking dates where the weekday or the week around them matters.
Do
- Set
minandmaxto the dates people can pick.
When not to use
- A date entered in a form:
hy-date-picker. The calendar alone submits nothing. - A date far from today, such as a birth date:
hy-date-field. - A time of day:
hy-date-field type="time".
Examples
Single date
<hy-calendar value="2026-03-07"></hy-calendar>Range
<hy-calendar range value="2026-03-07/2026-03-12"></hy-calendar>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
value | value | string | '' | Selected value — ISO YYYY-MM-DD (single) or ISO interval start/end (range). |
min | min | string | undefined | — | Earliest selectable date (ISO YYYY-MM-DD). Out-of-range days are disabled. |
max | max | string | undefined | — | Latest selectable date (ISO YYYY-MM-DD). Out-of-range days are disabled. |
locale | locale | string | undefined | — | Explicit locale override. Falls back to <html lang> then navigator.language. |
range | range | boolean | false | Enable two-endpoint range selection. |
loadingText | loading-text | string | 'Loading calendar' | Text announced while the calendar loads. Use for internationalization. |
previousMonthLabel | previous-month-label | string | 'Previous month' | Accessible label for the previous-month button. Use for internationalization. |
nextMonthLabel | next-month-label | string | 'Next month' | Accessible label for the next-month button. Use for internationalization. |
Events
| Event | Detail | Description |
|---|---|---|
input | — | Fires on each selection step (range anchor, navigation commit). |
change | — | Fires once a selection is committed (single click; range second click). |
CSS Parts
| Part | Description |
|---|---|
base | The calendar wrapper |
header | The month navigation header |
nav-prev | Previous-month button |
nav-next | Next-month button |
heading | The month/year heading |
grid | The day grid |
weekday | A weekday column header |
day | A day button |
live-region | Screen-reader announcement region |
CSS Custom Properties
The documented hooks. Every override hook the component reads — one per token it paints — is listed in @whitespaceux/harmony/theme-api.json, and @whitespaceux/harmony/component-bindings.json (the Harmony MCP get-bindings tool) says which part and state each one paints.
| Property | Default | Description |
|---|---|---|
--hy-calendar-cell-size | — | Edge length of a day cell |
--hy-calendar-gap-layout-2xs | var(--hy-gap-layout-2xs) | Gap between day cells |
--hy-calendar-radius-form-md | var(--hy-radius-form-md) | Corner radius of a day cell |
--hy-calendar-radius-overlay-floating-md | var(--hy-radius-overlay-floating-md) | Corner radius of the calendar surface |
--hy-calendar-background-brand-on-rest | var(--hy-background-brand-on-rest) | Selected day fill — the on token (C6) |
--hy-calendar-background-brand-soft-rest | var(--hy-background-brand-soft-rest) | In-range day background |
--hy-calendar-foreground-brand-rest | var(--hy-foreground-brand-rest) | Foreground of in-range and today day cells. |
--hy-calendar-background-surface-base | var(--hy-background-surface-base) | Calendar surface background. |
--hy-calendar-padding-form-md | var(--hy-padding-form-md) | Padding of the calendar surface. |
--hy-calendar-foreground-default-rest | var(--hy-foreground-default-rest) | Foreground of an in-month day cell. |
--hy-calendar-foreground-subtle-rest | var(--hy-foreground-subtle-rest) | Foreground of out-of-month day cells and weekday headers. |
--hy-calendar-background-neutral-hover | var(--hy-background-neutral-hover) | Hovered day background. |
--hy-calendar-foreground-brand-on-rest | var(--hy-foreground-brand-on-rest) | Selected day text on the on fill |
--hy-calendar-border-brand-rest | var(--hy-border-brand-rest) | Border of the today marker. |
--hy-calendar-motion-transition-hover-duration | var(--hy-motion-transition-hover-duration) | Hover transition duration. |
--hy-calendar-motion-transition-hover-easing | var(--hy-motion-transition-hover-easing) | Hover transition easing. |