KQ-1123 · SLOT/SWIPE

Slot Swipe

An inbox whose rows sit between a mail slot and a drawer. Swipe a row toward the slot and the slit opens to meet it while the row feeds in edgewise, clipped at the slit and squashed thinner the deeper it goes; past the threshold (or thrown past it, by projection) it runs in on the snap spring with its release velocity and the slot shuts on the recoil spring with a thwup. Swipe toward the drawer and the drawer slides out with a clock that winds and ticks as you pull; the row drops in, the clock spins round to the snooze hour and the drawer shuts. A short swipe springs back, Delete and S do the same as the swipes with the same frames and sounds, and Undo brings the last row back out of wherever it went.

KQ-1123 · SLOT/SWIPE

Inbox

5

Swipe toward the slot or press Delete to archive. Swipe toward the drawer or press S to snooze until 9:00.

5 in inbox · 0 archived · 0 snoozed

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/slot-swipe

Props

items
{ id: string; from: string; subject: string; time?: string }[]
default —
Every row that can appear.
value / defaultValue
string[]
default every item, in order
The ids still in the inbox, in order. Controlled or uncontrolled; a row the host keeps after a swipe comes back out of the slot or drawer.
onValueChange
(ids: string[]) => void
default —
Fires from the swipe, key or Undo that changed the list, with the ids left — at the commit, while the row is still going in.
onAction
(id: string, action: "archive" | "snooze") => void
default —
Fires with what happened to a row: into the slot (archive) or into the drawer (snooze).
onOpen
(id: string) => void
default —
A row pressed without a swipe, or Enter or Space on it.
label
string
default "Inbox"
The list's name, shown in the header with the count.
snoozeHour
number
default 9
The hour the drawer's clock spins to, 0 to 23; also the time in the announcement and the header.
threshold
number
default 0.35
How far a row must go, or be thrown, as a share of its lane (0.2 to 0.6) before it commits. The slot and drawer are fully open, and light up, at this point.
side
"right" | "left"
default "right"
Which end of each lane the slot is on; the drawer takes the other.
squash
number
default 0.5
How much a row squeezes to fit the slot or the drawer, 0 to 1: up to 60% thinner, and a little longer along the swipe.
undo
boolean
default true
An Undo button in the header after each swipe, which brings the row back out of where it went.
empty
ReactNode
default "Inbox zero"
Shown when nothing is left.
sound
boolean
default false
A detent as a row arms, paper as it feeds into the slot and a thwup as the slot shuts; the drawer's clock ticks as it winds and spins, and the drawer shuts with a clack. Nothing plays, and no audio context exists, unless this is on.
disabled
boolean
default false
Dims the rows and stops every swipe, key and Undo.
className
string
default —
Merged onto the outer wrapper.