KQ-1686 · SENDER/STACKS

Sender Stacks

A notification panel that gathers notifications by who sent them, each source a neat pile of cards — the newest on top, the next few peeking below, the unread count pinned on top. Opening a pile fans its cards down into a list on springs.glide, top card first, while the pile's height glides with them, and closing gathers them back in reverse; dismissing a source sweeps the whole pile aside together, the header leading and each card trailing a beat behind, then the gap closes and Undo is offered. Up and Down walk the stacks and an open stack's cards, Enter opens, Escape closes, Delete dismisses.

KQ-1686 · SENDER/STACKS
Coldbrook88 unread

Notifications

8 unread

Up and Down move between stacks and through an open stack. Enter opens a stack, Escape closes it, Delete dismisses it.

8 unread · gathered by sender

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/sender-stacks

Props

items
SenderItem[]
default defaultSenderItems
The notifications, grouped by source: { id, source, title, body?, at, read? }.
sources
SenderSource[]
default defaultSenderSources
The senders: { id, name, icon? } with icon one of deploy, review, billing, mention, alert, calendar.
now
number
default defaultSenderNow
The moment relative times count from, ms; never read from the clock during render.
open / defaultOpen
string[]
default — / []
The open stacks' source ids, controlled or initial.
onOpenChange
(open: string[]) => void
default —
Fires from the press or key that opened or closed a stack.
multiple
boolean
default false
Let several stacks stay open at once.
onOpen
(sourceId: string) => void
default —
A stack was opened.
onDismiss
(sourceId: string, itemIds: string[]) => void
default —
A stack was dismissed, with the ids it held.
onRestore
(sourceId: string) => void
default —
Undo brought a dismissed stack back.
onMarkRead
(sourceId: string, itemIds: string[]) => void
default —
A stack was marked read, with the ids that were unread.
onMarkAllRead
(itemIds: string[]) => void
default —
Mark all read was pressed, with every unread id.
onItemOpen
(item: SenderItem) => void
default —
A notification in an open stack was pressed; it is marked read.
stackDepth
number
default 2
How many cards peek under each stack's top card, 0 to 4.
cardStyle
"raised" | "flat" | "outline"
default "raised"
Shadowed paper cards, filled flat cards, or hairline outline cards; lower layers take a shade darker.
accent
string
default "var(--accent-bright)"
Count pills, unread dots and an open stack's rail; filled pills take its hue at a fixed lightness.
undoMs
number
default 5000
How long Undo is offered after a dismissal, ms.
maxHeight
number
default 560
Tallest the panel grows before its stacks scroll, px.
title
string
default "Notifications"
The panel's heading and accessible name.
status
"ready" | "loading" | "error"
default "ready"
Loading draws breathing skeleton piles; error offers Try again; no items draws the caught-up state.
onReload
() => void
default —
Try again was pressed after the notifications failed to load.
disabled
boolean
default false
Nothing can be opened, read or dismissed.
className
string
default —
Classes for the panel.