Time Field
A time of day as one typed field — "HH:mm" in and out, shown on the locale's 12- or 24-hour clock, stepped with the arrow keys.
- Status
- Since
0.39.0- Accessibility pattern
- APG spinbutton, typed entry, min/max
Last updated
Type “930” or “9:30 pm”, or use ↑ and ↓.
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Time Field before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify time-fieldInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/time-fieldRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The same command installs the registry items it composes: @vegastack/date-time, @vegastack/input-group.
Usage
import { TimeField } from "@/components/ui/time-field";
const [time, setTime] = useState<string | null>("09:00");
<TimeField aria-label="Time" value={time} onChange={setTime} />;The value is always "HH:mm" on a 24-hour clock, whatever the field shows: hourCycle={12} shows
"9:30 AM", 24 shows "09:30", and the locale decides when it is left out. Typing accepts "9",
"930", "9:30", "9.30 pm" or "21:30"; Enter or leaving the field sets it. A typed time
outside min/max moves inside, and text that is not a time is marked invalid until it is fixed or
left, when the field shows the value again. Clearing the field sends null. min is not after
max: an overnight window is not a range this field holds.
The field holds a wall-clock time, never an instant: say which zone it is in next to it (a hint, or
the RecurrencePicker's time-zone line).
Scope
A time of day on its own: a schedule's "at", a reminder, opening hours. A date and a time together
is the DateTimePicker; a duration is a NumberField with a unit.
Examples
Type “930” or “9:30 pm”, or use ↑ and ↓.
States
The 24-hour clock, a 30-minute step bounded by min and max, empty with a placeholder, invalid
and disabled.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
onChange* | (value: string | null) => void | — | Fires with the new time ("HH:mm"), or null when the field is cleared. |
value* | string | null | — | The time, "HH:mm" on a 24-hour clock; null for none. |
aria-describedby | string | — | Ids of the elements describing the field — a hint or an error. |
aria-invalid | "false" | "grammar" | "spelling" | "true" | boolean | — | Marks the field invalid (the destructive border). |
aria-label | string | — | The field's accessible name, when no <label> names it. |
aria-labelledby | string | — | Ids of the elements naming the field. |
className | string | — | Classes for the bordered box (its width: w-36 by default). |
disabled | boolean | false | Disable the field. |
hourCycle | 12 | 24 | — | 12 shows "9:30 AM", 24 shows "09:30". Omit it and the locale decides. |
id | string | — | id for the input, so a Label htmlFor names it. |
locale | string | "en-US" | BCP-47 locale for the clock and for reading AM/PM words. |
max | string | — | The latest time, "HH:mm"; a later one is lowered to it. It is not before min: an overnight
window ("22:00" to "06:00") is not a range this field holds. |
min | string | — | The earliest time, "HH:mm"; an earlier one is raised to it. |
placeholder | string | — | Text shown in the empty field. |
ref | React.Ref<HTMLInputElement> | — | Ref forwarded to the input. |
step | number | 15 | Minutes the arrow keys move the time; it snaps to this grid (15 → :00, :15, :30, :45), so
23:45 is the last stop unless max says otherwise. A typed time may fall between steps. |
Data attributes and CSS variables on TimeField
| Attribute | Values |
|---|---|
data-slot | "time-field" |
Accessibility
- The input is a spin button: its value is the minutes since midnight, read out as the formatted
time ("9:30 AM"), with
minandmaxas its bounds. Name it with a label oraria-label. - Text that is not a time sets
aria-invalidwhile it is in the field.
| Key | Action |
|---|---|
| ↑ / ↓ | Step by step minutes, on the step's grid. |
| Page Up / Page Down | Step by an hour. |
| Home / End | Jump to min / max, when set and not typing. |
| Enter | Set the typed time. |
| Esc | Drop what was typed; a second press reaches the host. |
| Contract | States tested |
|---|---|
| Behaviour | default, empty, stepping, clamped, typed, invalid-draft, cleared, disabled, invalid |
| Accessibility | keyboard, labeled, spinbutton, browser-accessibility-test |
| Visual | default, focus, invalid, disabled, dark |
Do / Don't
Date Picker
Pick a single date, a date and a time, or a date range from a calendar popover — token-styled, keyboard-navigable, with optional quick presets.
Recurrence Picker
The body of a repeat popover — presets, a custom editor (every n days, weeks, months or years; weekdays; month day; ends) and the host's next dates.