KQ-533 · EXPIRY/RING

Expiry Ring

A countdown ring that drains linearly — the one place a tween is more honest than a spring, because no easing may borrow from a second. Inside it mm:ss rolls on snap, one wheel per digit so only the place that changed moves; under the warn threshold the ring pulses on drift and the digits warm to warn, and zero stamps Expired on recoil and swaps the line for a Resend button that restarts the run. The element is a timer with its live region off so a screen reader is not read a number every second, a polite region speaks only at the two thresholds, and Resend is a real button; under reduced motion the pulse never runs while the ring still drains.

KQ-533 · EXPIRY/RING
418902
Waylight verification codeExpires in30 seconds remaining

Waylight · 30s left · sent 1×

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/expiry-ring

Props

seconds
number
default
Length of the countdown.
running
boolean
default true
Pause and resume. Pausing keeps the elapsed time, so resuming carries on rather than restarting.
warnAt
number
default 10
Seconds left when the ring starts pulsing and the digits warm to warn.
onExpire
() => void
default
Fires once, from the tick that reaches zero.
onResend
() => void
default
Fires from the Resend button, which also restarts the countdown.
onTick
(secondsLeft: number) => void
default
Whole seconds left, emitted from the tick that changed the number.
label
string
default
What is expiring. Names the timer and both threshold announcements.