Forms
Time Field
A segmented time input (hour, minute, second, AM/PM) with keyboard-friendly editing, built on react-aria-components.
forminputtimefieldsegmented
Default
A labelled time field with hour, minute, and AM/PM segments.
Event time
––––AM
With description
A hint rendered beneath the field.
Start time
930AM
Times are shown in your local timezone.States
Disabled and invalid fields.
Disabled
200PM
Invalid
1159PM
Choose a time during business hours.With seconds
Use granularity to add a seconds segment.
Precise time
101530AM
Controlled
Wake-up time
800AM
08:00:00Source
Copy this into your project. Resolve its dependencies from the registryDependencies in the component's API entry.
"use client"
import type { DateInputProps, TimeFieldProps, TimeValue } from "react-aria-components"
import {
DateInput as DateInputPrimitive,
DateSegment,
TimeField as TimeFieldPrimitive,
} from "react-aria-components"
import { cn } from "@/lib/utils"
/**
* TimeField — quebi design system
*
* Built on react-aria-components. A segmented time input (hour / minute /
* second / AM-PM) styled to match the quebi Input: a translucent field with a
* cyan-tinted border that lifts to brand teal on focus, plus the quebi teal
* ring. Each segment highlights with the brand tint while editing; invalid
* uses red and disabled dims the field.
*/
export function TimeField<T extends TimeValue>({ className, ...props }: TimeFieldProps<T>) {
return (
<TimeFieldPrimitive
{...props}
data-slot="control"
className={cn(
"group w-fit",
// label → control → hint stack with 6px between siblings.
"[&>[data-slot=label]+[data-slot=control]]:mt-1.5",
"[&>[data-slot=label]+[slot='description']]:mt-1",
"[&>[slot=description]+[data-slot=control]]:mt-1.5",
"[&>[data-slot=control]+[slot=description]]:mt-1.5",
"[&>[data-slot=control]+[slot=errorMessage]]:mt-1.5",
"in-disabled:opacity-50 disabled:opacity-50",
className,
)}
/>
)
}
/** `bare` strips the field chrome (border, bg, rounding, focus ring, padding)
* so the TimeInput can be composed inside a wrapper that owns those. */
interface TimeInputProps extends Omit<DateInputProps, "children"> {
bare?: boolean
}
export function TimeInput({ className, bare = false, ...props }: TimeInputProps) {
return (
<span data-slot="control" className={bare ? "relative block w-full" : "relative block"}>
<DateInputPrimitive
className={cn(
"relative block appearance-none text-sm text-white",
bare
? "w-full rounded-none border-0 bg-transparent px-3 py-2.5 outline-none"
: [
// matches the quebi Input chrome.
"rounded-quebi-sm border border-cyan-500/20 bg-white/[0.02] px-3 py-2.5",
"transition-[border-color,box-shadow] duration-200",
"enabled:hover:border-cyan-500/40",
"outline-none focus-within:border-quebi-brand focus-within:outline-none focus-within:ring-2 focus-within:ring-quebi-brand/50",
"group-open:border-quebi-brand group-open:ring-2 group-open:ring-quebi-brand/50",
"invalid:border-red-500 focus-within:invalid:ring-red-500/50",
"in-disabled:cursor-not-allowed in-disabled:opacity-50",
],
className,
)}
{...props}
>
{(segment) => (
<DateSegment
segment={segment}
className={cn(
"inline shrink-0 rounded px-1 py-0.5 text-sm tracking-wider text-white caret-transparent outline-0 type-literal:px-0",
"data-placeholder:not-data-focused:text-quebi-fg-subtle",
"focus:bg-quebi-brand/20 focus:text-white",
"focus:data-invalid:bg-red-500/20 focus:data-invalid:text-red-400",
"forced-colors:focus:bg-[Highlight] forced-colors:focus:text-[HighlightText]",
"forced-color-adjust-none forced-colors:text-[ButtonText]",
"in-disabled:opacity-50 disabled:opacity-50 forced-colors:disabled:text-[GrayText]",
)}
/>
)}
</DateInputPrimitive>
</span>
)
}