Changelog
Every released version of @dateforge/react-calendar, generated from the repository changelog.
v3.2.3
Patchv3.2.2
PatchSep 2, 2026Patch Changes
Fix month/year popups not following the calendar's theme and appearance.
A portalled popup lives in
document.body, so it inherits nothing from the root shell — and two things were missing there:The appearance bridge (--cal-*→--c-day-radius/--c-radius/--c-gap/--c-day-gap/--c-pad) and the type stack were declared on[data-dateforge-root]only. Every module inside a picker fell back to its literal defaults, so radii, gaps and padding ignored the appearance and the font stack differed from the calendar's (the popup read a--c-fonttoken nothing ever set). They now cover[data-dateforge-popup]too.An appearance set on a DOM ancestor rather than theappearanceprop never reached the popup at all, since theme-scope is a React context. The popup now copies the appearance contract off its anchor, which catches both routes; an explicit prop still wins.Default popup corner radius follows
--c-radius(12px) instead of its own 10px fallback, matching the root.Guarded by the first play-function story (
Toolbar / PopupMatchesRootAppearance) — it asserts the popup's computed shape vars equal the root's, and only means anything in the browser project, since happy-dom computes no cascade.
v3.2.1
PatchSep 2, 2026Patch Changes
Fix preset navigation and toolbar width jitter.
Presets move the view: clicking a preset committed the value but left the grid on the month you were already on — the selection landed off-screen. It now navigates to the preset's first date.Month trigger keeps one width:CalendarToolbarMonthTriggerreserved no width at all, so a long month name ("September") resized the button and shoved the rest of the toolbar sideways. Both name variants now reserve their own widest month. The labels' sizers moved off the longest-by-character-count heuristic (character count is not width — every English short month is 3 chars, yet "May" renders wider than "Jan") and their text rides in CSScontent, so it stays out oftextContent, DOM queries and the a11y tree.Toolbar month text no longer clips descenders (the y in "May", g in "Aug").Thinner default toolbar button border (0.5px); named appearances that set--cal-control-borderare unchanged.
v3.2.0
MinorAug 31, 2026Minor Changes
Per-module theming, completed.
themeon any module now takes acreateThemeobject, not just a built-in family name — the samestring | ThemeFamilyunion as the root<Calendar theme>. A name still rides ondata-theme; an object is applied as inline--c-*vars withlight-dark()values, so a custom family flips with the active scheme without any JS. Works on every module, including the tracks (CalendarDaysTrack/CalendarMonthsTrack/CalendarYearsTrack). The union is exported asModuleTheme.Fixed: per-moduleschemepainted nothing. The attribute rendered, but only the root shell and portalled popups narrowedcolor-scheme, so everylight-dark()token inside a module resolved to the light side regardless of the module'sscheme. Module-leveldata-schemenow narrowscolor-schemetoo (autore-opens both sides).A module that declares its ownthemeorschemenow paints its own surface (background: var(--c-backdrop); modules without either stay transparent and inherit the root) — otherwise a dark-schemed module painted light ink onto the root's light backdrop.
v3.1.1
PatchJul 16, 2026v3.1.0
MinorJul 6, 2026Minor Changes
CJS repair, smart layouts, and a hardened build pipeline.
Fixed:
CJS build repaired —require()of the root,/prebuiltand/modulesentries failed withMODULE_NOT_FOUND(phantomlayers-*.cjschunk). CJS CSS contract: the CJS output carries no CSS references — import the stylesheet once via the new@dateforge/react-calendar/style.cssexport (ESM stays automatic through bundler-resolved css imports). CI now smoke-loads every exports subpath and re-pins the@layerorder statement on the shipped stylesheet.Subpath.d.tsfiles mark type-only exports with thetypemodifier, including cross-chunk re-exports (fixesverbatimModuleSyntax/isolatedModulesconsumers); CI cross-checks everyd.ctsvalue export against the realrequire()namespace.Manual input: an unsupportedformat(e.g.TT.MM.JJJJ) silently never committed — it falls back to the default with a dev warning; committing a date outside the shown month moves the view to it (typed commits only — arrow segment-stepping stays quiet, and a rejected pick never moves the view).Selected-dates: the active chip keeps readable text on hover.Days: weekend column strips align with the real columns under appearance padding;createAppearancewarns on a multi-valuedaysPadding(the strip math needs a single length).Changed / added (the minor):
Smart toolbar: the default layout is a wrapping flex row — overflow wraps to the next line instead of escaping the container, rows distribute space-between, and an over-wide label shrinks with an ellipsis.CalendarToolbarGroupgainedpush="start" | "end". The explicitcols/colgrid mode is unchanged.Smart rootcols:cols={N}now renders up to N equal columns, collapsing toward one on narrow screens (--cal-cols-min, default14em; set0pxfor fixed tracks). Newcol="full"places a module across the full row collapse-safely.MultiMonthCalendarmonths are self-contained cells, so a collapse never separates a header from its grid.DatePickergainedallowClear(defaulttrue); dev warning whenschemechanges on an uncontrolled calendar.
v3.0.0
MajorJul 5, 2026Major Changes
v3.0.0 — ground-up rebuild. One implementation, no legacy.
Breaking:
Flat root props replaced byconfig(createCalendarConfig({ mode, unit, locale, min, max, disabled, withTime, … })).Selection model isunit × mode(day/week/month × single/multiple/range/multi-range);value/onChangeshapes derive from that pair alone. Spans are{ start, end }.Themes are families only (28,light-dark()-driven,schemeprop); token renameshighlight→accent,accent→focusRing. Import named objects from/themes,/appearances(per-name subpaths removed).Context split replaced by one store:/contextnow exportsuseCalendarStore/useStoreSelector/useCalendarActions/useUI/useLabels.~55 *Labelprops →labelsregistry with per-module overrides.See.notes/PARITY-V3.mdfor the full changed-by-design record.New:
Prebuilts (/prebuilt):SimpleCalendar,DatePicker,MonthPicker,MultiMonthCalendar(6/12-month boards).Pure core (no React/DOM/Date) with strategy-enforced invariants; presets commit through the same validation as clicks; property-fuzzed reducer.DST-safetimeZoneboundary (gap/fold policies,UTC±N), time window (minTime/maxTime), bound editing on wheels/tracks/toolbar/manual-input.aria-live selection announcer, roving focus, full keyboard maps (incl. Shift+PageUp/Down year jump), WCAG-audited 28×2 palettes.RTL, gradient mode, 8 appearances incl.zenith, container-query layouts,cal-userCSS layer escape hatch.Dev-warning registry: malformed input never throws — it degrades with a fix-oriented warning.
v2.0.1
PatchJun 1, 2026v2.0.0
MajorJun 1, 2026Major Changes
CalendarManualInput—formatpropPass a token string with
DD,MM,YYYYand any single-char separator(s) — e.g."DD.MM.YYYY"(default),"MM/DD/YYYY","YYYY-MM-DD","DD-MM-YYYY". The format string also serves as the placeholder. Invalid format strings fall back to the default with no error. Existing usage stays onDD.MM.YYYYand is unchanged.CalendarDays—renderDaypropCustom renderer for the day cell inner content:
tsx renderDay?: (date: Date, state: DayState) => ReactNodeDayStateexposesisSelected,isToday,isDisabled,isWeekend,isInRange,isRangeStart,isRangeEnd,isOtherMonth. The button shell, data attributes, keyboard handlers, and a11y stay owned by the library — only the inner label is replaced. Default rendering (just the day number) is preserved whenrenderDayis omitted. New public exports:DayState,RenderDayfrom@dateforge/react-calendar/modules/days.Calendar—data-testidpropOptional
data-testidon the root wrapper. Defaults to"dateforge-calendar". Override per-instance when mounting multiple calendars side by side.CSS layers
Collapse the styling contract to
cal-base,cal-themes,cal-appearances,cal-modules, andcal-user; old internal layer names are removed.Calendar—motionpropOpt into browser View Transitions with
motion="view-transition"for calendar navigation and popup open/close. Default remains"none".CalendarTimeGrid→CalendarTimeWheel(rename, breaking)The drum-style time picker module has been renamed. Migration:
```diff
import { CalendarTimeGrid } from "@dateforge/react-calendar/modules/time"; + import { CalendarTimeWheel } from "@dateforge/react-calendar/modules/time";type X = CalendarTimeGridProps; + type X = CalendarTimeWheelProps; ```The subpath import (
@dateforge/react-calendar/modules/time) stays the same. Only the component / type identifier changed. Motivation: free up theTimeGridname for a future fixed-slot booking-style picker (see roadmap F-9CalendarTimeSlots) and align with the new "Wheels" module family.New module:
CalendarMonthsWheelDrum-style month picker built on the shared
StepDrumphysics (same engine asCalendarTimeWheel). Mirrors the wheel API:bound?: "from"|"to"(range mode),showBoundDate?,showReset?,resetLabel?,showLabel?,monthsLabel?(aria),monthPickerLabel?(group aria),resetMonthLabel?,shortMonths?(renderJan/Февinstead of full names),onMonthSelect.Without
boundthe drum dispatchesnavigateTo(view-only); withboundit dispatchesonRangeBoundSet(bound, …)to mutate that boundary's month while preserving the other fields. Subpath:@dateforge/react-calendar/modules/months-wheel.New module:
CalendarYearsWheelDrum-style year picker. Same wheel API surface as
MonthsWheel. Year range is bounded byminDate.getFullYear()/maxDate.getFullYear()when set, otherwise the virtual range[1900, 2100]. Subpath:@dateforge/react-calendar/modules/years-wheel.New module:
CalendarLunarInformation-only lunar phase strip. Shows the day number + a moon-phase glyph + a short phase label for a 21-day window centered on the selected date (or
viewDateas fallback). CSS container queries auto-fit the visible subset (1 / 3 / 5 / ... / 21 cells) based on container width — anchor cell is always visible. Smooth crossfade animation between phases via stacked SVG paths with CSS opacity transition. Honoursprefers-reduced-motion.Props:
col?,theme?,lunarLabel?(group aria),phaseLabels?(falseto hide, orPartial<Record<LunarPhaseKey, string>>to localize),phaseAriaLabels?(per-locale screen-reader labels).Phase helpers (tree-shakeable when only the component is imported):
getLunarFraction,getLunarPhaseKey,getLunarIllumination,buildLunarWindowfromsrc/modules/lunar/helpers.ts. Astronomical math: synodic month = 29.530588853d, reference new moon = 2000-01-06 18:14 UTC.LunarPhaseKeyis a stable string union for typed label maps. Subpath:@dateforge/react-calendar/modules/lunar.Wheel ergonomics — shared additions across TimeWheel, MonthsWheel, YearsWheel
bound="from"|"to"— range mode only, edits that boundary instead of guessing byviewDate.showBoundDate?: boolean(defaulttrue) — localized date header above the drum whenboundis set.showReset?: boolean— render a reset button below the drum that snaps to "now" (time) / current month / current year.resetLabel?: ReactNode— override reset button content.showLabel?: boolean— show localized header above the drum (Intl.DisplayNamesdateTimeField).New action-label keys:monthsLabel,yearsLabel,resetMonthLabel("Reset to {month}"),resetYearLabel("Reset to {year}").CHANGE_TIMEambiguity guard in range mode (A-2 fix, breaking-soft)When both
rangeStartandrangeEndshare the calendar day matchingviewDate, the legacy reducer silently editedrangeStart(the earlier branch) and maderangeEndunreachable. After the fix, this ambiguous case is a no-op + a one-time dev warning that points to<CalendarTimeWheel bound="from"|"to">/onRangeBoundSet(bound, date).Single mode, multiple mode, and unambiguous range mode are unaffected. Bound-aware paths (
TimeWheel bound, bound Nav time popup) bypass this branch entirely. Migration: pass an explicitboundprop when editing time on a range-mode calendar.UIContext.containerWidthremoved (breaking)The field was unused internally — modules adapt via CSS
@containerqueries (cal-root,cal-days,cal-nav, …) instead of JS width measurement. The root<Calendar>no longer attaches aResizeObserver. If you readuseUI().containerWidthin custom modules, switch to container queries or measure your own ref.!importantpurge in `src//\*.module.css` (breaking-soft)\*\*All 55
!importantdeclarations have been removed from library CSS modules. The natural layer cascade (cal-base→cal-themes→cal-appearances→cal-modules→cal-user) now resolves all priority. User CSS overrides land incal-user(last layer) and win without needing!important. A CI guard hard-bans any new!importantfromsrc/**/*.module.css.Theme tokens —
outOfMonthrolled out to all 42 themesThe
outOfMonthcolor token (--c-oom) is now defined in every built-in theme so adjacent-month day text in the grid is colored consistently.Apple-style press feedback on selected cells
.activeItem(selected day, current month/year, active preset, selected chip) and popup confirm button gain a subtle scale + spring easing on press (transform: scale(0.95)for 80ms down, 180ms springy release). Tuneable via--cal-press-scale,--cal-press-down-duration,--cal-press-up-duration,--cal-press-up-easing. Honoursprefers-reduced-motion.New module:
CalendarToolbar+ toolbar submodules (replacesCalendarNavcomposition pattern)CalendarToolbaris a composable toolbar shell that replaces the monolithicCalendarNavfor custom header layouts. Drop it anywhere in the<Calendar>tree; it reads the activeboundand date from context.Submodules (all under
@dateforge/react-calendar/modules/toolbar):| Component | Role | | --------------------------------------------- | ----------------------------------------------------------------------------------- | |
CalendarToolbarGroup|flexwrapper with optionalgrow(fills remaining space) | |CalendarToolbarMonthLabel| Static month display,short?,currentMonthLabel?| |CalendarToolbarYearLabel| Static year display,currentYearLabel?| |CalendarToolbarDayLabel| Static day display,format?: "numeric" \| "2-digit" \| "long",currentDayLabel?| |CalendarToolbarLabel| Generic text label,content?: ReactNode| |CalendarToolbarClock| Live clock (mirrorsCalendarNav showNowTime) | |CalendarToolbarPrev/CalendarToolbarNext| Prev/next nav buttons,unit?: "day" \| "week" \| "month" \| "year"| |CalendarToolbarHome| Jump-to-today button | |CalendarToolbarClear| Clear selection button | |CalendarToolbarThemeToggle| Toggle light/dark theme | |CalendarToolbarTime| Open time-picker popup button | |CalendarToolbarMonthTrigger| Open month-picker popup button | |CalendarToolbarYearTrigger| Open year-picker popup button | |CalendarToolbarApply| Confirm / apply button (applyLabel?,onApply?,disabled?) |All label submodules use the visually-hidden text pattern for screen readers — the accessible name is always announced regardless of display format. Label text is resolved via
resolveActionLabel(per-instance prop →actionLabelsconfig → English default), so the full localization pipeline applies.New
CalendarActionLabelskeys:currentDayLabel(default"Current day, {day}"),currentMonthLabel(default"Current month, {month}"),currentYearLabel(default"Current year, {year}"),nextDayLabel,previousDayLabel.Toolbar submodules —
offsetpropCalendarToolbarMonthLabel,CalendarToolbarMonthTrigger,CalendarToolbarYearLabel, andCalendarToolbarYearTriggeracceptoffset?: numberto shift the displayed month/year by N months relative to the Calendar's base view date. Positive values go forward, negative backward; year rolls over correctly at December/January boundaries.Component-level
offsetis independent of anyoffsetset on the parent<CalendarToolbar>— it is always applied to the raw view date (baseDate), not to the already-offset toolbar date. This makes it safe to build multi-month panels using a single toolbar:tsx <CalendarToolbar> <CalendarToolbarPrev /> <CalendarToolbarMonthLabel /> {/* current month */} <CalendarToolbarMonthLabel offset={1} /> {/* next month */} <CalendarToolbarNext /> </CalendarToolbar>New
ToolbarContextValuefield:baseDate(pre-offset view date); previously onlydate(post-offset) was exposed. Custom toolbar submodules that need the raw anchor can readbaseDatefromuseToolbarContext().Toolbar — disabled-button animation fix
Interactive toolbar buttons (MonthTrigger, YearTrigger, and any
.interactiveelement) no longer show the:activepress animation whendisabledoraria-disabled="true". Both the module-level:active:not([disabled])guard and a globaltransform: nonefallback inglobal.module.cssenforce this.CalendarDays— keyboard month navigation slide animationArrow-key and Page-key navigation that crosses a month boundary now plays a vertical slide animation (
slideInDownfor previous month,slideInUpfor next month) instead of the existing horizontalslideInLeft/slideInRight. Left/right swipe and toolbar prev/next buttons retain the horizontal animation. Honoursprefers-reduced-motion.CalendarLunar— accessibilityStrip upgraded from
role="group"torole="list"/role="listitem". Each cell now includes visually-hidden text (date + phase name) so screen readers in browse mode announce content correctly.phaseAriaLabelsoverrides apply to the hidden text, not anaria-labelattribute.
v1.6.0
MinorMay 19, 2026Minor Changes
Add cascading
actionLabelsconfig to<Calendar>for centralized aria-label customization across all modules.Add
pressappearance — newspaper-style serif with sharp corners, wide letter-spacing, and flat shadows.Add
atelier(light) andbauhaus(dark) themes — paired warm cream / cool ink palette with red dateline accent.Rename appearance tokens for clarity:
--header-padding→--cal-nav-padding,--header-min-height→--cal-nav-min-height,--cal-text-2xl→--cal-nav-font-size,--cal-text-xl→--cal-nav-meta-font-size. TheheaderPadding/headerMinHeightTS keys becomenavPadding/navMinHeight, with newnavFontSize/navMetaFontSizeadded.
v1.5.1
PatchMay 18, 2026Patch Changes
Perf: rAF-coalesce range-hover updates and gate hover-date recomputation per month so multi-month layouts stay under one frame per hover tick.
Feat: new
syncViewOnSelectprop onCalendarDays. Defaults totruefor the primary grid andfalsefor anyoffsetgrid, so clicking a day in a side month no longer steals the primary view. Passtrue/falseto override.
v1.5.0
MinorMay 16, 2026Minor Changes
compactTimeprop for Nav module, to get quick access to time popup.Add the new
CalendarInfomodule for rendering selection summaries and relative date text.Improve time selection workflows with bound range time editing, expanded time grid behavior, and a range duration recipe that combines range selection with time grids.
Extend tokens for customization and polish across appearances, themes.
v1.4.8
PatchMay 13, 2026v1.4.7
PatchMay 13, 2026v1.4.6
PatchMay 13, 2026v1.4.5
PatchMay 12, 2026v1.4.4
PatchMay 12, 2026v1.4.3
PatchMay 12, 2026Patch Changes
Fix
getTimeStringignoring locale (always formatted with"en"). Addlocaleparameter; update both call sites inCalendarNavto pass the configured locale.Add
getNumberFormattointl-cachesoIntl.NumberFormatinstances (used for unit labels in the time track) are cached across renders instead of recreated each time.Deduplicate wrap arithmetic in
StepDrumby using the existinggetDrumValueutility.Only fire
onTimeSelectfor accepted time changes.Remove second-step future shortcuts from
basicPresets.
v1.4.2
PatchMay 11, 2026Patch Changes
AM/PM control redesigned as a single
role="switch"toggle: larger hit target, sliding thumb, gradient-aware active styles, softer focus ring, keyboard Space/Enter support.CalendarDaysrender perf: stabilize cell handlers via ref-pattern soReact.memono longer bypasses on every selection/hover, key cells bydateTimefor stable identity across renders, cacheisTodayDateper cell, extract class composition into puregetDayCellClassNamehelper.Shared
Intl.DateTimeFormatcache keyed bylocale + sorted-options. Replaces ad-hocnew Intl.DateTimeFormatcalls and inconsistentuseMemowrapping acrossCalendarNav,CalendarDays,CalendarSelectedDates,CalendarMonthsGrid,CalendarMonthsTrack,CalendarDaysTrack, layout announcer,getTodayInTimezone, and live-timegetTimeString. Bounded FIFO eviction (64 entries) prevents unbounded growth.CalendarTimeGridgains optionallabelsprop.labels="short"rendersHH/MM/SSabove each drum (clock convention).labels="long"renders the localized field name viaIntl.DisplayNames(locale, { type: "dateTimeField" }). Omit to keep current label-less layout.CalendarNavpolish: chevron SVG icons replace unicode‹/›arrows; compact month/year buttons match time/picker height and pick up hover styles; nav usesjustify-content: space-betweenso items distribute across the row;themeTogglearia-label is dynamic ("Switch to dark mode"/"Switch to light mode") witharia-pressedreflecting current theme; month/year picker buttons use nativedisabledinstead ofaria-disabledwhen fixed byminDate === maxDate; navlabelprop renders as a heading and is wired to the toolbar viaaria-labelledby;manual-inputmask preserves caret position when editing mid-string and steps over separator on Backspace;DaysTrackmonth label uses active-cell color for proper contrast in the active item;CalendarManualInputinternals split intomasked-date-input.tsxanddate-slot.tsx.CalendarTimeGridAM/PM switch fix: switch active label now uses an explicitdata-valueattribute so the AM label receives the active color in AM mode (the previous:first-of-typeselector matched the thumb span, leaving AM muted).
v1.4.1
PatchMay 10, 2026Patch Changes
CalendarDays: new
weekdayFormatprop ("narrow" | "short" | "long", default"short"). Locale-aware viaIntl.DateTimeFormat—"narrow"renders single-letter labels (M T W…),"long"renders full names. Header font-size auto-adjusts per format.Gradient mode (
<Calendar gradient />): styling extended beyond root background. Active surfaces across all modules now consume the gradient tokens — selected day cell, active preset chip, active selected-dates chip, current month/year cell, drum/track highlight pills (time, days/months/years tracks), nav time button, popup confirm button, days-track confirm button, manual-input chip wrapper, month-year nav track. Theme-aware via--c-hhighlight token. Non-gradient mode behavior unchanged (var fallbacks resolve to existing tokens).
v1.4.0
MinorMay 10, 2026Minor Changes
Theming, tokens, a11y, standalone module callbacks.
Theme tokens: addactiveText(--c-at),mutedText(--c-m),disabledText(--c-dt). All built-in themes updated.Themes: add 3 new dark themes —cobalt,velvet,eclipse(36 built-in total).Contrast: cover all WCAG AA contrast issues across themes (axe-clean baseline).Appearance tokens: extendcreateAppearancewithfont,fontSize,dayFontSize,controlFontSize,daysSpacing,trackHeight,dayRatio(typography + sizing).Built-in appearances (loft,compact,square,soft,bubble) refreshed with new tokens.CalendarMonthsTrack: newshowYearLabelprop — renders year under the active month chip.Standalone callbacks — every navigation/time module now exposes a callback so it can be used withoutCalendarDays:CalendarMonthsGrid→onMonthSelect?: (date: Date) => voidCalendarYearsGrid→onYearSelect?: (date: Date) => voidCalendarMonthsTrack→onMonthSelect?: (date: Date) => void(receives clamped bound date in range mode)CalendarYearsTrack→onYearSelect?: (date: Date) => void(receives clamped bound date in range mode)CalendarTimeGrid→onTimeSelect?: (date: Date) => void(fires on every drum change; readgetHours()/getMinutes()/getSeconds()for time-only value)CalendarSelectedDates: split date and time with|whenshowTimeis on (Jun 15, 2024 | 3:30 PM).A11y: Storybook addon-a11y switched totest: "error"— 117 stories × axe per story = 0 violations gate. Newa11y.ymlworkflow + README badge.
v1.3.0
MinorMay 7, 2026Minor Changes
Add
timeStepprop on<Calendar>for time drum granularity (applies toCalendarTimeGridand theCalendarNavtime popup):tsx <Calendar timeStep={{ minute: 15 }}> <CalendarTimeGrid /> </Calendar>{ hour?, minute?, second? }, default1. Affectsaria-valuemax, keyboard, and snap.Add
useToday()SSR-safe hook (exported from main entry). Returnsnullon the server and during pre-hydration render, then resolves tonew Date()after mount via layout effect. Use this instead ofuseState(new Date())to avoid hydration mismatch / two visually-selected day cells in Next.js / Remix / any SSR app. SeeDOCUMENTATION.md → SSR pitfall.Fix:
CHANGE_TIMEnow rejects time edits that violateminDate/maxDate/disabledrules. Range mode re-validates the affected endpoint viavalidateRange(coversminRangeDays/maxRangeDays/ ordering).
v1.2.2
PatchMay 6, 2026Patch Changes
Improve track and range behavior.
StopuseTrackanimation frames when the track is idle, and restart them only for active movement.Validate range updates from manual input, presets, track bounds, and selection actions against min/max range length and disabled dates.Keep outside-month day styling dominant when a day is also disabled.
v1.2.1
PatchMay 1, 2026v1.2.0
MinorMinor Changes
- fix: range-bound tracks enforce composite ordering —
tocan't move beforefrom,fromcan't move pastto. Per-field min/max recomputed each render from opposite bound + other fields.fix:useBoundDateViewfalls back to opposite bound when own is null, so bound modules stay coherent across both sides.fix:SET_RANGE_BOUNDreducer replaces auto-swap with no-cross clamp. Bounds no longer flip identity mid-drag.feat:boundprop on<CalendarNav>— labels, arrows, popups,home,clearroute through the bound boundary.fix: controlledvalueis now single source of truth. User actions fireonChangewith the next value but never mutate internal selection state. OnlyviewDateupdates locally.
v1.1.0
MinorApr 28, 2026Minor Changes
Add
errortheme token (--c-e) and surface it across feedback paths.Newerrortoken inThemeTokensmapped to CSS var--c-e. Provided for all 33 built-in themes (red-themed palettes use a contrasting amber/orange so the signal still pops).createThemeaccepts the new key transparently.<CalendarManualInput>: invalid-input border, focus ring, save button and chip-remove hover all now usevar(--c-e). Input gets a deterministic SSR-safeidviauseId.multiplemode: whenmaxDatesis reached, hovering an unselected day shows a soft--c-etint withnot-allowedcursor (data-max-reachedattribute on the cell).Disabled-day clicks no longer leak through the cell handler — explicitly rejected in the day select reducer.