Time Picker

Type a localized time in editable hour, minute and optional second blocks, or open native time choices. Valid edits update immediately. Hour choices follow the locale and hour-cycle override. Unavailable choices are disabled, including overnight bounds. Native forms submit one canonical time, and native time entry remains available before JavaScript.

Type or choose a time

Type within the time blocks, paste an ISO time, or open the clock action for localized choices. Valid edits update immediately.

Choose a time between 12:00 AM and 11:59 PM.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-form-field/ui-form-field.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-time-picker/ui-time-picker.html"
>

<ui-form-field>
  <label
    slot="label"
    for="appointment-time"
  >Appointment time</label>
  <ui-time-picker
    input-id="appointment-time"
    name="appointment"
    value="14:30"
    locale="en-US"
  ></ui-time-picker>
  <small
    slot="help"
  >Choose a time between 12:00 AM and 11:59 PM.</small>
</ui-form-field>
<script setup>
  import { UiFormField, UiTimePicker } from "@threadlabs/looma";
</script>

<template>
  <UiFormField>
    <template #label>
      <label
        for="appointment-time"
      >Appointment time</label>
    </template>
    <UiTimePicker
      input-id="appointment-time"
      name="appointment"
      value="14:30"
      locale="en-US"
    />
    <template #help>
      <small>Choose a time between 12:00 AM and 11:59 PM.</small>
    </template>
  </UiFormField>
</template>
import { UiFormField, UiTimePicker } from "@threadlabs/looma";

export default function Example() {
  return <UiFormField
    slots={{
      label: <label
        htmlFor="appointment-time"
      >Appointment time</label>,
      help: <small>Choose a time between 12:00 AM and 11:59 PM.</small>,
    }}
  >
    <UiTimePicker
      inputId="appointment-time"
      name="appointment"
      value="14:30"
      locale="en-US"
    />
  </UiFormField>;
}
/** @jsxImportSource preact */
import { UiFormField, UiTimePicker } from "@threadlabs/looma";

export default function Example() {
  return <UiFormField
    slots={{
      label: <label
        htmlFor="appointment-time"
      >Appointment time</label>,
      help: <small>Choose a time between 12:00 AM and 11:59 PM.</small>,
    }}
  >
    <UiTimePicker
      inputId="appointment-time"
      name="appointment"
      value="14:30"
      locale="en-US"
    />
  </UiFormField>;
}
<script>
  import { UiFormField, UiTimePicker } from "@threadlabs/looma";
</script>

{#snippet label()}
  <label
    for="appointment-time"
  >Appointment time</label>
{/snippet}

{#snippet help()}
  <small>Choose a time between 12:00 AM and 11:59 PM.</small>
{/snippet}

<UiFormField
  slots={{ label, help }}
>
  <UiTimePicker
    inputId="appointment-time"
    name="appointment"
    value="14:30"
    locale="en-US"
  />
</UiFormField>
import { Component } from "@angular/core";
import { UiFormField, UiTimePicker } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiFormField, UiTimePicker],
  template: `
    <div
      ui-form-field
    >
      <ng-container ngProjectAs="[slot='label']">
        <label
          for="appointment-time"
        >Appointment time</label>
      </ng-container>
      <span
        ui-time-picker
        [inputId]="'appointment-time'"
        [name]="'appointment'"
        [value]="'14:30'"
        [locale]="'en-US'"
      ></span>
      <ng-container ngProjectAs="[slot='help']">
        <small>Choose a time between 12:00 AM and 11:59 PM.</small>
      </ng-container>
    </div>
  `,
})
export default class Example {}

Overnight bounds and seconds

A maximum before the minimum wraps midnight. Existing fractions stay intact while the picker edits hours, minutes and seconds.

Available from 9:00 PM through 3:00 AM.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-form-field/ui-form-field.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-time-picker/ui-time-picker.html"
>

<ui-form-field>
  <label
    slot="label"
    for="overnight-time"
  >Time</label>
  <ui-time-picker
    input-id="overnight-time"
    name="time"
    value="23:15:30.125"
    min="21:00"
    max="03:00"
    granularity="second"
    locale="en-US"
  ></ui-time-picker>
  <small
    slot="help"
  >Available from 9:00 PM through 3:00 AM.</small>
</ui-form-field>
<script setup>
  import { UiFormField, UiTimePicker } from "@threadlabs/looma";
</script>

<template>
  <UiFormField>
    <template #label>
      <label
        for="overnight-time"
      >Time</label>
    </template>
    <UiTimePicker
      input-id="overnight-time"
      name="time"
      value="23:15:30.125"
      min="21:00"
      max="03:00"
      granularity="second"
      locale="en-US"
    />
    <template #help>
      <small>Available from 9:00 PM through 3:00 AM.</small>
    </template>
  </UiFormField>
</template>
import { UiFormField, UiTimePicker } from "@threadlabs/looma";

