KQ-1267 · REFRESH/WIND

Refresh Wind

A refresh button you wind like a clock spring: drag around it, or press and hold, and the arrow turns back with the finger while its stroke coils into a tightening spiral and a ring of notches lights and ticks with the tension; let go and a per-frame integrator whips it forward from rest on the spring, then lets it coast on the stored energy against friction and air drag while the refresh runs, settling to a steady cruise if the work outlasts the energy and, once it has answered, decaying exactly onto the nearest upright. A plain press does one turn, and on success the Updated 2 min ago stamp rolls to just now on the snap spring while the notches flash and fade. It is a native button: Enter and Space refresh, Shift+Enter winds a full turn (again for another) and lets go, and Escape cancels a refresh in flight.

KQ-1267 · REFRESH/WIND

Line 3 pressure

Gaugeworks · Basin plant

4.82bar

Updated 2 min ago. Drag around or hold to wind; Shift+Enter winds a turn.

updated 2 min ago · drag around to wind

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/refresh-wind

Props

onRefresh
(signal: AbortSignal) => void | Promise<unknown>
default —
Runs the refresh. Return a promise and the arrow keeps turning until it settles; the signal aborts on Escape.
state
"idle" | "pending" | "success" | "error"
default —
Controlled action state. Every move goes through onStateChange and waits for this.
onStateChange
(state: "idle" | "pending" | "success" | "error") => void
default —
Each action state, from the press, the answer or the timer that caused it.
updatedAt / defaultUpdatedAt
Date | number | null
default null
The time of the last successful refresh, controlled or initial. The stamp is written from it.
onUpdatedAtChange
(updatedAt: Date) => void
default —
The moment of each successful refresh: the now prop's value, or the clock when none is given.
now
Date | number
default a clock started on mount
The time the stamp is read against. Pass it for a relative stamp that renders on the server; without it the phrase arrives after mount and a shared 30-second clock keeps it current while the page is visible.
onWind
(turns: number) => void
default —
Fires when a wind is let go, with the turns of spring it stored.
friction
number
default 0.5
How quickly the released spin slows, 0 to 1: 0 coasts for seconds, 1 stops within a turn or two.
wind
number
default 2
Turns of spring the button holds: how far a full wind goes back, how tight the coil gets and how long the spin runs.
stamp
"relative" | "clock" | "off"
default "relative"
The stamp beside the button: Updated 2 min ago, Updated 09:40, or none (an icon-only button whose stamp is its accessible description).
formatStamp
(updatedAt: Date, now: Date) => string
default relative, or a UTC clock
Writes the stamp from the last refresh and the current time; pass one for the reader's own time zone or language.
label
string
default "Refresh"
The button's accessible name.
pendingLabel
string
default "Refreshing"
The name and the stamp while refreshing.
errorLabel
string
default "Couldn't refresh"
The stamp after a failed refresh; a press retries.
emptyLabel
string
default "Not updated yet"
The stamp before the first refresh.
accent
string
default "var(--accent-bright)"
The coil and the tension notches. Any CSS colour.
successHold
number
default 1400
How long the success flash holds before the button rests, in ms.
errorHold
number
default 2600
How long a failure holds before the button rests, in ms.
size
"sm" | "md" | "lg"
default "md"
32, 40 or 48 px across; the coil and the notch ring scale with it.
sound
boolean
default false
Play a tick for every notch the wind lights and the whir of the spin while it spends the spring's energy. No audio context exists unless this is on.
disabled
boolean
default false
Dims the button and lets go of any wind without firing it.
className
string
default —
Merged onto the root's classes.