KQ-1264 · BIN/LID

Bin Lid

A delete button whose bin lid lifts on its hinge on the snap spring when the pointer or keyboard focus arrives; pressing folds the label into a paper sheet that is lobbed into the bin on a true ballistic arc and tumbles out of sight behind its front wall, the lid falls shut on an accelerating tween, and the bin squashes and recovers on snap while dust puffs from both ends of the lid. The button's corners then glide into an undo pill and a ring round the bin drains linearly over the undo window; undo, by a press or Escape, flies the paper back out along the same arc and unfolds it into the label, and when the ring empties onDelete fires, with a spinning ring while its promise is pending and the paper thrown back out with a shake if it fails. It is a native button whose Enter and Space press through the native click, its width never changes, and a polite live region speaks each change once.

KQ-1264 · BIN/LID

Q3 forecast draft

Fernworks Drive · edited 3 min ago

Moves it to the bin, with 5 seconds to undo.

in drafts · press delete

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/bin-lid

Props

onDelete
() => void | Promise<unknown>
default —
Fires once the undo window runs out. Return a promise and the button shows the deletion pending until it settles, then Deleted or the error.
onUndo
() => void
default —
The visitor fished it back out, with a press or Escape, before the window ran out.
state
"idle" | "armed" | "pending" | "success" | "error"
default —
Controlled state. Every move the button wants to make goes through onStateChange and waits for this; the choreography follows the shown state, so a host's change plays it too.
onStateChange
(state: "idle" | "armed" | "pending" | "success" | "error") => void
default —
Each state the button moves to, from the press, the timer or the promise that caused it.
window
number
default 5000
How long the undo window stays open, in ms: the time the ring takes to drain. It pauses while the page is hidden.
arc
number
default 0.6
How high the paper is lobbed into the bin and how much it tumbles, 0 to 1. The apex is kept inside the component's frame.
puff
number
default 0.6
How much dust the slammed lid pushes out of its ends, 0 to 1: count, reach and size. 0 slams clean.
label
string
default "Delete"
The text at rest.
undoLabel
string
default "Undo"
The text while the undo window is open.
pendingLabel
string
default "Deleting"
The text while onDelete is settling.
successLabel
string
default "Deleted"
The text once it is gone.
errorLabel
string
default "Retry"
The text after onDelete failed; a press throws it away again.
itemName
string
default —
What is being thrown away. Joins the accessible name ("Delete Q3 forecast draft") and the spoken sentences.
successHold
number
default 1800
How long Deleted holds before the button is ready again, in ms.
errorHold
number
default 2600
How long the error holds before the button rests again, in ms.
iconOnly
boolean
default false
The bin alone in a square button (a circle as the undo pill) that keeps its accessible name.
size
"sm" | "md" | "lg"
default "md"
32, 40 or 48 px tall; the bin, the paper and the dust scale with it.
accent
string
default "var(--danger)"
The destructive colour for the text, outline, bin and ring. Any CSS colour.
sound
boolean
default false
Play the paper as it is thrown, the thud of the lid and the pop as it comes back out, only in answer to a press or key. No audio context exists unless this is on.
disabled
boolean
default false
Dims and disables the button.
className
string
default —
Merged onto the root's classes (the button and the 12 px frame its lob and dust stay inside).