Skip to content

Date & time

These components use Reka’s date values and render their controls in the document. They do not open native date or time popups. Each example starts with an empty selection, uses the current calendar view and has its own SbProvider with an explicit locale.

All roots accept merged class (HTMLAttributes['class'], unset), extend the corresponding Reka *RootProps, and forward the remaining props and declared emits. v-model listens to update:modelValue. Tables include wrapper overrides, theme defaults and the most useful forwarded props; they are not an exhaustive copy of Reka’s root interfaces.

Date values are DateValue objects from @internationalized/date, rather than JS Date or ISO strings. CalendarDate is suitable for a date without time. TimeValue accepts Reka’s supported time values, including Time. A DateRange has start and end date values (either may be undefined while incomplete); a TimeRange has the same shape with time values. Install @internationalized/date in your own app if you import its constructors. Preserve these values with shallowRef when controlling a model.

The anatomy blocks show the parts supplied by the wrapper’s default slot. They describe nesting, rather than complete custom implementations: custom grids also need the root slot’s dates, rows, segment values and event bindings.

SbCalendar wraps Reka CalendarRoot and supplies a header, weekday row and date grid. Use it when the calendar should stay visible.

November 2026
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
Available dispatch dates, November 2026
CalendarDemo.vue
<script setup lang="ts">
import { SbCalendar, SbProvider } from '@static-bloom/vue'
</script>
<template>
<SbProvider locale="en-US">
<SbCalendar calendar-label="Available dispatch dates" />
</SbProvider>
</template>
<SbCalendar>
<SbCalendarHeader>
<SbCalendarPrev /><SbCalendarHeading /><SbCalendarNext />
</SbCalendarHeader>
<SbCalendarGrid>
<SbCalendarGridHead><SbCalendarGridRow><SbCalendarHeadCell /></SbCalendarGridRow></SbCalendarGridHead>
<SbCalendarGridBody><SbCalendarGridRow>
<SbCalendarCell><SbCalendarCellTrigger /></SbCalendarCell>
</SbCalendarGridRow></SbCalendarGridBody>
</SbCalendarGrid>
</SbCalendar>
Prop Type Default Effect
disableDaysOutsideCurrentView boolean true Disables dates outside the displayed month
modelValue DateValue | DateValue[] | null unset Selected date(s)
defaultValue DateValue unset Initial selection
multiple boolean false Multiple selected dates
numberOfMonths number 1 Visible months
calendarLabel string unset Accessible calendar label
minValue, maxValue DateValue unset Selectable bounds
isDateDisabled, isDateUnavailable (date: DateValue) => boolean unset Date restrictions
disabled, readonly, initialFocus boolean false Interaction lock / read-only / focus on mount

Remaining CalendarRootProps and emits pass through. placeholder controls the visible date independently of selection; defaultPlaceholder initializes it. The wrapper also exports the uncomposed SbCalendarRoot.

ArrowLeft/Right move a day, ArrowUp/Down a week, PageUp/Down a month, and Home/End to week boundaries. Min/max and unavailable dates cannot be selected. Calendar labels, provider locale and today’s marker are covered by the component tests. In-game calendars use these DOM cells instead of a native date input.

SbRangeCalendar wraps Reka RangeCalendarRoot with the same day-grid composition, selecting start and end dates.

Contract date window, November 2026
November 2026
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
RangeCalendarDemo.vue
<script setup lang="ts">
import { SbProvider, SbRangeCalendar } from '@static-bloom/vue'
</script>
<template>
<SbProvider locale="en-US">
<SbRangeCalendar calendar-label="Contract date window" />
</SbProvider>
</template>
<SbRangeCalendar>
<SbRangeCalendarHeader>
<SbRangeCalendarPrev /><SbRangeCalendarHeading /><SbRangeCalendarNext />
</SbRangeCalendarHeader>
<SbRangeCalendarGrid>
<SbRangeCalendarGridHead><SbRangeCalendarGridRow><SbRangeCalendarHeadCell /></SbRangeCalendarGridRow></SbRangeCalendarGridHead>
<SbRangeCalendarGridBody><SbRangeCalendarGridRow>
<SbRangeCalendarCell><SbRangeCalendarCellTrigger /></SbRangeCalendarCell>
</SbRangeCalendarGridRow></SbRangeCalendarGridBody>
</SbRangeCalendarGrid>
</SbRangeCalendar>
Prop Type Default Effect
disableDaysOutsideCurrentView boolean true Disables outside-month dates
modelValue DateRange | null uncontrolled Controlled range
defaultValue DateRange { start: undefined, end: undefined } Initial range
numberOfMonths number 1 Visible month count
calendarLabel string unset Accessible name
allowNonContiguousRanges boolean false Allows ranges containing unavailable dates
maximumDays number unset Maximum range length
fixedDate 'start' | 'end' unset Fixed endpoint
minValue, maxValue DateValue unset Selectable bounds

