Time Picker
Free12h/24h scroll-column time picker with hour/minute/second wheels, AM/PM selector, Now button and 3 sizes.
import { SvTimePickerComponent } from '@styloviz/forms';Demo & usage
Basic usage
Usage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
24-hour with seconds
<sv-time-picker format="24h" [withSeconds]="true" [(ngModel)]="time" />
Type the time instead of scrolling to it
The panel opens with a text field focused, so the keyboard path to any time is Tab, Enter, type, Enter. It accepts 14:30, 14.30, 1430, 2:30 PM, 2:30pm and the same forms in Arabic-Indic, Bengali or fullwidth digits. Set [typed]="false" to remove the field; the columns remain.
<!-- on by default --> <sv-time-picker format="24h" [(ngModel)]="time" /> <!-- wheels only --> <sv-time-picker [typed]="false" [(ngModel)]="time" />
When to use
- Selecting a time of day with hour/minute (and optional second) precision.
- You want scroll-column wheels and a "Now" shortcut.
- Pair with a Date Picker when you need a full timestamp.
Async states
None — this component has nothing to load, so it has no loading, empty or error state to drive.
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| value | string (two-way) | '09:00' | Selected time as `"HH:mm"`, or `"HH:mm:ss"` with `withSeconds`. Two-way. |
| format | TimePickerFormat | '12h' | Clock format: 12h or 24h. |
| size | TimePickerSize | 'md' | Control size: sm, md or lg. |
| disabled | boolean | false | Disable the picker. |
| label | string | '' | Accessible label for the control. |
| min | string | '' | Earliest selectable time ("HH:mm"). Earlier cells are disabled. |
| max | string | '' | Latest selectable time ("HH:mm"). Later cells are disabled. |
| withSeconds | boolean | false | Include a seconds column. |
| typed | boolean | true | Offer a text field inside the panel so the time can be TYPED. |
| errorMessage | string | '' | Client-side validation message. |
| serverError | SvServerError | null | A rejection reported by the server about this value. Self-expiring. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| changed | string | Emitted with the new time string when it changes. |
| valueChange | string | Emits the new `value` whenever it changes — the output half of `[(value)]`. |
Customization & theming
- 12-hour or 24-hour mode with optional seconds, plus min/max bounds and a "Now" shortcut.
- The value is a wall-clock `HH:mm` (or `HH:mm:ss`) string — not an instant, and not zoned. Pair it with the Date Picker's `YYYY-MM-DD` calendar date through `svComposeDateTime` when you need a real timestamp.
- Three sizes; the trigger shows the selected time with a chevron and is named via `label`.
- Two-way bind `[(value)]` or a reactive control (CVA); listen to `(changed)`.
- `typed` (default `true`) puts a text field at the top of the panel and focuses it on open. It parses `14:30`, `14.30`, `1430`, `143015`, `2:30 PM`, `2:30pm` and the same forms written in Arabic-Indic, Bengali, Devanagari or fullwidth digits, plus day-period markers in Arabic, Japanese and Korean. It commits on Enter and on blur — never mid-keystroke, because `1` and `14` are both valid times on the way to `14:30`.
Best practices
- Do: Match `format` (12h/24h) to the user's locale conventions.
- Don't: Don't compose a timestamp with `new Date(date + "T" + time)`. The value here is a WALL-CLOCK time and the date picker's is a CALENDAR DATE — neither is an instant — so that expression resolves in the browser's zone, and a booking entered by a London scheduler for a Dublin clinic is stored an hour out with nothing reporting a problem.
- Do: Use `svComposeDateTime(date, time, zone)` from `@styloviz/core`. Twice a year the composition is not a function: London on 2026-03-29 has no 01:30 (the clocks go 01:00 → 02:00, and `new Date` silently returns 02:30), and on 2026-10-25 it has two. It returns `status: "ok" | "gap" | "ambiguous"` plus both instants when ambiguous, so a booking flow can say "that hour does not exist on this date" rather than move the appointment. The transition is not always an hour — Australia/Lord_Howe shifts by thirty minutes.
- Do: Only enable `withSeconds` when seconds genuinely matter.
- Tip: Constrain selectable times with `min`/`max` for booking-style flows.
- Do: Leave `typed` on. Before it, the open panel was 146 tab stops with `format="24h"` and `withSeconds` — minute 37 was the 61st — because every cell was its own tab stop and there were no arrow keys. That is not a preference; it is the difference between a control a keyboard user can operate and one they cannot.
- Don't: Don't expect a refused time to be corrected for you. `25:00` stays in the field marked invalid rather than being clamped to `23:59`: clamping looks like acceptance, and the user walks away believing they set a time they did not set. A well-formed time outside `min`/`max` is a different case and IS clamped, because the host asked for that.
Accessibility
- The trigger is named by `label` (announced with the current value) and exposes `aria-expanded` / `aria-haspopup`.
- The popover is a non-modal `role="dialog"`; columns are buttons reachable by keyboard, and the selected time updates the value live.
- Each column is a single tab stop on its selected cell, with Up/Down by one, PageUp/PageDown by five, and Home/End to the ends — clamping at the ends rather than wrapping, and stepping over cells disabled by `min`/`max` rather than landing on one (a disabled button cannot take focus, which would strand focus on the body with the panel still open).
- A refused typed time sets `aria-invalid` on the field and turns its hint into a `role="alert"` carrying the accepted format, so the correction is announced with the complaint.
- Implements ControlValueAccessor — works with `[(ngModel)]`, `[formControl]` and `formControlName`; `formControl.disable()` disables the trigger.
Feedback on Time Picker
Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.