Forms
Date picker
Chooses a date from a calendar, in any locale and direction.
Installation
npx sinaui add date-pickerCopy src/lib/ui/DatePicker.svelte, Calendar.svelte, Popover.svelte, floating.ts, announce.ts, Button.svelte, Icon.svelte and tokens.css, then install
its date library:
pnpm add @internationalized/dateValues are CalendarDates from that library: a day, with no time or time zone.
For dates typed in words, also copy NaturalDate.svelte and parse-date.ts (English and Bangla). For the Bangla calendar, copy calendars/bangla.ts.
Examples
Date picker
Opens on the chosen day, or today. Days outside the range or unavailable are crossed out and can't be chosen. With a name, a hidden input submits the date as YYYY-MM-DD.
Return date
October 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
Loans last up to 90 days. The library is closed on Fridays.
<script lang="ts">
import {
type CalendarDate,
getDayOfWeek,
getLocalTimeZone,
today
} from '@internationalized/date';
import DatePicker from '#lib/ui/DatePicker.svelte';
let due = $state<CalendarDate>();
const now = today(getLocalTimeZone());
</script>
<DatePicker
label="Return date"
bind:value={due}
min={now}
max={now.add({ days: 90 })}
isUnavailable={(d) => getDayOfWeek(d, 'en-US') === 5}
hint="Loans last up to 90 days. The library is closed on Fridays."
name="due"
/>Typed in words
Type “tomorrow”, “next friday”, “in 2 weeks”, “15 june” or 2026-10-02, or click an example to type it in: what it means shows underneath, and Enter or leaving the field takes it. The calendar button is still there for picking.
Choose return date
October 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
Type a date, like , , or .
<script lang="ts">
import {
type CalendarDate,
getDayOfWeek,
getLocalTimeZone,
today
} from '@internationalized/date';
import NaturalDate from '#lib/ui/NaturalDate.svelte';
let due = $state<CalendarDate>();
const now = today(getLocalTimeZone());
</script>
<NaturalDate
label="Return date"
bind:value={due}
min={now}
max={now.add({ days: 90 })}
isUnavailable={(d) => getDayOfWeek(d, 'en-US') === 5}
name="natural-due"
/>With an error
Start of the copying
October 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
Choose when the copyist should start.
<script lang="ts">
import type { CalendarDate } from '@internationalized/date';
import DatePicker from '#lib/ui/DatePicker.svelte';
let start = $state<CalendarDate>();
</script>
<DatePicker
label="Start of the copying"
bind:value={start}
error={start ? undefined : 'Choose when the copyist should start.'}
/>Calendar on its own
For a page where the calendar is the main thing, like booking a visit.
October 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
Booked for 2026-10-04.
<script lang="ts">
import { type CalendarDate, getLocalTimeZone, today } from '@internationalized/date';
import Calendar from '#lib/ui/Calendar.svelte';
let visit = $state<CalendarDate>(today(getLocalTimeZone()).add({ days: 3 }));
// Fully booked: every seventh day from today.
const booked = (d: CalendarDate) => d.day % 7 === 0;
</script>
<div class="booking">
<Calendar label="Reading room visit" bind:value={visit} isUnavailable={booked} />
<p>Booked for {visit.toString()}.</p>
</div>
<style>
.booking {
display: grid;
gap: 0.75rem;
}
.booking p {
margin: 0;
color: var(--ui-muted);
font-size: 0.875rem;
}
</style>Hijri calendar
Arabic, right to left, in the Umm al-Qura Hijri calendar: Hijri months and years, Arabic names and digits, arrow keys following the direction. The value stays a Gregorian date.
موعد الزيارة
ربيع الآخر ١٤٤٨ هـ
| ح | ن | ث | ر | خ | ج | س |
|---|---|---|---|---|---|---|
Stored as: —
<script lang="ts">
import type { CalendarDate } from '@internationalized/date';
import DatePicker from '#lib/ui/DatePicker.svelte';
let visit = $state<CalendarDate>();
</script>
<!-- Hijri months and years (Umm al-Qura), Arabic names and digits, right to left. -->
<div dir="rtl" lang="ar">
<DatePicker
label="موعد الزيارة"
placeholder="اختر تاريخًا"
locale="ar-SA"
calendar="islamic-umalqura"
bind:value={visit}
/>
</div>
<p>Stored as: {visit?.toString() ?? '—'}</p>Bangla calendar
বৈশাখ to চৈত্র with Bangla digits, as Bangladesh keeps its calendar (not West Bengal's). Pass bangla from calendars/bangla.ts as the calendar; the value stays a Gregorian date.
পরিদর্শনের তারিখ
আশ্বিন ১৪৩৩
| র | সো | ম | বু | বৃ | শু | শ |
|---|---|---|---|---|---|---|
Stored as: —
<script lang="ts">
import type { CalendarDate } from '@internationalized/date';
import DatePicker from '#lib/ui/DatePicker.svelte';
import { bangla } from '#lib/ui/calendars/bangla.js';
let visit = $state<CalendarDate>();
</script>
<!-- Bangabda: বৈশাখ, আষাঢ়, শ্রাবণ... with Bangla digits; the value stays a Gregorian date. -->
<div lang="bn">
<DatePicker
label="পরিদর্শনের তারিখ"
placeholder="তারিখ বেছে নিন"
locale="bn-BD"
calendar={bangla}
bind:value={visit}
/>
</div>
<p>Stored as: {visit?.toString() ?? '—'}</p>Props
| Prop | Type | Default |
|---|---|---|
label | string (required) | |
value | CalendarDate, bindable | |
min, max | CalendarDate | |
isUnavailable | (date) => boolean | |
locale | string | 'en' |
calendar | 'gregory' | 'islamic-umalqura' | 'persian' | … | 'gregory' |
name | string | |
placeholder, hint, error | string |
Calendar takes the same value, label, locale, min, max and isUnavailable, plus onchange.
Accessibility
- The calendar is a grid with one Tab stop: arrow keys move between days, and each day is read in full (“Saturday, 14 June 2025, today”).
- Changing month is announced, and today is marked by a dot as well as by name, not by color alone.
- Choosing a date closes the picker and puts focus back on it.
| Key | Action |
|---|---|
| ← → | Previous / next day (mirrored right to left). |
| ↑ ↓ | Same day, previous / next week. |
| Page Up Page Down | Previous / next month; with Shift, year. |
| Home End | Start / end of the week. |
| Enter Space | Choose the day. |
| Esc | Close without choosing. |