TimePicker
Takes a time of day from hour, minute and second columns. Each column is a scrolling listbox.
import { TimePicker } from 'neba';
<TimePicker label="Starts at" placeholder="Pick a time" minuteStep={15} clearable />;Props
| Prop | Type | Default | Description |
|---|---|---|---|
| variantshared | 'solid' | 'outline' | 'text' | 'outline' | Weight of the surface: filled, hairline, or none |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Height and type scale. A row of the clock is the same height |
| colorshared | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | Semantic colour role. Arbitrary colour values are not accepted |
| densityshared | 'default' | 'compact' | 'default' | Padding only: never the height, never the type scale |
| elevationshared | 0 | 1 | 2 | 3 | 0 | Drop shadow depth. 0 means no shadow at all |
| value | Date | null | — | The chosen time. A Date, so it carries a day as well: see referenceDate |
| defaultValue | Date | null | — | The initial value, for an uncontrolled picker |
| onValueChange | (value: Date | null) => void | — | Called when the chosen time changes |
| referenceDate | Date | today | The day a chosen time is written onto while there is no value yet |
| minTime | Date | null | — | The earliest time of day that may be chosen: only the clock is read. Compared against the span a row stands for, so 09:30 keeps the hour 9 and greys out the minutes before it |
| maxTime | Date | null | — | The other end of the same span |
| hour12 | boolean | — | A 12-hour dial with an AM/PM column. Defaults to whatever the locale does |
| showSeconds | boolean | false | Adds the seconds column |
| hourStep | number | 1 | How far apart the rows of the hour column are |
| minuteStep | number | 1 | The same, for minutes |
| secondStep | number | 1 | The same, for seconds |
| shouldDisableTime | (value: Date, unit: 'hour' | 'minute' | 'second' | 'meridiem') => boolean | — | Blocks individual rows. Called once per row per column with the instant that row would produce and the column it is in |
| showNowButton | boolean | true | Offers the shortcut to the current time in the footer |
| open | boolean | — | Whether the popup is open. Use with onOpenChange for a controlled one |
| defaultOpen | boolean | false | Whether it starts open |
| onOpenChange | (open: boolean) => void | — | Called when the popup opens or closes |
| locale | string | — | BCP 47 tag deciding the month and weekday names, the order of the header's two buttons, and how the trigger writes the value. Defaults to the browser's |
| format | Intl.DateTimeFormatOptions | — | How the trigger writes the value. Passed straight to Intl |
| placeholder | ReactNode | — | Shown in the trigger while nothing is chosen |
| clearable | boolean | false | Offers the × that empties the control |
| closeOnSelect | boolean | false | Closes the popup as soon as a column is touched. False by default, unlike DatePicker: a time is two answers, and closing after the first would make choosing 9:30 a matter of opening the popup twice |
| labels | Partial<PickerLabels> | — | The strings a screen reader hears. One object rather than twenty props, because they are a set: they default to the locale’s wording, and the date names are not among them, those come from Intl |
| name | string | — | Identifies the field when a form is submitted, as HH:MM (HH:MM:SS with seconds shown) |
| fullWidth | boolean | false | Stretches to the width of the container |
| label | ReactNode | — | The label, wired to the control by Base UI's Field |
| description | ReactNode | — | Helper text |
| error | ReactNode | — | Error message. Its presence turns the control invalid and re-points the colour family at danger |
| invalid | boolean | !!error | Forces the invalid state without a message, for when a form library owns validity |
| readOnly | boolean | false | Shown but not changeable. Keeps its colour and edge, drains the saturation |
| disabled | boolean | false | Unavailable. Drops the colour family for neutral grey |
Native <div> attributes pass through to the root. Only color, defaultValue and children are excluded, since the table above spells them differently.
value is a Date | null. referenceDate is the day a chosen time is written onto while the value is still empty; it defaults to today and is held still for as long as the picker is mounted, so a popup left open across midnight does not move the value onto a new day.
closeOnSelect defaults to false: an hour and a minute both have to be given, so the popup does not close on the first, and the footer carries a Done button.
Examples
minuteStep · secondStep · hour12
minuteStep and secondStep are the intervals each column lists. hour12 follows the locale by default, and a 12-hour column runs 12, 1, 2 … 11 rather than 0…11.
minTime · maxTime · shouldDisableTime
minTime and maxTime are compared against the span a row stands for, not a single instant. With a minimum of 09:30, the hour 9 covers 09:00–09:59 and overlaps what is allowed, so it stays available while 00 through 25 grey out in the minute column. That is what keeps 09:30 reachable.
shouldDisableTime is handed the instant a row would produce and the column it belongs to, so a rule may be as coarse as "no lunch hour" or as fine as one minute.
showNowButton and clearable
showNowButton adds a button that jumps to the current time; clearable adds one that empties the value.
Accessibility
- Each column is a
role="listbox"ofrole="option"rows. The column names come from thelocale, andlabelswrites them out instead. - The chosen row carries
aria-selected; a blocked one carriesaria-disabledrather than thedisabledattribute, so it stays reachable and announces why it cannot be taken. - The chosen row in each column is scrolled into view when the popup opens.
- A live region beside the columns reads out the whole time whenever it changes.