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.
Q3 forecast draft
Fernworks Drive · edited 3 min ago
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).
| Prop | Type | Default | Description |
|---|---|---|---|
| onDelete | () => void | Promise<unknown> | — | 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 | — | The visitor fished it back out, with a press or Escape, before the window ran out. |
| state | "idle" | "armed" | "pending" | "success" | "error" | — | 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 | — | Each state the button moves to, from the press, the timer or the promise that caused it. |
| window | number | 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 | 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 | 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 | "Delete" | The text at rest. |
| undoLabel | string | "Undo" | The text while the undo window is open. |
| pendingLabel | string | "Deleting" | The text while onDelete is settling. |
| successLabel | string | "Deleted" | The text once it is gone. |
| errorLabel | string | "Retry" | The text after onDelete failed; a press throws it away again. |
| itemName | string | — | What is being thrown away. Joins the accessible name ("Delete Q3 forecast draft") and the spoken sentences. |
| successHold | number | 1800 | How long Deleted holds before the button is ready again, in ms. |
| errorHold | number | 2600 | How long the error holds before the button rests again, in ms. |
| iconOnly | boolean | false | The bin alone in a square button (a circle as the undo pill) that keeps its accessible name. |
| size | "sm" | "md" | "lg" | "md" | 32, 40 or 48 px tall; the bin, the paper and the dust scale with it. |
| accent | string | "var(--danger)" | The destructive colour for the text, outline, bin and ring. Any CSS colour. |
| sound | boolean | 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 | false | Dims and disables the button. |
| className | string | — | Merged onto the root's classes (the button and the 12 px frame its lob and dust stay inside). |