TimeInputbeta
A field for entering a time of day: hour, minute and optional seconds cells, stepped with the Arrow keys and shown in 12- or 24-hour. The bound value is a 24-hour string (HH:MM, or HH:MM:SS with seconds), and null while any segment is empty.
Playground
<TimeInput />01 Examples
Display format vs. bound value
Value: 09:30
Value (24h): 14:15
Value: 13:45:30
<script>
import { TimeInput } from '@urbicon-ui/blocks';
let startTime = $state('09:30');
let meetingTime = $state('14:15');
let preciseTime = $state('13:45:30');
</script>
<TimeInput label="Start" bind:value={startTime} />
<TimeInput label="Meeting" format="12h" bind:value={meetingTime} />
<TimeInput label="Duration" withSeconds bind:value={preciseTime} />Range bounds
Value: 09:00
<script>
let officeTime = $state('09:00');
</script>
<TimeInput
label="Appointment"
min="08:00"
max="18:00"
helper="Office hours"
bind:value={officeTime}
/>Error state
Value: —
<script>
let errorTime = $state(null);
</script>
<TimeInput label="Time" error="Please pick a time" bind:value={errorTime} />02 Date + Time
TimeInput is the form family's time field: Calendar, DatePicker and DateRangePicker are for dates, TimeInput for the time of day. It edits only the time, so for a full timestamp pair it with a DatePicker as two separate fields. Each keeps its own value — an ISO date from
the picker, an HH:MM string from the time field — and you join them yourself, as
the example below does. What you get is a local wall-clock time, not a point in time: turning 2026-08-15T14:30 into an instant needs a time zone, and that decision stays with you.
Date and time in one row
Date: 2026-08-15 · Time: 14:30 · Joined: 2026-08-15T14:30
<script>
import { DatePicker, TimeInput } from '@urbicon-ui/blocks';
let apptDate = $state('2026-08-15');
let apptTime = $state('14:30');
// A local wall-clock string. Give it a time zone before it becomes an instant.
const startsAt = $derived(apptDate && apptTime ? `${apptDate}T${apptTime}` : null);
</script>
<div class="flex flex-wrap items-end gap-3">
<DatePicker label="Date" class="w-auto" bind:value={apptDate} />
<TimeInput label="Time" class="w-auto" bind:value={apptTime} />
</div>03 Customization
For a reusable look, register a named preset on <BlocksProvider>; for individual parts, use slotClasses. The
slots are wrapper (what class also targets), label, field, icon (replace the clock with your own snippet via the icon prop), segment, separator, meridiem, and message. For a full ground-up restyle, set unstyled to drop every default class and rebuild from the slots.
showIcon={false} hides the leading clock icon, and fullWidth stretches the field to fill its container instead of hugging its content.
Boarding-pass segments via slotClasses
<TimeInput
label="Departure"
bind:value={departure}
slotClasses={{
field: 'gap-1 border-transparent bg-transparent px-0',
segment: 'rounded-modify border border-border-default bg-surface-subtle px-2 py-1 font-mono',
separator: 'text-text-tertiary'
}}
/>04 Accessibility
Group semantics
The field is a role="group" named by its label (or aria-label).
Per-segment naming
Each segment (hour, minute, and, when present, second) carries its own aria-label.
Keyboard
Arrow Up / Down moves the focused segment by one and wraps inside it — 59
goes to 00 without carrying the hour. Arrow Left / Right moves between
segments; typing digits auto-advances to the next. min and max do not
limit stepping; they apply on blur.
The AM/PM segment
The AM/PM segment toggles by click, the Arrow keys, Enter / Space, or the A / P keys.
Clamping
Out-of-range values clamp to min / max when the field loses focus.
Errors are announced
The error message is announced via role="alert".
05 API Reference
28 propsProp | Type | Default | Description | |
|---|---|---|---|---|
aria-label | string | — | Accessible name for the field group when no visible label is set. | |
class | string | — | Extra classes merged onto the root wrapper. | |
disabled | boolean | false | Blocks input and dims the whole field. | |
error | string | — | Error message below the field. When set it overrides helper, colours the
field danger, and marks the segments aria-invalid. | |
format | 12h24h | '24h' | Display the hour as 12-hour with an AM/PM segment. The value stays 24-hour. | |
fullWidth | boolean | false | Stretch the field to the full width of its container. | |
helper | string | — | Helper text below the field — hidden when error is present. | |
icon | Snippet | — | A custom leading icon; replaces the default clock. | |
id | string | — | Root id; the segments derive their ids and ARIA wiring from it. | |
intent variant | dangerdefaultsuccesswarning | default | Controls the color theme and semantic meaning of the TimeInput. Affects the overall appearance and user perception. Available options: danger, default, success, warning. | |
label | string | — | Label text displayed above the field, linked via aria-labelledby. | |
max | string | — | Latest allowed time, canonical 24-hour HH:MM(:SS). Values above it are
clamped down on blur. | |
messageType variant | errorhelper | helper | Controls the messageType behavior and appearance of the TimeInput component. Available options: error, helper. | |
min | string | — | Earliest allowed time, canonical 24-hour HH:MM(:SS). Values below it are
clamped up on blur. | |
name | string | — | Name for a hidden input carrying the canonical value, for native form submission. | |
onValueChange | (value: string | null) => void | — | Fires after any change with the canonical 24-hour value (or null). | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ TimeInput: {...} }}>. | |
readonly | boolean | false | Shows the time but refuses edits; the segments stay focusable. | |
required | boolean | false | Marks the label with an asterisk and sets aria-required on the segments. It does
not block a native submit — the value lives in component state, so validate it
yourself before you act on it. | |
showIcon | boolean | true | Show the leading clock icon. | |
size variant | lgmdsm | md | Controls the dimensions, padding, and text size of the TimeInput. Affects the component's physical footprint. Available options: lg, md, sm. | |
slotClasses | Partial<Record<TimeInputSlots, string>> | — | Per-slot class overrides merged with tv() styles. Slots: wrapper (what
class also targets) | label | field | icon | segment | separator |
meridiem | message. | |
tier variant | commitmodify | modify | Selects the semantic radius tier of the TimeInput — the shape family it belongs to (--radius-commit/-modify/-contain/-bridge). Shape is retuned per family in your theme, so this picks the family rather than a pixel value. Available options: commit, modify. | |
unstyled | boolean | — | Remove all default tv() classes — only user-provided classes apply. | |
value | string | null | — | Current time as a canonical 24-hour HH:MM / HH:MM:SS string; null when
empty. The stored format never changes with format. Supports bind:value. | |
variant variant | filledghostoutlined | outlined | Controls the visual style and presentation of the TimeInput. Determines the component's visual treatment. Available options: filled, ghost, outlined. | |
withSeconds | boolean | false | Add a seconds segment. | |
...TimeInputVariants variant | VariantProps | — | Styling variants from TimeInputVariants |
06 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
TimeInputProps | interface | props | 0 | — | |
TimeInputSlots | type | variant | 0 | Slot names derived from the tv() config above — single source of truth for slotClasses. | |
TimeInputVariants | type | variant | 0 | — | |
SlotNames | type | helper | 0 | Extracts the slot-name union from a slotted tv() config function — the
companion to VariantProps. The slot-mode overload returns
(props?) => { [K in keyof S]: SlotFn }, so keyof ReturnType<T> is exactly
the set of slot names a component declares in tv({ slots: … }).
Use it to type a component's slotClasses prop from the single source of
truth (its *.variants.ts) instead of hand-maintaining a parallel union
that silently drifts when a slot is added or renamed: | |
VariantProps | type | helper | 1 | — |
07 Installation
Import
import { TimeInput } from '@urbicon-ui/blocks';