Forms
Date Field
Accessible segmented date entry built on react-aria-components, styled with the quebi design system. Each date part is individually editable; the focused segment lights up with a brand-teal wash.
forminputdatedatetimeinteractive
Default
A labelled date field with a muted hint.
Event date
mmddyyyy
The day the event takes place.With value
Pre-filled via @internationalized/date.
Start date
6302026
Invalid
Validation state surfaces a red border and error message.
Expiry
112020
Date must be in the future.Disabled
Locked date
6302026
Controlled
Pick a date
6302026
2026-06-30Source
Copy this into your project. Resolve its dependencies from the registryDependencies in the component's API entry.
"use client"
import type { DateFieldProps, DateInputProps, DateValue } from "react-aria-components"
import {
DateField as DateFieldPrimitive,
DateInput as DateInputPrimitive,
DateSegment,
} from "react-aria-components"
import { cn } from "@/lib/utils"
/**
* DateField — quebi design system
*
* Built on react-aria-components. A segmented date entry control: each part
* (day / month / year) is an individually editable segment. The wrapper uses
* the quebi input chrome (translucent fill, cyan-tinted border, brand-teal
* focus ring); the focused segment lights up with a brand-teal wash.
*/
export function DateField<T extends DateValue>({ className, ...props }: DateFieldProps<T>) {
return (
<DateFieldPrimitive
{...props}
data-slot="control"
className={cn("group flex w-fit flex-col gap-1", className)}
/>
)
}
/** `bare` strips the input chrome (border, bg, rounding, focus ring, padding)
* so the DateInput can be composed inside a wrapper that owns those — e.g.
* a DatePickerTrigger which adds a calendar-icon button on the right. */
interface DateInputComponentProps extends Omit<DateInputProps, "children"> {
bare?: boolean
}
export function DateInput({ className, bare = false, ...props }: DateInputComponentProps) {
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"
: [
// quebi input chrome — matches input.tsx.
"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 text-white tracking-wider caret-transparent outline-0 type-literal:px-0",
"data-placeholder:text-quebi-fg-subtle data-[type=literal]:text-quebi-fg-muted",
"data-focused:bg-quebi-brand/20 data-focused:text-white",
"data-invalid:text-red-500 data-focused:data-invalid:bg-red-500/20 data-focused:data-invalid:text-red-500",
"forced-colors:data-focused:bg-[Highlight] forced-colors:data-focused:text-[HighlightText]",
"forced-color-adjust-none forced-colors:text-[ButtonText]",
"in-disabled:opacity-50 disabled:opacity-50 forced-colors:disabled:text-[GrayText]",
)}
/>
)}
</DateInputPrimitive>
</span>
)
}