Components

Wheel Picker

People choose a date or a time by scrolling columns, the usual pattern on phones for a time or a nearby date. In Arabic the day sits on the right and ص / م on the left, while hour and minute keep clock order; days and months carry their Arabic names.

Preview

Mirrored is the same English content laid out right to left: it separates a mirroring bug from a translation one.

Installation

SoonInstalling from the registry is coming soon. Until then, these are the commands you will run.
npx ritla add wheel-picker

Also works with shadcn: npx shadcn@latest add @ritla/wheel-picker

Installing by hand copies the component's source into your project. The source comes with a Ritla UI license, so the CLI is the short way to the same files.

Usage

import { WheelDateTimePicker } from "@/components/ui/wheel-picker";
const [value, setValue] = useState(() => nextFiveMinutes());

<WheelDateTimePicker value={value} onValueChange={setValue} minuteStep={5} />

Examples

Date

WheelDatePicker: day, month and year, the day first in reading order. Months are short in English and written out in Arabic; years run five either side of the value by default (minYear, maxYear). For a birthday, type the date in a field rather than scroll through years.

Get the code ›

In a bottom sheet, from a field

The field shows the value and opens a bottom sheet (Drawer) titled with the field's label. Done keeps the new value; Cancel, the close button or a swipe down leave it as it was. Don't leave a wheel open inline on a page.

Get the code ›

API reference

WheelDateTimePicker

PropTypeDefaultDescription
value / onValueChangeDate / (value: Date) => voidNoneThe value, always a full date and time. Open on a sensible one: today, the next full five minutes.
min / maxDatetoday / 60 days laterThe first and last day offered. Limit them to what is valid, such as no past days for a delivery.
minuteStepnumber5Minutes in steps of this.
hourCycle"h12" | "h23""h12"12 hours with the AM / PM column, or 24 hours without it.
labels{ group, day, hour, minute, period }NoneNames for the group and columns, for screen readers. Default: in the page language; repeat the field's label in group.
locale / numeralsstring / "latn" | "arab"NoneDefault: from KanzProvider. Names come from Intl in the page language, and every column uses one digit system.

WheelDatePicker

PropTypeDefaultDescription
value / onValueChangeDate / (value: Date) => voidNoneA day past the end of the new month moves to its last day (31 May, then June: 30 June).
minYear / maxYearnumbervalue ± 5The years offered.
labels{ group, day, month, year }NoneNames for the group and columns.

WheelPicker / WheelPickerColumn

PropTypeDefaultDescription
options{ value, label, valueText?, disabled? }[]NoneThe column's rows. valueText is what a screen reader says; disabled rows stay visible and the column skips them (a booked slot).
value / onValueChangestring / (value: string) => voidNoneThe value of the row in the band.
labelstringNoneThe column's name ("Hour").
aria-label (WheelPicker)stringNoneThe whole picker's name; repeat the field's label.

Accessibility

Keyboard

Keys are written for left-to-right. In right-to-left the arrow keys swap: the one pointing toward the reading direction moves forward.

KeyAction
TabFrom one column to the next: in the date and time picker, day, hour, minute, then AM / PM.
ArrowUpArrowDownOne row up or down in the column.
PageUpPageDownFive rows.
HomeEndThe first and last row.

ARIA

  • Each column is a spinbutton with its name and its value in words in aria-valuetext ("Wednesday 30 December"), never the band's color alone.
  • The columns are a group named after the field. The rows are the picture only, hidden from screen readers.
  • Scrolling uses the platform's own momentum and settles by CSS scroll snapping; with reduced motion the column jumps instead of gliding.
  • Scrolling is hard for some people: also let them type the value in the field.

Arabic and RTL notes

Mirrors

  • The column order: the day on the right, where the Arabic reader starts, then the time, with ص / م on the left.

Does not mirror

  • Scrolling is vertical: up and down don't change and the rows inside a column don't flip.
  • Hour, then minute, left to right in every language, as on a clock; only AM / PM moves to the other side. Tab follows the pair: hour, then minute.
Numerals
Every column uses one digit system from numerals: "6 25" or "٦ ٢٥", never both in one picker.
Arabic typography
Rows are body-24 Semibold, 48px tall; long Arabic names ("الأربعاء 30 ديسمبر") widen the column rather than wrap.
Mixed-direction text
Arabic month names come from Intl (يناير to ديسمبر), the same set as the date pickers and the rest of the product.