Value: 2026-09-04
<script setup lang="ts">
import { ref } from 'vue'
import { DatePicker, Stack, Text } from '@hina-ui/vue'
const date = ref<string | null>('2026-09-04')
</script>
<template>
<Stack gap="sm" align="start">
<DatePicker v-model="date" clearable aria-label="Publish date" class="w-72" />
<Text tone="muted" size="sm">Value: {{ date ?? 'empty' }}</Text>
</Stack>
</template>
Usage
import { DatePicker } from '@hina-ui/vue'
The date picker puts the segmented DateField and the Calendar together: type into the segments, or open the calendar from the button at the end and pick a day. v-model binds an ISO 8601 string in the YYYY-MM-DD form, the same as both parts. Attributes it does not declare land on the group element that wraps the segments, so name it with aria-label or aria-labelledby.
Value: empty
<script setup lang="ts">
import { ref } from 'vue'
import { DatePicker, Stack, Text } from '@hina-ui/vue'
const date = ref<string | null>(null)
</script>
<template>
<Stack gap="sm" align="start">
<DatePicker v-model="date" aria-label="Release date" class="w-72" />
<Text tone="muted" size="sm">Value: {{ date ?? 'empty' }}</Text>
</Stack>
</template>
Examples
Range and unavailable days
min and max constrain both the field and the calendar; unavailable strikes days out.
<script setup lang="ts">
import { ref } from 'vue'
import { DatePicker } from '@hina-ui/vue'
const date = ref<string | null>('2026-09-10')
const weekend = (value: string) => [0, 6].includes(new Date(`${value}T00:00`).getDay())
</script>
<template>
<DatePicker
v-model="date"
min="2026-09-01"
max="2026-09-30"
:unavailable="weekend"
aria-label="Event date"
class="w-72"
/>
</template>
Clearable
clearable adds a clear button after the segments; clearing sets the value to null.
<script setup lang="ts">
import { ref } from 'vue'
import { DatePicker } from '@hina-ui/vue'
const date = ref<string | null>('2026-09-04')
</script>
<template>
<DatePicker v-model="date" clearable aria-label="Publish date" class="w-72" />
</template>
Sizes
size has three steps, sm, md and lg, matching the input of the same step; the calendar in the popover uses the same step.
<script setup lang="ts">
import { DatePicker, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-72">
<DatePicker size="sm" model-value="2026-09-04" aria-label="Small" />
<DatePicker size="md" model-value="2026-09-04" aria-label="Medium" />
<DatePicker size="lg" model-value="2026-09-04" aria-label="Large" />
</Stack>
</template>
States
invalid gives the field a warning color, disabled disables the group and readonly makes it read only. variant="secondary" is the flat form used inside a surface.
<script setup lang="ts">
import { DatePicker, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-72">
<DatePicker model-value="2026-09-04" invalid aria-label="Invalid" />
<DatePicker model-value="2026-09-04" disabled aria-label="Disabled" />
<DatePicker model-value="2026-09-04" readonly aria-label="Read only" />
<DatePicker model-value="2026-09-04" variant="secondary" aria-label="Secondary" />
</Stack>
</template>
In a form
Inside a FormField the label is linked to the input, and the description and error message are rendered by the field; validation rules and submission belong to the Form.
<script setup lang="ts">
import { reactive, ref } from 'vue'
import * as v from 'valibot'
import { Button, DatePicker, Form, FormField, Text } from '@hina-ui/vue'
const schema = v.object({
released: v.string('Pick a release date'),
})
const values = reactive({ released: null as string | null })
const saved = ref('')
async function save(data: unknown) {
await new Promise(resolve => setTimeout(resolve, 600))
saved.value = JSON.stringify(data)
}
</script>
<template>
<Form v-slot="{ submitting }" :values="values" :rules="schema" class="w-80" @submit="save">
<FormField name="released" label="Release date" required>
<DatePicker v-model="values.released" />
</FormField>
<Button type="submit" :loading="submitting" class="self-start">Save</Button>
<Text v-if="saved" tone="muted" size="sm">Saved: {{ saved }}</Text>
</Form>
</template>
Behavior
- The button at the end opens the calendar with focus on the picked day; picking a day writes the value, closes the calendar and returns focus to the button.
- The calendar is a modal popover: focus stays inside it while open, page scrolling is locked, and clicking anywhere outside it, the field included, closes it.
- Esc closes the calendar and returns focus to the button; inside its month and year views Esc steps back first.
- The calendar opens below the field aligned to its start edge.
Accessibility
- The segments and the calendar carry the semantics described for
DateFieldandCalendar. - The button that opens the calendar has a name from the locale and
aria-expanded. - Name the group with
aria-labeloraria-labelledby.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | null | null | The picked day, YYYY-MM-DD |
open | boolean | false | Whether the calendar is open; v-model:open |
placeholder | string | today | Starting point of the empty field and calendar |
min | string | — | Lower bound of the selectable range |
max | string | — | Upper bound of the selectable range |
unavailable | (date: string) => boolean | — | Whether a given day cannot be picked |
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 the calendar always shows six weeks |
clearable | boolean | false | Whether the clear button is shown |
readonly | boolean | false | Whether the field is read only |
name | string | — | Form field name |
variant | 'primary' | 'secondary' | 'primary' | Variant |
size | 'sm' | 'md' | 'lg' | 'md' | Size |
disabled | boolean | false | Whether the group is disabled |
invalid | boolean | false | Whether the field failed validation |
class | string | — | Classes appended to the root element |
Slots
| Slot | Description |
|---|---|
leading | Content of the leading adornment |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | value: string | null | The value changed |
update:open | open: boolean | The calendar opened or closed |
clear | — | The clear was used |