Calendar
The calendar
component provides a way to select a date.
Features
Keyboard navigation
- Space or Enter on a date selects it.
- Arrow Up, Arrow Right, Arrow Down, or Arrow Left navigates between dates.
- Home or End navigates to the first or last day of the month.
- Page Up or Page Down navigates to the same day in the previous or next month.
Example
You have selected: nothing
July 2023
Sun
Mon
Tue
Wed
Thu
Fri
Sat
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
Sources
+page.svelte
<script lang="ts">
import Calendar from './Calendar.svelte'
let date: Date | null = null
</script>
<p class="mb-4 text-sm opacity-60">
You have selected: <strong>{date ? date.toDateString() : 'nothing'}</strong>
</p>
<Calendar bind:date />
Calendar.svelte
<script lang="ts">
import { createCalendar } from 'louisette'
export let date: Date | null = null
const {
calendarAttrs,
nextButtonAttrs,
prevButtonAttrs,
title,
days,
weekdays,
selected,
} = createCalendar({
selected: [date],
})
// Update the date when the selected date changes (2-way binding)
$: date = $selected[0] ? new Date($selected[0]) : null
</script>
<div
{...$calendarAttrs}
class="flex max-w-md flex-col overflow-clip rounded-lg border border-neutral-200 bg-white p-4 text-neutral-900 dark:border-neutral-600 dark:bg-neutral-800 dark:text-neutral-100"
>
<div class="mb-4 flex items-center justify-between">
<button
type="button"
class="mx-4 rounded-md p-2 text-center transition-colors hover:bg-neutral-100 dark:hover:bg-neutral-700"
{...$prevButtonAttrs}
aria-label="Previous month"
>
<svg
class="h-6 w-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 19l-7-7 7-7"
/>
</svg>
</button>
<div class="flex-grow text-center font-bold">{$title}</div>
<button
type="button"
class="mx-4 rounded-md p-2 text-center transition-colors hover:bg-neutral-100 dark:hover:bg-neutral-700"
{...$nextButtonAttrs}
aria-label="Next month"
>
<svg
class="h-6 w-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</button>
</div>
<div>
<div
class="mb-2 grid w-full grid-cols-7 gap-1 text-center text-xs font-bold uppercase"
>
{#each $weekdays as weekday, i}
<div aria-label={weekday}>{weekday.slice(0, 3)}</div>
{/each}
</div>
<div role="grid" class="grid w-full grid-cols-7 gap-1">
{#each $days as day, i}
<div
role="gridcell"
class="w-full cursor-pointer select-none rounded-md p-2 text-center transition-colors hover:bg-neutral-100 focus:outline-none dark:hover:bg-neutral-700"
class:is-selected={day.isSelected}
class:is-today={day.isToday}
class:is-out-of-month={day.isOutOfMonth}
{...day.dayAttrs}
>
<slot name="day" {...day}>
{day.date.getDate()}
</slot>
</div>
{/each}
</div>
</div>
</div>
<style lang="postcss">
.is-selected {
@apply bg-accent-500 text-white !important;
}
.is-today {
@apply font-bold underline underline-offset-4;
}
.is-out-of-month {
@apply opacity-20;
}
</style>