Remaining RangeCalendarRootProps and emits pass through, including date restrictions and disabled/readonly state. The tests verify range endpoint emission and accessible selected-range rendering. The calendar uses Reka’s day-grid keyboard navigation; keep its header controls and grid parts together when composing it yourself. In-game pages need WebUI focus before those keys reach the grid.

SbDateField wraps Reka DateFieldRoot, rendering one SbDateFieldInput for each locale-ordered segment.

mm
dd
yyyy
DateFieldDemo.vue
<script setup lang="ts">
import { SbDateField, SbProvider } from '@static-bloom/vue'
</script>
<template>
<SbProvider locale="en-US">
<SbDateField aria-label="Contract date" />
</SbProvider>
</template>
<SbDateField>
<SbDateFieldInput /> <!-- repeated for each segment, using its part and value -->
</SbDateField>
Prop Type Default Effect
size 'sm' | 'md' | 'lg' 'md' Field dimensions
invalid boolean false Invalid appearance and aria-invalid
modelValue DateValue | null unset Controlled date
defaultValue DateValue unset Initial date
minValue, maxValue DateValue unset Valid bounds
disabled, readonly boolean false Prevent interaction / editing

Remaining DateFieldRootProps and emits pass through. Supply aria-label or a connected label. ArrowUp increments the focused segment and numeric typing edits it. Locale changes segment ordering; disabled segments are not editable. It renders editable DOM segments, so no native popup is involved in-game.

SbDateRangeField wraps Reka DateRangeFieldRoot and renders start/end segment sets separated by an en dash.

mm
dd
yyyy
mm
dd
yyyy
DateRangeFieldDemo.vue
<script setup lang="ts">
import { SbDateRangeField, SbProvider } from '@static-bloom/vue'
</script>
<template>
<SbProvider locale="en-US">
<SbDateRangeField aria-label="Contract start and end dates" />
</SbProvider>
</template>
<SbDateRangeField>
<SbDateRangeFieldInput type="start" /> <!-- start segments -->
<SbDateRangeFieldInput type="end" /> <!-- end segments -->
</SbDateRangeField>
Prop Type Default Effect
size 'sm' | 'md' | 'lg' 'md' Field dimensions
invalid boolean false Invalid appearance and aria-invalid
modelValue DateRange | null uncontrolled Controlled endpoints
defaultValue DateRange unset (empty endpoints internally) Initial range
minValue, maxValue DateValue unset Date bounds
disabled, readonly boolean false Interaction / editing lock

Remaining DateRangeFieldRootProps and emits pass through. Each endpoint is independently editable; the tests change the end date while preserving the start. Name the range as a whole, keep endpoint segment types when composing, and let Reka manage segment focus. These are DOM controls in OPEN//77 too.

SbTimeField wraps Reka TimeFieldRoot, rendering SbTimeFieldInput parts for the formatted time segments.

––
––
TimeFieldDemo.vue
<script setup lang="ts">
import { SbProvider, SbTimeField } from '@static-bloom/vue'
</script>
<template>
<SbProvider locale="en-US">
<SbTimeField :hour-cycle="24" aria-label="Dispatch time" />
</SbProvider>
</template>
<SbTimeField>
<SbTimeFieldInput /> <!-- repeated for each segment -->
</SbTimeField>
Prop Type Default Effect
size 'sm' | 'md' | 'lg' 'md' Field dimensions
invalid boolean false Invalid appearance and aria-invalid
modelValue TimeValue | null unset Controlled time
defaultValue TimeValue unset Initial time
hourCycle 12 | 24 locale preference Hour display
granularity 'hour' | 'minute' | 'second' 'minute' Last displayed segment
minValue, maxValue TimeValue unset Time bounds
disabled, readonly boolean false Interaction / editing lock

Remaining TimeFieldRootProps and emits pass through. Name the field with aria-label. ArrowUp increments the focused minute segment, as verified by the tests. The example explicitly uses 24-hour entry. For in-game schedules, define whether values mean server time or local time before persisting them; the component does not decide that policy.

