Kanzo UI
Forms

Dates

One machine with a switch — the month grid inline, or the same grid in a popover.

Ark UI

Calendar and DatePicker are one Ark machine with inline flipped: DatePicker is literally <Calendar inline={false}>. That is why the grid parts are shared, why the props are identical, and why this is one page — a machine with a switch is a variant, not a second component.

The grid is a real role="grid" with roving tabindex, arrow-key navigation and month/year quick-nav, and CalendarTableDays / CalendarWeekDays read the weeks and weekday names off the machine, so a locale change reflows it with no work on your side.

Usage

import {
  Calendar,
  CalendarTable,
  CalendarTableDays,
  CalendarView,
  CalendarViewControl,
  CalendarWeekDays,
  DatePicker,
  DatePickerContent,
  DatePickerInput,
} from "@kanzo-tech/ui";

Anatomy

Calendar | DatePicker
├── DatePickerInput          (the picker only: input + calendar button, or…)
├── DatePickerTrigger        (…a button showing DatePickerValue)
└── CalendarView             (in DatePickerContent when there is a popover)
    ├── CalendarViewControl
    │   ├── CalendarPrevTrigger
    │   ├── CalendarMonthSelect
    │   ├── CalendarYearSelect
    │   ├── CalendarViewDate
    │   └── CalendarNextTrigger
    └── CalendarTable
        ├── CalendarWeekDays
        ├── CalendarTableDays
        └── CalendarTableNextMonth

The month grid

Calendar fixes inline on, so the grid is always visible and there is no trigger or popover. Reach for it when the calendar is furniture — a booking screen, an availability view — rather than a form value.

28
29
30
31
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

Range

September 1312
28
29
30
31
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

Two months

CalendarTableNextMonth renders the month months ahead of the visible one, so a two-month range picker is two tables in one view rather than two calendars.

September 1312 - October 1312
28
29
30
31
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
25
26
27
28
29
30
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
31
1
2
3
4
5

With actions

28
29
30
31
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

The popover

DatePicker is the same grid behind an input and a positioned popover — the form shape.

With a trigger button

Range

An ISO string in and out

A form library, a JSON body and a database column all speak YYYY-MM-DD; this machine speaks Ark's DateValue. The adapter between them is two small functions, and it belongs at your seam rather than in the library — which side of it your product lives on is a product decision.

Stored as 2026-03-14

Reading a string out is one line, because onValueChange hands you both representations. Going the other way — hydrating a stored string back into the picker — needs parseDate, which @kanzo-tech/ui re-exports along with the DateValue type, for the same reason it re-exports createListCollection: a machine whose value you cannot build from the barrel is not usable from the barrel. You do not install @internationalized/date. Wrap the parse in a try/catch: an unparseable stored value must leave the calendar empty rather than throw it, or one bad row in a table takes down the whole form.

Keyboard

KeyDoes
/ Moves the focused day back / forward one day, crossing into the neighbouring month.
/ Moves it back / forward one week.
Home / EndJumps to the first / last day of the visible range.
PageUp / PageDownMoves back / forward one month.
Shift+PageUp / PageDownMoves back / forward one year.
EnterSelects the focused day; in the month or year view it drills down to the next view instead.
EscCloses the popover without changing the value, and returns focus to the trigger that opened it — DatePicker only, since Calendar is inline.

The grid is a roving tabindex: only the focused cell is reachable with Tab, so Tab leaves the month rather than walking its days.

API Reference

DatePickerInput

PropTypeDefault
size"sm" | "md" | "lg""md"
indexnumber0

Renders DatePicker.Control, an InputGroup and the calendar trigger button for you.

CalendarWeekDays

PropTypeDefault
format"narrow" | "short" | "long""narrow"

CalendarTableNextMonth

PropTypeDefault
monthsnumber1

Both roots extend DatePicker.Root from Ark, so value, defaultValue, onValueChange, selectionMode, numOfMonths, min, max, locale, startOfWeek, fixedWeeks, isDateUnavailable, open, disabled and readOnly pass through. lazyMount and unmountOnExit default to true; DatePicker's positioning defaults to { placement: "top" }.

The parts the Anatomy does not show

The tree above is the composition you write. These ship as well, and they are the tier below it: reach for them when you are rearranging the grid rather than using it.

The table, by hand. CalendarWeekDays and CalendarTableDays exist so you never write these — the first renders the header row, the second the weeks. Both are assembled out of the five below, and the five are exported so a caller who wants a different cell can build the same table around it.

PartWhat it is
CalendarTableHeadThe <thead>.
CalendarTableHeaderOne weekday-name cell inside it.
CalendarTableBodyThe <tbody>.
CalendarTableRowOne week.
CalendarTableCellOne day. Takes the day as value, and its children are the label.

There is no CalendarTableCellTrigger on our surface and that is deliberate: CalendarTableCell renders Ark's cell and the trigger inside it, because every state a day carries — selected, in range, today, unavailable — is painted on the trigger while the rounding of a range's ends is painted on the cell. Two parts that can never be composed apart are one part.

The picker's own shell. DatePickerInput renders the control and the trigger for you; these are what it is made of.

PartWhat it is
CalendarControlThe row holding the input and the trigger.
CalendarLabelThe field's label.
CalendarTriggerThe button that opens the popover.
CalendarPresetTriggerA button that sets a value — Ark's presets, so value="last7Days" and the rest.
CalendarTodayTriggerAn outline Button reading "Today" that calls selectToday(). It renders its own CalendarContext to reach the machine, so it needs nothing passed to it.
CalendarClearTriggerArk's clear trigger, unstyled — pass a Button with asChild and the label is yours.
CalendarContextThe render prop. Takes a function, renders no element of its own, and is how CalendarTableDays reads the weeks.

DatePicker names two more, and both are worth knowing for what they are rather than what they sound like. DatePickerPresetTrigger is CalendarPresetTrigger under the picker's own data-slot — the same shared machine, the same part. And DatePickerTimer is a time field, not a countdown: an InputGroup around <input type="time" step="1"> with a clock addon, for the half of a timestamp a calendar cannot express.

On this page