Installation
Registry — recommended
Copies the component into your configured shadcn UI directory and installs Motion.
npx shadcn@latest add https://suluu.site/r/rope-time-picker.jsonnpm package
For centralized upgrades, install the optional ESM package and configure Tailwind source detection as shown below.
pnpm add suluu motionUsage
RopeTimePicker uses a focused twelve-hour value with explicit AM or PM. Use defaultValue for local state, or pair value with onValueChange when your application owns the time.
import { RopeTimePicker } from "@/components/ui/rope-time-picker"
export function AlarmTime() {
return (
<RopeTimePicker
defaultValue={{ hours: 7, minutes: 30, seconds: 0, period: "AM" }}
onValueChange={(time) => saveAlarm(time)}
/>
)
}// app/globals.css
@import "suluu/styles.css";
@source "../node_modules/suluu/dist";
// component.tsx
import { RopeTimePicker } from "suluu/rope-time-picker"
export function AlarmTime() {
return <RopeTimePicker />
}Controlled time and seconds
Seconds stay in the value even when hidden, so hour and minute edits never discard application state.
const [time, setTime] = useState({
hours: 9,
minutes: 35,
seconds: 20,
period: "AM" as const,
})
<RopeTimePicker
onValueChange={setTime}
showSeconds
value={time}
/>Controlled mode
Mode normally follows the field or rope point the user chooses. Control it when another part of your interface needs to coordinate the active unit.
const [mode, setMode] = useState<"hour" | "minute" | "second">("hour")
<RopeTimePicker
mode={mode}
onModeChange={setMode}
showSeconds
/>Five-minute steps
One-unit snapping is the default. Use five-unit snapping for scheduling surfaces where quick selection matters more than minute-by-minute granularity.
<RopeTimePicker
defaultValue={{ hours: 10, minutes: 30, seconds: 0, period: "AM" }}
snapStep={5}
/>In context
A five-minute scheduling control that keeps its wall-clock value explicit.
Focus session
10:30 AM
Choose a calm start time. The five-minute dial stays quick while the editable fields remain precise and familiar.
Props
RopeTimePicker accepts safe native div attributes and forwards its ref to the accessible root group. It intentionally does not parse dates, manage timezones, submit a hidden form value, or own a popover.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | RopeTimeValue | — | Controlled twelve-hour wall-clock value. |
| defaultValue | RopeTimeValue | 12:00:00 AM | Initial uncontrolled value. |
| onValueChange | (value) => void | — | Runs once for every distinct snapped value requested by an interaction. |
| mode | "hour" | "minute" | "second" | — | Controlled unit being edited. |
| defaultMode | "hour" | "minute" | "second" | "hour" | Initial editing unit when mode is uncontrolled. |
| onModeChange | (mode) => void | — | Runs when a field or control point requests another unit. |
| showSeconds | boolean | false | Shows the seconds rope, slider, and digital field. |
| showDigital | boolean | true | Shows the editable digital fields above the dial. |
| snapStep | 1 | 5 | 1 | Interaction step for minutes and seconds; hours always step by one. |
| size | "sm" | "default" | "lg" | "default" | Controls dial, center, dot, and readout scale. |
| motionIntensity | "subtle" | "default" | "expressive" | "default" | Controls endpoint settle, rope lag, and resting weight. |
| disabled | boolean | false | Disables every interaction and focus target. |
| readOnly | boolean | false | Keeps controls discoverable while preventing value changes. |
| className | string | — | Class name applied to the root group. |
Theming
Override these variables in your light and dark theme scopes. The registry installs the defaults automatically.
--suluu-rope-time-backgroundDial surface.--suluu-rope-time-foregroundLabels, digits, and period text.--suluu-rope-time-mutedMinute ticks and separators.--suluu-rope-time-hourHour ticks and numerals.--suluu-rope-time-borderDial and readout borders.--suluu-rope-time-guideInner guide ring.--suluu-rope-time-ropeInactive hour and minute rope color.--suluu-rope-time-rope-highlightRope and control-point highlight.--suluu-rope-time-accentActive hour and minute rope, point, and field.--suluu-rope-time-secondSeconds rope and active second control.--suluu-rope-time-controlInactive control-point surface.--suluu-rope-time-control-borderControl and center borders.--suluu-rope-time-centerAM/PM control surface.--suluu-rope-time-center-hoverAM/PM hover surface.--suluu-rope-time-readoutDigital readout surface.--suluu-rope-time-readout-activeActive digital field wash.--suluu-rope-time-readout-hoverInactive field hover wash.--suluu-rope-time-ringKeyboard focus ring.--suluu-rope-time-offsetColor behind focus-ring offsets.--suluu-rope-time-shadowDial depth.--suluu-rope-time-rope-shadowRope depth filter.--suluu-rope-time-control-shadowControl-point elevation.--suluu-rope-time-center-shadowAM/PM control elevation.--suluu-rope-time-readout-shadowDigital readout elevation.Sizes
Every size keeps the same SVG geometry and minimum control-point hit area; only its rendered scale and typography change.
The root exposes mode, size, disabled, read-only, and dragging state through data attributes. Individual ropes, fields, and points expose stable slot and mode attributes for local restyling.
Accessibility
Each visible control point is a named slider with live minimum, maximum, current value, and complete time text. Arrow keys move one allowed step and wrap naturally around the clock; Home and End move to the unit bounds. This follows the WAI-ARIA slider pattern.
The visible readout uses real text-editable spinbuttons, preserving native selection and editing while supporting arrows, direct numeric entry, Enter, and Escape. This also provides a reliable alternative to circular pointer gestures, following the WAI-ARIA spinbutton pattern.
Reduced motion removes spring lag and moves every rope immediately while retaining its quiet static curve. Disabled controls leave the tab order; read-only controls remain discoverable but cannot alter the time or period.