SbTimeRangeField wraps Reka TimeRangeFieldRoot for separate start and end times.

––
––
––
––
TimeRangeFieldDemo.vue
<script setup lang="ts">
import { SbProvider, SbTimeRangeField } from '@static-bloom/vue'
</script>
<template>
<SbProvider locale="en-US">
<SbTimeRangeField :hour-cycle="24" aria-label="Patrol start and end times" />
</SbProvider>
</template>
<SbTimeRangeField>
<SbTimeRangeFieldInput type="start" /> <!-- start segments -->
<SbTimeRangeFieldInput type="end" /> <!-- end segments -->
</SbTimeRangeField>
Prop Type Default Effect
size 'sm' | 'md' | 'lg' 'md' Field dimensions
invalid boolean false Invalid appearance and aria-invalid
modelValue TimeRange | null uncontrolled Controlled endpoints
defaultValue TimeRange unset Initial range
hourCycle 12 | 24 locale preference Hour display
granularity 'hour' | 'minute' | 'second' 'minute' Last displayed segment
disabled, readonly boolean false Interaction / editing lock

Remaining TimeRangeFieldRootProps and emits pass through. The tests decrement an end hour with ArrowDown while preserving the start. Name the whole range and preserve type="start"/type="end" when composing. It uses editable DOM segments; keep a visible alternative to platform-level shortcuts in-game.

SbDatePicker wraps Reka DatePickerRoot, supplying a segmented field, calendar trigger and portalled calendar dialog.

mm
dd
yyyy
DatePickerDemo.vue
<script setup lang="ts">
import { SbDatePicker, SbProvider } from '@static-bloom/vue'
</script>
<template>
<SbProvider locale="en-US">
<SbDatePicker label="Dispatch date" trigger-label="Choose dispatch date" close-on-select />
</SbProvider>
</template>
<SbDatePicker>
<SbDatePickerAnchor>
<SbDatePickerField>
<SbDatePickerInput />
<SbDatePickerTrigger />
</SbDatePickerField>
</SbDatePickerAnchor>
<SbDatePickerContent>
<SbDatePickerCalendar>
<SbDatePickerHeader><SbDatePickerPrev /><SbDatePickerHeading /><SbDatePickerNext /></SbDatePickerHeader>
<SbDatePickerGrid>
<SbDatePickerGridHead><SbDatePickerGridRow><SbDatePickerHeadCell /></SbDatePickerGridRow></SbDatePickerGridHead>
<SbDatePickerGridBody><SbDatePickerGridRow>
<SbDatePickerCell><SbDatePickerCellTrigger /></SbDatePickerCell>
</SbDatePickerGridRow></SbDatePickerGridBody>
</SbDatePickerGrid>
</SbDatePickerCalendar>
</SbDatePickerContent>
</SbDatePicker>
Prop Type Default Effect
size 'sm' | 'md' | 'lg' 'md' Field dimensions
invalid boolean false Field aria-invalid and styling
label string 'Date' Field and dialog name
triggerLabel string 'Open calendar' Trigger name
modelValue, defaultValue DateValue unset Controlled / initial selection
closeOnSelect boolean false Close after selection
defaultOpen, modal boolean false Initial popup state / modal behavior
numberOfMonths number 1 Calendar month count

Remaining DatePickerRootProps/emits pass through, including v-model:open, bounds and unavailable-date matchers. The default calendar disables days outside its current view. SbDatePickerRoot, Arrow and Close parts are also exported with the same prefix.

The trigger opens a portalled dialog. Escape closes and restores trigger focus; selecting with close-on-select emits a date and closes. Field segments remain directly editable. The popup is DOM-rendered for OPEN//77; the platform may swallow Escape, so a custom in-game composition can include SbDatePickerClose. Popups outside a scoped provider inherit the page flavor.

SbDateRangePicker wraps Reka DateRangePickerRoot with a two-endpoint field and portalled range calendar.

