Vize

Time Field

Accessible, unstyled segmented time input with 12/24-hour clocks and hour/minute/second granularity.

Package @vizejs/ui/time-field
Maturity stable
Own the source vize lib pull time-field
Requires controllable-state, date-field, id, locale
Aliases time input, segmented time, clock input, hh:mm
Covers WAI-ARIA spinbutton, React Aria TimeField, Reka UI TimeField, HTML input type=time

Minimal setup

Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).

vp install @vizejs/ui

Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.

import { TimeField } from "@vizejs/ui/time-field";

Or copy the source into your project with vize lib pull time-field (see Source Distribution).

Try it

This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.

Open the interactive example · Styling these components

Copy the example

<!-- Controlled segmented time field with a fixed locale, business-hour bounds, and ISO output. -->
<script setup lang="ts">
import { ref, useId } from "vue";

import { createPlainTime, formatIsoTime, TimeField } from "@vizejs/ui/time-field";
import type { PlainTime } from "@vizejs/ui/time-field";

const start = ref<PlainTime | null>(createPlainTime(9, 30));
const labelId = useId();
</script>

<template>
  <div>
    <span :id="labelId">Meeting start</span>
    <TimeField
      v-model="start"
      name="start"
      locale="en-US"
      :min="createPlainTime(8)"
      :max="createPlainTime(18)"
      :aria-labelledby="labelId"
    />
    <output>{{ start ? formatIsoTime(start) : "No time selected" }}</output>
  </div>
</template>

API

TimeField

Source: src/families/date-time/time-field/time-field.vue

Props

Prop Type Default Description
id string | null | undefined undefined Consumer-owned group id; nullish values use a deterministic fallback.
name string | undefined undefined Hidden input name that submits HH:MM or HH:MM:SS with forms.
modelValue PlainTime | null | undefined undefined Controlled time; undefined selects uncontrolled mode and null clears.
defaultValue PlainTime | null | undefined null Initial uncontrolled time and native form-reset target.
min PlainTime | null | undefined undefined Earliest valid time; earlier values mark the field invalid.
max PlainTime | null | undefined undefined Latest valid time; later values mark the field invalid.
hourCycle HourCycle | undefined undefined 12 shows 1–12 plus an AM/PM segment, 24 shows 0–23; defaults to the locale clock.
granularity TimeGranularity | undefined "minute" Smallest edited unit; second adds a seconds segment.
placeholderValue PlainTime | null | undefined { hour: 0, minute: 0, second: 0 } Time whose segments seed arrow-key stepping from empty.
locale string | undefined undefined BCP 47 locale deciding segment order, clock, and labels; defaults to the nearest LocaleProvider.
dir "ltr" | "rtl" | undefined undefined Text direction for arrow-key segment movement; defaults to the nearest LocaleProvider.
disabled boolean | undefined false Remove every segment from focus and editing.
readOnly boolean | undefined false Keep segments focusable while blocking edits.
required boolean | undefined false Require a complete value; participates in native form validation.
invalidMessage string | undefined "Invalid value" Custom validity message reported while the value is out of range or unavailable.
placeholders FieldSegmentPlaceholders | undefined { hour: "––", minute: "––", second: "––", dayPeriod: "––" } Placeholder text for empty segments.
emptyText string | undefined "Empty" Text announced for empty segments.
ariaLabel string | undefined undefined Accessible name for the segment group.
ariaLabelledby string | undefined undefined Ids that label the segment group.
ariaDescribedby string | undefined undefined Ids that describe the group and every segment.
ariaErrormessage string | undefined undefined Id of the validation message used while invalid.
ariaInvalid boolean | undefined false Force the invalid state in addition to built-in min/max validation.

Events

Event Payload Description
update:modelValue [value: PlainTime | null] Fired when segments request a new controlled time or null while incomplete.
change [value: PlainTime | null, previous: PlainTime | null, nativeEvent: Event | null] Fired after a distinct committed time with the previous time and triggering event.

Slots

Slot Slot props Description
default TimeFieldSlotState Extra content after the segments, such as a picker trigger. Receives field state.
segment FieldSegmentState Segment content. Receives the segment state; defaults to its text.

Behavior

Normative state x input -> outcome table for time-field.vue (@vizejs/ui/time-field). TimeField shares the DateField segment engine, so DateField rows F2–F8, F10–F12, and F15 (native required validation) apply to hour, minute, second, and day-period segments. Every row below is proven by the named test.

# State Input Outcome Proven by
T1 locale clock render segment order, hour padding, and day-period labels follow Intl; hourCycle forces 12 or 24; granularity adds or removes minute/second segments renders locale clocks: 12-hour with a day period or 24-hour without
T2 12-hour, empty digits and a/p hours, minutes, and the day period fill in order and commit a 24-hour PlainTime; letters and arrows toggle AM/PM typing hours, minutes, and a day period commits a 24-hour value
T3 12-hour step 12 AM is midnight and 12 PM is noon; hours wrap 12→1 without flipping the period; minutes wrap and Page steps by 15 12 AM and 12 PM map to midnight and noon; hours wrap within the clock
T4 24-hour, seconds digits two-digit hours wait for the second digit when it can fit; second granularity commits only after seconds; no hidden input without name 24-hour typing accepts two-digit hours and seconds granularity requires seconds
T5 min / max commit / empty step out-of-window times are invalid; placeholderValue seeds stepping from empty min and max mark times invalid; placeholderValue seeds empty stepping
T6 disabled/read-only keys no edits are committed disabled and read-only time fields block edits
T7 imperative expose / clock change hourCycle, granularity, focus, setValue, clear; switching the clock re-renders the same time exposes hour cycle, granularity, and imperative editing
T8 time model helpers PlainTime validation, comparison, truncation, and HH:MM[:SS] round-trips plain time helpers validate, compare, truncate, and round-trip ISO text
T9 SSR/hydration isolated requests byte-identical segments and deterministic ids hydrate silently renders byte-identical locale-ordered time segments across SSR requests, hydrates time segments with generated ids and no mismatches

Root data adds data-hour-cycle (12/24) and data-granularity to the DateField hooks, with data-vize-ui="time-field", time-field-segment, and time-field-literal. The hidden input submits HH:MM, or HH:MM:SS for second granularity.