export default function Example() {
  return <UiFormField
    slots={{
      label: <label
        htmlFor="overnight-time"
      >Time</label>,
      help: <small>Available from 9:00 PM through 3:00 AM.</small>,
    }}
  >
    <UiTimePicker
      inputId="overnight-time"
      name="time"
      value="23:15:30.125"
      min="21:00"
      max="03:00"
      granularity="second"
      locale="en-US"
    />
  </UiFormField>;
}
/** @jsxImportSource preact */
import { UiFormField, UiTimePicker } from "@threadlabs/looma";

export default function Example() {
  return <UiFormField
    slots={{
      label: <label
        htmlFor="overnight-time"
      >Time</label>,
      help: <small>Available from 9:00 PM through 3:00 AM.</small>,
    }}
  >
    <UiTimePicker
      inputId="overnight-time"
      name="time"
      value="23:15:30.125"
      min="21:00"
      max="03:00"
      granularity="second"
      locale="en-US"
    />
  </UiFormField>;
}
<script>
  import { UiFormField, UiTimePicker } from "@threadlabs/looma";
</script>

{#snippet label()}
  <label
    for="overnight-time"
  >Time</label>
{/snippet}

{#snippet help()}
  <small>Available from 9:00 PM through 3:00 AM.</small>
{/snippet}

<UiFormField
  slots={{ label, help }}
>
  <UiTimePicker
    inputId="overnight-time"
    name="time"
    value="23:15:30.125"
    min="21:00"
    max="03:00"
    granularity="second"
    locale="en-US"
  />
</UiFormField>
import { Component } from "@angular/core";
import { UiFormField, UiTimePicker } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiFormField, UiTimePicker],
  template: `
    <div
      ui-form-field
    >
      <ng-container ngProjectAs="[slot='label']">
        <label
          for="overnight-time"
        >Time</label>
      </ng-container>
      <span
        ui-time-picker
        [inputId]="'overnight-time'"
        [name]="'time'"
        [value]="'23:15:30.125'"
        [min]="'21:00'"
        [max]="'03:00'"
        [granularity]="'second'"
        [locale]="'en-US'"
      ></span>
      <ng-container ngProjectAs="[slot='help']">
        <small>Available from 9:00 PM through 3:00 AM.</small>
      </ng-container>
    </div>
  `,
})
export default class Example {}

API

Package: @threadlabs/looma · Native root: span

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
disabledPrevents editing/opening and omits native submission.disabledbooleanfalse—
formOptional external form ID.formstring—
granularitySmallest displayed time unit. Existing seconds and fractions are preserved.granularitykeywordminuteminute, second
hour-cycleHour cycle for the field and picker choices; omission follows the locale.hourCyclekeyword—h11, h12, h23, h24
hour-labelVisible hour editor label.hourLabelstringHour—
input-idID of the active native input for an authored label.inputIdstring—
invalidExternal invalid appearance supplied by the application, independent of parsed validity.invalidbooleanfalse—
labelOptional accessible field name; prefer an authored visible label.labelstring—
localeIntl locale for typed time and display. ISO time paste is always accepted.localestringen-US—
maxInclusive latest permitted time, in canonical ISO time format.maxstring—
minInclusive earliest time. Bounds may wrap midnight when max precedes min.minstring—
minute-labelVisible minute editor label.minuteLabelstringMinute—
nameNative form field name for the canonical time.namestring—
picker-labelAccessible name of the trigger and picker dialog.pickerLabelstringChoose time—
placeholderOptional hint inside the empty field.placeholderstring—
readonlyPrevents editing/opening while retaining selection and submission.readonlybooleanfalse—
requiredRequires a complete time.requiredbooleanfalse—
second-labelVisible second editor label.secondLabelstringSecond—
sizeShared field and trigger size.sizekeywordmdsm, md, lg
valueInitial canonical time, replaced when changed externally. Reset restores this external value.valuestring—

Framework props

NameTypeDefaultAllowed values
disabledbooleanfalse—
formstring—
granularity"minute" | "second"minuteminute, second
hourCycle"h11" | "h12" | "h23" | "h24"—h11, h12, h23, h24
hourLabelstringHour—
inputIdstring—
invalidbooleanfalse—
labelstring—
localestringen-US—
maxstring—
minstring—
minuteLabelstringMinute—
namestring—
pickerLabelstringChoose time—
placeholderstring—
readonlybooleanfalse—
requiredbooleanfalse—
secondLabelstringSecond—
size"sm" | "md" | "lg"mdsm, md, lg
valuestring—

Events

NameDetailDescription
changeobject
  • value: string (required)
  • trigger: keyword (required)
Emitted when a toggleable control changes checked state.
closeobject
  • open: boolean (required)
  • reason: keyword (required)
  • trigger: keyword (required)
Emitted when the component transitions to a closed state.
inputobject
  • value: string (required)
  • trigger: keyword (required)
Emitted with the declared detail payload.
openobject
  • open: boolean (required)
  • reason: keyword (required)
  • trigger: keyword (required)
Emitted when the component transitions to an open state.
query-changeobject
  • query: string (required)
Emitted with the declared detail payload.
validation-changeobject
  • invalid: boolean (required)
  • message: string (required)
Emitted with the declared detail payload.

Slots

No slots.

Design tokens

Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.

Component tokens

No component tokens.

Shared tokens

No shared tokens.