mm
dd
yyyy
mm
dd
yyyy
DateRangePickerDemo.vue
<script setup lang="ts">
import { SbDateRangePicker, SbProvider } from '@static-bloom/vue'
</script>
<template>
<SbProvider locale="en-US">
<SbDateRangePicker
label="Contract window"
trigger-label="Choose contract window"
close-on-select
/>
</SbProvider>
</template>
<SbDateRangePicker>
<SbDateRangePickerAnchor>
<SbDateRangePickerField>
<SbDateRangePickerInput type="start" />
<SbDateRangePickerInput type="end" />
<SbDateRangePickerTrigger />
</SbDateRangePickerField>
</SbDateRangePickerAnchor>
<SbDateRangePickerContent>
<SbDateRangePickerCalendar>
<SbDateRangePickerHeader><SbDateRangePickerPrev /><SbDateRangePickerHeading /><SbDateRangePickerNext /></SbDateRangePickerHeader>
<SbDateRangePickerGrid>
<SbDateRangePickerGridHead><SbDateRangePickerGridRow><SbDateRangePickerHeadCell /></SbDateRangePickerGridRow></SbDateRangePickerGridHead>
<SbDateRangePickerGridBody><SbDateRangePickerGridRow>
<SbDateRangePickerCell><SbDateRangePickerCellTrigger /></SbDateRangePickerCell>
</SbDateRangePickerGridRow></SbDateRangePickerGridBody>
</SbDateRangePickerGrid>
</SbDateRangePickerCalendar>
</SbDateRangePickerContent>
</SbDateRangePicker>
Prop Type Default Effect
size 'sm' | 'md' | 'lg' 'md' Field dimensions
invalid boolean false Field aria-invalid and styling
label string 'Date range' Field and dialog name
triggerLabel string 'Open calendar' Trigger name
modelValue DateRange uncontrolled Selected endpoints
defaultValue DateRange { start: undefined, end: undefined } Initial endpoints
closeOnSelect boolean false Close on completed selection
numberOfMonths number 1 Visible months
defaultOpen, modal boolean false Initial popup state / modal behavior

Remaining DateRangePickerRootProps and emits pass through. The default calendar disables outside-view dates. Tests verify the dialog portal, Escape closure, restored trigger focus and open-popup accessibility. Keep endpoint segment types when composing; SbDateRangePickerClose provides an explicit close action for game pages whose Escape key is unavailable. On narrow HUDs, keep the month count at one unless there is room for a wider calendar.

SbMonthPicker wraps Reka MonthPickerRoot, displaying months in a year with previous/next year navigation.

2026
Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec
Patrol month, 2026
MonthPickerDemo.vue
<script setup lang="ts">
import { SbMonthPicker, SbProvider } from '@static-bloom/vue'
</script>
<template>
<SbProvider locale="en-US">
<SbMonthPicker calendar-label="Patrol month" />
</SbProvider>
</template>
<SbMonthPicker>
<SbMonthPickerHeader><SbMonthPickerPrev /><SbMonthPickerHeading /><SbMonthPickerNext /></SbMonthPickerHeader>
<SbMonthPickerGrid><SbMonthPickerGridBody><SbMonthPickerGridRow>
<SbMonthPickerCell><SbMonthPickerCellTrigger /></SbMonthPickerCell>
</SbMonthPickerGridRow></SbMonthPickerGridBody></SbMonthPickerGrid>
</SbMonthPicker>
Prop Type Default Effect
modelValue DateValue | DateValue[] | null unset Selected month(s)
defaultValue DateValue unset Initial selection
calendarLabel string unset Accessible calendar name
multiple, preventDeselect boolean false Multiple selection / prevent deselection
minValue, maxValue DateValue unset Selection bounds
isMonthDisabled, isMonthUnavailable (date: DateValue) => boolean unset Month restrictions
disabled, readonly, initialFocus boolean false Interaction lock / read-only / focus on mount

Remaining MonthPickerRootProps and emits pass through. Month selection emits a date value, as verified in the tests. Preserve Reka’s cell triggers and named navigation controls for keyboard operation; this is a persistent grid, so it has no popup Escape behavior. It renders in the document for in-game use.

SbMonthRangePicker wraps Reka MonthRangePickerRoot for a start and end month.

Contract months, 2026
2026
Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec
MonthRangePickerDemo.vue
<script setup lang="ts">
import { SbMonthRangePicker, SbProvider } from '@static-bloom/vue'
</script>
<template>
<SbProvider locale="en-US">
<SbMonthRangePicker calendar-label="Contract months" />
</SbProvider>
</template>
<SbMonthRangePicker>
<SbMonthRangePickerHeader><SbMonthRangePickerPrev /><SbMonthRangePickerHeading /><SbMonthRangePickerNext /></SbMonthRangePickerHeader>
<SbMonthRangePickerGrid><SbMonthRangePickerGridBody><SbMonthRangePickerGridRow>
<SbMonthRangePickerCell><SbMonthRangePickerCellTrigger /></SbMonthRangePickerCell>
</SbMonthRangePickerGridRow></SbMonthRangePickerGridBody></SbMonthRangePickerGrid>
</SbMonthRangePicker>
Prop Type Default Effect
modelValue DateRange | null uncontrolled Selected month endpoints
defaultValue DateRange { start: undefined, end: undefined } Initial endpoints
calendarLabel string unset Accessible name
maximumMonths number unset Range length limit
allowNonContiguousRanges, preventDeselect boolean false Unavailable-month ranges / deselection policy
fixedDate 'start' | 'end' unset Fixed endpoint
disabled, readonly, initialFocus boolean false Interaction lock / read-only / focus on mount

