Skip to content

Date Picker

A calendar-backed date picker that returns a Temporal.PlainDate -- a date without time or timezone. Perfect for birthdays, deadlines, due dates, and any scenario where "which day" is all that matters.

ts
import { CoarPlainDatePicker } from '@cocoar/vue-ui';

Basic Usage

Click the input to open a calendar dropdown. The selected value is a proper Temporal.PlainDate, so date math and formatting are straightforward.

Selected: none
vue
<template>
  <div style="display: flex; flex-direction: column; gap: 12px; max-width: 320px;">
    <CoarFormField label="Select date">
      <CoarPlainDatePicker
        v-model="date"
        placeholder="DD.MM.YYYY"
      />
    </CoarFormField>
    <span style="font-size: 13px; color: #64748b;">Selected: {{ date?.toString() ?? 'none' }}</span>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { CoarPlainDatePicker, CoarFormField } from '@cocoar/vue-ui';
import type { Temporal } from '@js-temporal/polyfill';

const date = ref<Temporal.PlainDate | null>(null);
</script>

Required & Error States

Combine required and error props for form validation. The required asterisk and error message integrate with the same patterns as every other Cocoar input.

Enter your date of birth
Date is in the past
vue
<template>
  <div style="display: flex; flex-direction: column; gap: 12px; max-width: 320px;">
    <CoarFormField label="Birth date" hint="Enter your date of birth" required>
      <CoarPlainDatePicker
        placeholder="DD.MM.YYYY"
        :required="true"
      />
    </CoarFormField>
    <CoarFormField label="Expiry date" error="Date is in the past">
      <CoarPlainDatePicker
        placeholder="DD.MM.YYYY"
      />
    </CoarFormField>
  </div>
</template>

<script setup lang="ts">
import { CoarPlainDatePicker, CoarFormField } from '@cocoar/vue-ui';
</script>

Disabled & Readonly

disabled greys out the entire picker; readonly lets users see the selected date but prevents changes.

vue
<template>
  <div style="display: flex; flex-direction: column; gap: 12px; max-width: 320px;">
    <CoarFormField label="Disabled">
      <CoarPlainDatePicker placeholder="DD.MM.YYYY" :disabled="true" />
    </CoarFormField>
    <CoarFormField label="Readonly">
      <CoarPlainDatePicker placeholder="DD.MM.YYYY" :readonly="true" />
    </CoarFormField>
  </div>
</template>

<script setup lang="ts">
import { CoarPlainDatePicker, CoarFormField } from '@cocoar/vue-ui';
</script>

Date Range (Manual)

For start/end date pairs, use two pickers and pass the start date as :min on the end picker. This prevents users from selecting an end date before the start.

Range: ? → ?
vue
<template>
  <div style="display: flex; flex-direction: column; gap: 12px; max-width: 320px;">
    <CoarFormField label="Start date">
      <CoarPlainDatePicker v-model="start" placeholder="From" />
    </CoarFormField>
    <CoarFormField label="End date">
      <CoarPlainDatePicker v-model="end" placeholder="To" :min="start ?? undefined" />
    </CoarFormField>
    <span style="font-size: 13px; color: #64748b;">
      Range: {{ start?.toString() ?? '?' }} → {{ end?.toString() ?? '?' }}
    </span>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { CoarPlainDatePicker, CoarFormField } from '@cocoar/vue-ui';
import type { Temporal } from '@js-temporal/polyfill';

const start = ref<Temporal.PlainDate | null>(null);
const end = ref<Temporal.PlainDate | null>(null);
</script>

Sizes

Four sizes to stay consistent with other form controls across your layout.

vue
<template>
  <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px;">
    <CoarFormField label="Extra Small">
      <CoarPlainDatePicker size="xs" placeholder="DD.MM.YYYY" />
    </CoarFormField>
    <CoarFormField label="Small">
      <CoarPlainDatePicker size="s" placeholder="DD.MM.YYYY" />
    </CoarFormField>
    <CoarFormField label="Medium">
      <CoarPlainDatePicker size="m" placeholder="DD.MM.YYYY" />
    </CoarFormField>
    <CoarFormField label="Large">
      <CoarPlainDatePicker size="l" placeholder="DD.MM.YYYY" />
    </CoarFormField>
  </div>
</template>

<script setup lang="ts">
import { CoarPlainDatePicker, CoarFormField } from '@cocoar/vue-ui';
</script>

INFO

Temporal.PlainDate: This component uses the TC39 Temporal API (@js-temporal/polyfill). PlainDate represents a calendar date without time or timezone -- no more wrestling with midnight UTC offsets.

Accessibility

Keyboard Navigation

KeyAction
TabMove focus to input / calendar
EnterOpen calendar / select date
EscapeClose calendar dropdown
Arrow KeysNavigate within the calendar

Screen Reader Support

  • Label text announces on focus
  • Selected date is read aloud
  • Calendar navigation is keyboard accessible
  • Required and error states announced

API

Props

PropTypeDefaultDescription
v-modelTemporal.PlainDate | nullnullSelected date
labelstring''Label text
placeholderstring''Placeholder text
minTemporal.PlainDateundefinedMinimum selectable date
maxTemporal.PlainDateundefinedMaximum selectable date
size'xs' | 's' | 'm' | 'l''m'Input size
disabledbooleanfalseDisable the picker
readonlybooleanfalseMake read-only
requiredbooleanfalseMark as required
errorstring''Error message

i18n Keys

These keys can be translated via @cocoar/vue-localization.

KeyDefault (English)Used as
coar.ui.datePicker.previousYear'Previous year'Previous year button aria-label
coar.ui.datePicker.nextYear'Next year'Next year button aria-label
coar.ui.datePicker.jumpToToday'Jump to today\'s month'Scroll-to-today button aria-label
coar.ui.datePicker.months'Months'Month grid aria-label
coar.ui.datePicker.dialog'Date picker'Overlay dialog aria-label
coar.ui.datePicker.clearDate'Clear date'Clear button aria-label
coar.ui.datePicker.openPicker'Open picker'Calendar button aria-label

Released under the Apache-2.0 License.