KQ-1255 · RENEW/LOOP
Renew Loop
An auto-renew switch whose icon is the renewal: switched on, two arc arrows chase round the ring on springs.glide until their heads catch the other's tails and lock into a closed ring on springs.snap, the heads folding to small chevrons as the ring takes the accent, while a split-flap readout beside the label turns over cell by cell to the renewal date. Switched off, the ring breaks at the top, the arrows coast apart under friction on an inertia decay and the readout flips back to the day access ends. Dates come only from props, read in UTC; Space and Enter toggle it, and a polite status speaks the new date.
Fernworks Pro
Waylight Pay · €12 / month
auto-renew on · next charge €12 on 15 Nov
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/renew-loop
Props
- pressed / defaultPressed
- boolean
- default false
- Controlled and initial state; true while the plan renews by itself.
- onPressedChange
- (pressed: boolean) => void
- default —
- Fires from the press that changed it, with the new state.
- renewsOn
- Date | number | string
- default renewedOn plus one period
- The next renewal date, when your billing system already knows it.
- renewedOn
- Date | number | string
- default "2026-10-15"
- The day the current term began: the last renewal, or the purchase.
- period
- "month" | "quarter" | "year"
- default "month"
- The billing period: how far each renewal carries the plan.
- label
- string
- default "Auto-renew"
- What the switch controls; the button's accessible name.
- renewsLabel
- string
- default "Renews"
- The word before the date while on.
- endsLabel
- string
- default "Ends"
- The word before the date while off.
- formatDate
- (date: Date) => string
- default "15 Nov" in UTC
- How a date is written in the readout and the description.
- chase
- number
- default 240
- How far the arrows chase before their heads lock, in degrees.
- flip
- number
- default 90
- How long one flap takes to fall, in ms.
- friction
- number
- default 0.5
- How quickly the released arrows coast to a stop, 0 (they glide on) to 1 (they stop short).
- compact
- boolean
- default false
- Ring only; the label becomes the accessible name and the date its description and title.
- size
- "sm" | "md" | "lg"
- default "md"
- Pill height 32, 36 or 44px with the readout scaled to match.
- accent
- string
- default var(--accent-bright)
- The closed ring's colour, any CSS colour.
- sound
- boolean
- default false
- A ratchet tick per 30° of spin, a snap when the heads lock or the ring breaks, a faint tick per flap. Only after a press.
- disabled
- boolean
- default false
- Greys the switch out and ignores presses and hover.
- className
- string
- default —
- Extra classes for the root, which wraps the button.
| Prop | Type | Default | Description |
|---|---|---|---|
| pressed / defaultPressed | boolean | false | Controlled and initial state; true while the plan renews by itself. |
| onPressedChange | (pressed: boolean) => void | — | Fires from the press that changed it, with the new state. |
| renewsOn | Date | number | string | renewedOn plus one period | The next renewal date, when your billing system already knows it. |
| renewedOn | Date | number | string | "2026-10-15" | The day the current term began: the last renewal, or the purchase. |
| period | "month" | "quarter" | "year" | "month" | The billing period: how far each renewal carries the plan. |
| label | string | "Auto-renew" | What the switch controls; the button's accessible name. |
| renewsLabel | string | "Renews" | The word before the date while on. |
| endsLabel | string | "Ends" | The word before the date while off. |
| formatDate | (date: Date) => string | "15 Nov" in UTC | How a date is written in the readout and the description. |
| chase | number | 240 | How far the arrows chase before their heads lock, in degrees. |
| flip | number | 90 | How long one flap takes to fall, in ms. |
| friction | number | 0.5 | How quickly the released arrows coast to a stop, 0 (they glide on) to 1 (they stop short). |
| compact | boolean | false | Ring only; the label becomes the accessible name and the date its description and title. |
| size | "sm" | "md" | "lg" | "md" | Pill height 32, 36 or 44px with the readout scaled to match. |
| accent | string | var(--accent-bright) | The closed ring's colour, any CSS colour. |
| sound | boolean | false | A ratchet tick per 30° of spin, a snap when the heads lock or the ring breaks, a faint tick per flap. Only after a press. |
| disabled | boolean | false | Greys the switch out and ignores presses and hover. |
| className | string | — | Extra classes for the root, which wraps the button. |