Remaining MonthRangePickerRootProps and emits pass through, including month matchers and bounds. The tests select two endpoints and verify the emitted range. Keep its focusable Reka cell triggers when customizing; give the calendar a meaningful label. This persistent DOM grid needs no native month popup in-game.

SbYearPicker wraps Reka YearPickerRoot for archive years or other year-level values.

2020 - 2031
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
Archive year, 2020 - 2031
YearPickerDemo.vue
<script setup lang="ts">
import { SbProvider, SbYearPicker } from '@static-bloom/vue'
</script>
<template>
<SbProvider locale="en-US">
<SbYearPicker calendar-label="Archive year" />
</SbProvider>
</template>
<SbYearPicker>
<SbYearPickerHeader><SbYearPickerPrev /><SbYearPickerHeading /><SbYearPickerNext /></SbYearPickerHeader>
<SbYearPickerGrid><SbYearPickerGridBody><SbYearPickerGridRow>
<SbYearPickerCell><SbYearPickerCellTrigger /></SbYearPickerCell>
</SbYearPickerGridRow></SbYearPickerGridBody></SbYearPickerGrid>
</SbYearPicker>
Prop Type Default Effect
modelValue DateValue | DateValue[] | null unset Selected year(s)
defaultValue DateValue unset Initial selection
yearsPerPage number 12 Years in a view
calendarLabel string unset Accessible name
multiple, preventDeselect boolean false Multiple selection / deselection policy
isYearDisabled, isYearUnavailable (date: DateValue) => boolean unset Year restrictions
disabled, readonly, initialFocus boolean false Interaction lock / read-only / focus on mount

Remaining YearPickerRootProps and emits pass through, including date bounds. The tests verify that choosing a year emits a date value. Keep the provided Reka triggers for focus and keyboard handling; previous/next controls have accessible names. The DOM grid works in the game compositor.

SbYearRangePicker wraps Reka YearRangePickerRoot for a pair of year endpoints.

Service years, 2020 - 2031
2020 - 2031
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
YearRangePickerDemo.vue
<script setup lang="ts">
import { SbProvider, SbYearRangePicker } from '@static-bloom/vue'
</script>
<template>
<SbProvider locale="en-US">
<SbYearRangePicker calendar-label="Service years" />
</SbProvider>
</template>
<SbYearRangePicker>
<SbYearRangePickerHeader><SbYearRangePickerPrev /><SbYearRangePickerHeading /><SbYearRangePickerNext /></SbYearRangePickerHeader>
<SbYearRangePickerGrid><SbYearRangePickerGridBody><SbYearRangePickerGridRow>
<SbYearRangePickerCell><SbYearRangePickerCellTrigger /></SbYearRangePickerCell>
</SbYearRangePickerGridRow></SbYearRangePickerGridBody></SbYearRangePickerGrid>
</SbYearRangePicker>
Prop Type Default Effect
modelValue DateRange | null uncontrolled Selected year endpoints
defaultValue DateRange { start: undefined, end: undefined } Initial endpoints
yearsPerPage number 12 Years per view
maximumYears number unset Range length limit
calendarLabel string unset Accessible name
allowNonContiguousRanges, preventDeselect boolean false Unavailable-year ranges / deselection policy
fixedDate 'start' | 'end' unset Fixed endpoint
disabled, readonly, initialFocus boolean false Interaction lock / read-only / focus on mount

Remaining YearRangePickerRootProps and emits pass through, including year matchers and bounds. The tests verify selecting and emitting both endpoints. Preserve the cell trigger semantics for keyboard access and keep the calendar named. A game HUD can place this inline without opening a native control.

For all game date/time controls, define the server’s scheduling policy, keep keyboard focus on the WebUI while editing, and use the page’s flavor for portalled content or configure an appropriate teleport destination. In-game UIs covers CEF restrictions and the ready/focus lifecycle.