npx shadcn@latest add https://sdk-components.thesqd.com/r/timer.jsonimport { Timer } from "@/components/blocks/timer";"use client";
import { Timer } from "@/components/blocks/timer";
export function Example() {
return (
<Timer
label="Time tracked"
confirmDescription="Time starts counting immediately and is logged against this task."
onEvent={(event) => {
if (event.type === "stop") console.log("logged", event.seconds);
}}
/>
);
}Timer (single component — pass everything as props)
└── card-surface <button> (the whole card is the control; goes red running)
├── icon (Play when off, Square when running)
├── label (muted caption, truncates)
├── reel (transitions-dev spinning counter)
│ └── t-reel-col[n] (one clipped window per digit)
│ └── t-reel-strip (0-9 cells; rolls forward, blur decays to 0)
└── off → Popover (start confirmation)
│ ├── header (confirmTitle / confirmDescription)
│ ├── confirmBody (any ReactNode)
│ └── actions (confirmActions({ start, cancel }) or the
│ default Cancel + confirm pair)
on → click stops (no confirmation on the way down)onEvent
Start the timer to see events land here.
Timer
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | "Time tracked" | Muted caption above the digits. |
defaultSeconds | number | 0 | Elapsed seconds to resume from. |
confirmTitle | ReactNode | null | "Start the timer?" | Heading of the start-confirmation popover. `null` drops the header. |
confirmDescription | ReactNode | null | "Time starts counting immediately…" | Body copy of the start-confirmation popover. `null` drops it. |
confirmBody | ReactNode | — | Arbitrary content rendered between the popover header and its buttons. |
confirmActions | (api: TimerConfirmApi) => ReactNode | — | Replaces the popover's button row. Called with `start` / `cancel`. |
confirmLabel | string | "Start timer" | Label of the default confirm button. |
confirmSide | "top" | "right" | "bottom" | "left" | "bottom" | Which edge of the card the popover anchors to. |
confirmAlign | "start" | "center" | "end" | "start" | How the popover aligns along that edge. |
confirmSideOffset | number | 4 | Gap in px between the card and the popover. |
onEvent | (event: TimerEvent) => void | — | Fires on start, on stop, and once per whole second while running. |
className | string | — | Override the card's width or layout classes. |
TimerConfirmApi
| Prop | Type | Default | Description |
|---|---|---|---|
start | () => void | — | Starts the timer and closes the popover. |
cancel | () => void | — | Closes the popover without starting. |
TimerEvent
| Prop | Type | Default | Description |
|---|---|---|---|
type | "start" | "stop" | "tick" | — | What happened. |
seconds | number | — | Elapsed whole seconds at the moment of the event. |
at | number | — | `Date.now()` when the event fired. |