Forms · DatePicker

DatePicker

Date input with an attached calendar popover. DatePicker owns the shared value, visible month and open state; Soma composes DateField, Popover and Calendar, while Eidos only adds the visual size, variant and color recipe.

parts{compiled.parts.order.length} events{events.length} value{valueLabel} min/max{boundsLabel} months{numberOfMonths}
{labelText} {#snippet children({ segments }: DatePickerInputSnippetProps)} {#each segments as { part, value }, index (`${part}-${index}`)} {value} {/each} {/snippet}
{boundsMessage || valueMessage}
{#snippet children(args: DatePickerCalendarSnippetProps)} {@render calendarBody(args)} {/snippet}
trace {#if trace.length === 0} open the picker and select a date {:else} {#each trace.slice(0, 3) as entry} {entry.event} · {entry.family}{entry.intent ? ' · ' + entry.intent : ''} {fmtTime(entry.at)} {/each} {/if} open {String(open)} value {valueLabel}
{#if tab === 'live'}

Controls

Props grouped by the layer that owns them: soma keeps value, min/max, popover state and calendar behavior; eidos adds visual treatment. Sema events live in the dedicated { event.preventDefault(); tab = 'sema'; }}>Sema tab.

soma props · value, min/max and calendar
eidos props · visual treatment
Demo content
soma headless · shared value, min/max, popover and calendar svelte
{somaSnippet}
eidos visual · root plus attached DateField, Popover and Calendar parts svelte
{eidosSnippet}
{/if} {#if tab === 'api'}

API reference

Public DatePicker surface used by this demo. The component is date-first: value, min/max, popup state and calendar behavior are the relevant controls here.

PropOwnerDefaultDescription
value bindablesomaundefinedSelected `DateValue`. Calendar and input segments share this state.
placeholder bindablesomatodayVisible month anchor when the picker opens.
open bindablesomafalsePopover open state.
minValue / maxValuesoma-Selectable date limits. Invalid min/max marks Calendar invalid and exposes the message.
isDateDisabledsoma-Matcher for hard-disabled days.
isDateUnavailablesoma-Matcher for unavailable days that remain visible in the grid.
required / disabled / readonlysomafalseRoot, input, trigger and calendar state.
closeOnDateSelectsomatrueClose the popover after a calendar selection.
numberOfMonthssoma1Render one or two visible months in the popup.
fixedWeeks / pagedNavigationsomafalseCalendar grid height and navigation step.
disableDaysOutsideMonthsomatrueDays from adjacent months are shown but not selectable.
weekStartsOn / locale / dirsomaservicesCalendar ordering, labels and directional keys.
size eidoseidosmdShared visual scale for field, trigger and calendar.
variant eidoseidossurfaceVisual treatment of the input and popup calendar.
color eidoseidosprimaryIntent palette for focus and selected-day affordances.
Reference comparison
FeatureAirReact AriaArkBitsUIXDecision
Segmented input + calendar popoverno local Air componentyesyesyesyesCompose DateField, Popover and Calendar.
Explicit minValue / maxValue-yesyesyesyesExpose directly in Soma and demo controls.
Unavailable / disabled matchers-yesyesyesyesForward to Calendar as first-class picker props.
Multiple months-yesyesyesyesCalendar remains the source of month layout.
Composite semantic events-callbackscallbackscallbacksyesField, calendar and popover events are surfaced together in the picker demo.
{/if} {#if tab === 'morfo'}

morfo · declarative contract

Source: src/uix/morfo/components/date-picker.ts. DatePicker declares only the composition-specific root, trigger and calendar bridge. DateField, Popover and Calendar keep their own contracts.

FieldValue
name"{datePickerMorfo.name}"
kebab"{datePickerMorfo.kebab}"
scope[{datePickerMorfo.scope.map((s) => `"${s}"`).join(', ')}]
apgcomposed: DateField + Popover + Calendar
parts.length{datePickerMorfo.parts.length}
own events{datePickerEvents.length}
composed events{events.length}
Parts
{#each partsList as part} {/each}
PartMarkerElementRoleArchetypeStatesOptional
{part.kebab} [{part.marker}] <{part.defaultElement}> {part.role ?? '-'} {part.archetype ?? '-'} {part.states.length ? part.states.join(' | ') : '-'} {part.optional ? 'yes' : 'no'}
{#each datePickerMorfo.parts as rawPart} {@const partAny = rawPart as unknown as { kebab: string; aria?: ReadonlyArray<{ attr: string; value: { kind: string }; condition?: { when: string; prop?: string; part?: string }; severity?: string }>; keyboard?: ReadonlyArray<{ key: string; action: string }> }} {@const dataAttrs = compiled.contracts.dataAttrsByPart.get(partAny.kebab) ?? []} {@const ariaAttrs = partAny.aria ?? []} {@const keyboard = partAny.keyboard ?? []} {#if dataAttrs.length || ariaAttrs.length || keyboard.length}
{partAny.kebab}
{#if dataAttrs.length}
{#each dataAttrs as attr} {/each}
data-attrValuesSource kind
{attr.attr} {attr.values ? attr.values.join(' | ') : '-'} {'value' in attr && attr.value ? (attr as { value: { kind: string } }).value.kind : '-'}
{/if} {#if ariaAttrs.length}
{#each ariaAttrs as a} {/each}
aria-attrSource kindConditionSeverity
{a.attr} {a.value.kind} {a.condition ? `when ${a.condition.when}${a.condition.prop ? ` (${a.condition.prop})` : ''}${a.condition.part ? ` (${a.condition.part})` : ''}` : 'always'} {a.severity ?? 'required'}
{/if} {#if keyboard.length}
{#each keyboard as kb} {/each}
keyaction
{kb.key === ' ' ? 'Space' : kb.key} {kb.action}
{/if} {/if} {/each}
Events declaration
{#each eventRows as { owner, action }} {@const intentDecl = 'intent' in action.semantic ? action.semantic.intent : undefined} {/each}
NameFamilyVerbSequenceIntentTarget
{owner}.{action.name} {action.semantic.family} {action.semantic.verb ?? '-'} {action.semantic.sequence ?? 'pre'} {typeof intentDecl === 'string' ? intentDecl : intentDecl ? 'fromProp' : '-'} {action.target}
{/if} {#if tab === 'sema'}

sema · events + perceptual signature

The picker event surface is composed: DateField commits typed segment changes, Calendar commits date selection and navigation, and Popover owns disclosure events.

OwnerEventsWhy
DatePicker{datePickerEvents.length}Root state and composition.
DateField{dateFieldEvents.length}Typed date commits and clears.
Calendar{calendarEvents.length}Date select, unselect and month navigation.
Popover{popoverEvents.length}Open and close causes.
Composed event declarations
{#each eventRows as { owner, action }} {@const intentDecl = 'intent' in action.semantic ? action.semantic.intent : undefined} {/each}
OwnerNameFamilyVerbSequenceIntent
{owner} {action.name} {action.semantic.family} {action.semantic.verb ?? '-'} {action.semantic.sequence ?? 'pre'} {typeof intentDecl === 'string' ? intentDecl : intentDecl ? 'fromProp' : '-'}
{/if} {#if tab === 'recipe'}

Eidos recipe

Selectors live in src/uix/eidos/components/date-picker/date-picker.css. The root supplies DateField variables and the popup calendar reuses the Calendar recipe without drawing a second card.

SelectorSourcePurpose
[data-date-picker]morfoRoot provider, open and validation states.
[data-date-picker][data-size][data-variant][data-color]eidosVisual scale, treatment and intent palette.
[data-date-picker-trigger]morfoCalendar opener button.
[data-popover-content]:has([data-date-picker-calendar])eidosPopup width follows one or two calendar months.
[data-date-picker-calendar][data-calendar]morfoCalendar bridge with nested card chrome removed.
[data-calendar-day][data-selected]morfoSelected date state inherited from Calendar.
{/if} {#if tab === 'a11y'}

Accessibility

The picker combines the ARIA contracts of DateField, Popover and Calendar. The trigger opens a dialog-style popup; the grid keeps Calendar keyboard behavior.

Keyboard
KeyAction
Enter / SpaceOpen the popover from Trigger; select focused day inside Calendar.
EscapeClose the popover.
Arrow keysEdit date segments in the input or move focus by day/week in the calendar grid.
PageUp / PageDownCalendar month navigation.
Home / EndMove to first or last day of the current week.
ARIA contract
PartAttributeValue
rootaria-disabled / aria-readonly / aria-required{`{state flags}`}
inputrole"group"
segmentrole"spinbutton"
triggeraria-haspopup"dialog"
triggeraria-expanded{`{open}`}
contentrole"dialog"
calendar gridrole"grid"
calendar dayaria-selected / aria-disabled{`{date state}`}
{/if}
{#snippet calendarBody({ months, weekdays, validationMessage }: DatePickerCalendarSnippetProps)} <
>
{#if validationMessage} {/if}
{#each months as month (month.value.toString())}
{#if months.length > 1} {/if} 1 ? monthHeadingId(month.value) : undefined}> {#if showWeekNumbers} wk {/if} {#each weekdays as day} {day} {/each} {#each month.weeks as week} {#snippet children({ weekNumber }: DatePickerGridRowSnippetProps)} {#if showWeekNumbers} {weekNumber ?? ''} {/if} {#each week as date} {/each} {/snippet} {/each}
{/each}
{/snippet}