RangeCalendar

Shows a month and picks a date range.

Calendar, September 2026
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
2
3
4
5
6
7
8
9
10

Value: 2026-09-04 to 2026-09-12

<script setup lang="ts">
  import { ref } from 'vue'
  import { RangeCalendar, Stack, Text, type DateRangeValue } from '@hina-ui/vue'

  const range = ref<DateRangeValue | null>({ start: '2026-09-04', end: '2026-09-12' })
</script>

<template>
  <Stack gap="sm" align="start">
    <RangeCalendar v-model="range" />
    <Text tone="muted" size="sm">
      Value: {{ range ? `${range.start ?? 'empty'} to ${range.end ?? 'empty'}` : 'empty' }}
    </Text>
  </Stack>
</template>
vue

Usage

import { RangeCalendar } from '@hina-ui/vue'
ts

The range calendar shares its grid, heading and month and year views with Calendar; the difference is that it picks a span: click one date as the start, then another as the end. Start and end render as solid cells and the dates between them join into a tinted band. v-model binds a { start, end } object whose fields are YYYY-MM-DD ISO 8601 strings, the same shape as DateRangeField, and is null when nothing is selected.

Calendar, September 2026
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
2
3
4
5
6
7
8
9
10

Value: empty

<script setup lang="ts">
  import { ref } from 'vue'
  import { RangeCalendar, Stack, Text, type DateRangeValue } from '@hina-ui/vue'

  const range = ref<DateRangeValue | null>(null)
</script>

<template>
  <Stack gap="sm" align="start">
    <RangeCalendar v-model="range" placeholder="2026-09-01" />
    <Text tone="muted" size="sm">
      Value: {{ range ? `${range.start ?? 'empty'} to ${range.end ?? 'empty'}` : 'empty' }}
    </Text>
  </Stack>
</template>
vue

Examples

Range

min and max bound the selectable range and disable the dates outside it. maximumDays caps how many days a span may contain: once a start date is picked, the dates beyond the cap are disabled until the span is complete.

Calendar, September 2026
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
2
3
4
5
6
7
8
9
10
<script setup lang="ts">
  import { ref } from 'vue'
  import { RangeCalendar, type DateRangeValue } from '@hina-ui/vue'

  const range = ref<DateRangeValue | null>({ start: '2026-09-10', end: '2026-09-14' })
</script>

<template>
  <RangeCalendar v-model="range" min="2026-09-07" max="2026-09-25" :maximum-days="7" />
</template>
vue

Unavailable dates

unavailable receives a YYYY-MM-DD string and returns whether that date is unavailable; unavailable dates are struck through. A span cannot cross an unavailable date.

Calendar, September 2026
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
2
3
4
5
6
7
8
9
10
<script setup lang="ts">
  import { ref } from 'vue'
  import { RangeCalendar, type DateRangeValue } from '@hina-ui/vue'

  const range = ref<DateRangeValue | null>({ start: '2026-09-07', end: '2026-09-11' })
  const weekend = (value: string) => [0, 6].includes(new Date(`${value}T00:00`).getDay())
</script>

<template>
  <RangeCalendar v-model="range" :unavailable="weekend" />
</template>
vue

Sizes

size has three steps, sm, md and lg, identical to the same step of Calendar.

Calendar, September 2026
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
2
3
4
5
6
7
8
9
10
Calendar, September 2026
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
2
3
4
5
6
7
8
9
10
Calendar, September 2026
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
2
3
4
5
6
7
8
9
10
<script setup lang="ts">
  import { RangeCalendar, Stack } from '@hina-ui/vue'

  const range = { start: '2026-09-04', end: '2026-09-12' }
</script>

<template>
  <Stack gap="lg" align="start">
    <RangeCalendar size="sm" :model-value="range" />
    <RangeCalendar size="md" :model-value="range" />
    <RangeCalendar size="lg" :model-value="range" />
  </Stack>
</template>
vue

States

readonly keeps the calendar navigable by keyboard but not selectable; disabled disables the whole calendar.

Calendar, September 2026
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
2
3
4
5
6
7
8
9
10
Calendar, September 2026
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
2
3
4
5
6
7
8
9
10
<script setup lang="ts">
  import { RangeCalendar, Inline } from '@hina-ui/vue'

  const range = { start: '2026-09-04', end: '2026-09-12' }
</script>

<template>
  <Inline gap="lg" align="start">
    <RangeCalendar :model-value="range" readonly />
    <RangeCalendar :model-value="range" disabled />
  </Inline>
</template>
vue

Behavior

  • The first click sets the start date, at which point the value is { start, end: null }; the second click sets the end. Once a span is set, clicking any date starts a new span from it.
  • After the start is set, hovering or moving the focus to another date previews the band in between.
  • An end before the start swaps the two.
  • Keyboard handling, the month and year views, paging and placeholder are the same as Calendar.

Accessibility

  • The root carries the localized name and the current month, the grid is role="grid" and every cell inside the span carries aria-selected.
  • The whole grid is one Tab stop; the arrow keys move the focus between dates and the focus ring only appears for keyboard use.

API

Props

Prop
Type
Default
Description
modelValue
DateRangeValue | null
null
The selected span, both ends YYYY-MM-DD
placeholder
string
This month
The month shown, supports v-model:placeholder
min
string
—
Lower bound of the selectable range
max
string
—
Upper bound of the selectable range
maximumDays
number
—
Maximum number of days a span may cover
unavailable
(date: string) => boolean
—
Whether a given date is unavailable
weekStartsOn
0 | 1 | 2 | 3 | 4 | 5 | 6
Locale
First day of the week
weekdayFormat
'narrow' | 'short'
'narrow'
Format of the weekday names
fixedWeeks
boolean
true
Whether six weeks are always shown
size
'sm' | 'md' | 'lg'
'md'
Size
autofocus
boolean
false
Whether to focus the start date on mount
readonly
boolean
false
Whether the calendar is read only
disabled
boolean
false
Whether the calendar is disabled
class
string
—
Classes appended to the root element

DateRangeValue is { start: string | null; end: string | null } and is exported from the package entry.

Events

Event
Payload
Description
update:modelValue
value: DateRangeValue | null
The span changed
update:placeholder
value: string
The shown month changed, with a date inside the new view