Payment receivedKestrel Studio paid ₹48,200 for INV-1042.
All Taste components
Animated app UI
Notification stack
An inbox of updates that slide in one at a time, with the unread count popping in once they’ve all arrived.
4 notifications · spring slide-in · unread count · 4 tones
Invoice viewedBrightwater Design opened INV-1043.
Due tomorrowINV-1039 to Oakline Joinery, ₹12,000.
Asha joined your teamShe can now create and send invoices.
import { Bell, Check, Clock, Mail, Users } from "lucide-react";
import { cn } from "@/lib/utils";
/* Add once to globals.css:
@keyframes toast-in { from { opacity: 0; transform: translateX(44px) scale(.95); } 55% { opacity: 1; } }
*/
const notifications = [
{ icon: Check, tone: "bg-success-soft text-success-fg", title: "Payment received", text: "Kestrel Studio paid ₹48,200 for INV-1042.", time: "now" },
{ icon: Mail, tone: "bg-info-soft text-info-fg", title: "Invoice viewed", text: "Brightwater Design opened INV-1043.", time: "4m" },
{ icon: Clock, tone: "bg-warning-soft text-warning-fg", title: "Due tomorrow", text: "INV-1039 to Oakline Joinery, ₹12,000.", time: "1h" },
{ icon: Users, tone: "bg-primary-soft text-primary-soft-fg", title: "Asha joined your team", text: "She can now create and send invoices.", time: "3h" },
];
const STEP = 380; // ms between arrivals
export function NotificationStack() {
return (
<section aria-label="Notifications" className="mx-auto max-w-[400px] overflow-hidden rounded-lg border border-border bg-bg font-sans shadow-[0_18px_40px_-26px_rgb(0_0_0/.35)]">
<header className="flex items-center gap-2.5 border-b border-border px-3.5 py-3">
<span className="relative grid size-8 place-items-center rounded bg-surface text-fg-muted">
<Bell className="size-4" aria-hidden />
<span
className="absolute -right-1.5 -top-1.5 grid h-[18px] min-w-[18px] place-items-center rounded-full border-2 border-bg bg-primary px-1 text-[10.5px] font-semibold text-primary-fg animate-pop motion-reduce:animate-none"
style={{ animationDelay: 250 + notifications.length * STEP + 100 + "ms" }}
>
{notifications.length}
</span>
</span>
<h2 className="flex-1 text-[15px] font-medium text-fg">Notifications</h2>
<button type="button" className="h-8 rounded px-2.5 text-[13px] font-medium text-fg transition hover:bg-surface-2">Mark all read</button>
</header>
<ul className="grid gap-2.5 bg-surface p-3.5">
{notifications.map((n, i) => (
<li
key={n.title}
className="flex items-start gap-3 rounded-lg border border-border bg-bg p-3 text-[13px] shadow-[0_10px_28px_-20px_rgb(0_0_0/.45)] animate-[toast-in_.7s_cubic-bezier(.34,1.56,.64,1)_both] motion-reduce:animate-none"
style={{ animationDelay: 250 + i * STEP + "ms" }}
>
<span className={cn("grid size-8 shrink-0 place-items-center rounded", n.tone)}><n.icon className="size-4" aria-hidden /></span>
<div className="min-w-0 flex-1">
<p className="font-medium text-fg">{n.title}</p>
<p className="text-fg-muted">{n.text}</p>
</div>
<time className="shrink-0 text-[11.5px] text-fg-subtle">{n.time}</time>
</li>
))}
</ul>
</section>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.